-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.xml
4487 lines (4487 loc) · 725 KB
/
index.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http:https://www.w3.org/2005/Atom"><channel><title>YanTree</title><link>https://yantree.github.io/</link><description>Recent content on YanTree</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sat, 05 Jun 2021 10:41:12 +0800</lastBuildDate><atom:link href="https://yantree.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Shader入门精要-画面动起来</title><link>https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/</link><pubDate>Sat, 05 Jun 2021 10:41:12 +0800</pubDate><guid>https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/</guid><description><img src="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/topimg.png" alt="Featured image of post Shader入门精要-画面动起来" /><h1 id="技术美术动画">技术美术——动画</h1>
<p>Unity Shader 提供了一系列关于时间的内置变量,可以方便地在 Shader 中访问,实现各种动画效果。下表给出了这些内置的时间变量。</p>
<table>
<thead>
<tr>
<th>名称</th>
<th>类型</th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>_Time</td>
<td>float4</td>
<td>t 是自该场景加载开始所经过的时间,4 个分量的值分别是 ( t/20, t, 1t, 3t)</td>
</tr>
<tr>
<td>_SinTime</td>
<td>float4</td>
<td>t 是时间的正弦值,4 个分量的值分别是 ( t/8, t/4, t/2, t)</td>
</tr>
<tr>
<td>_CosTime</td>
<td>float4</td>
<td>t 是时间的余弦值,4 个分量的值分别是 ( t/8, t/4, t/2, t)</td>
</tr>
<tr>
<td>unity_DeltaTime</td>
<td>float4</td>
<td>dt 是时间增量,4 个分量的值分别是 ( dt, 1/dt, smoothDt, 1/smoothDt)</td>
</tr>
</tbody>
</table>
<h2 id="纹理动画">纹理动画</h2>
<p>纹理动画在游戏中应该非常广泛,尤其是在资源比较局限的移动平台上,往往会使用纹理动画来代替复杂的粒子系统等模拟的各种动画</p>
<h3 id="序列帧动画">序列帧动画</h3>
<p>做序列帧动画,我们先要提供一张包含了关键帧图像的图像。例如:8 X 8 的关键帧图像,他们的大小相同,而且播放顺序从左到右、从上到下。</p>
<p>在该着色器的实现上我们需要注意以下几个步骤:</p>
<ol>
<li>声明四个属性,<strong>_MainTex</strong> 是包含所有关键帧的纹理,<strong>_HorizontalAmount</strong> 和 <strong>_VerticalAmount</strong> 分别代表了该图像在水平方向和垂直方向上包含的关键帧图像个数,<strong>_Speed</strong> 用于控制序列帧动画的播放速度。</li>
<li>在顶点着色器中进行基本的顶点变换,并把顶点纹理坐标存储到结构体中。我们通过 <strong>TRANSFORM_TEX</strong> 函数来获取初始纹理坐标。</li>
<li>在片元着色器中,我们使用 <strong>_Time.y</strong> 函数和速度属性 <strong>_Speed</strong> 进行相乘来模拟时间,然后使用 <strong>floor</strong> 函数对结果取整来得到整数时间。</li>
<li>使用时间除以 <strong>_HorizontalAmount</strong> 来得到当前时间下对应的 <strong>行索引</strong> ,而 <strong>列索引</strong> 则为余数。</li>
<li>后面使用行列索引构建采样坐标。在计算竖直的偏移值时需要注意:由于 Unity 中纹理坐标竖直方向的顺序(从下到上逐渐增大)和序列帧纹理中的顺序(播放顺序从上到下)时相反的,所以计算竖直方向偏移量时要使用减法。</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">ImgAni</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">MainTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_HorizontalCount</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Horizontal</span> <span class="n">Count</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">_VerticalCount</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Vertical</span> <span class="n">Count</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">_Speed</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Speed</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">Queue</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">IgnoreProjector</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">ZWrite</span> <span class="n">Off</span>
<span class="n">Blend</span> <span class="n">SrcAlpha</span> <span class="n">OneMinusSrcAlpha</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_HorizontalCount</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_VerticalCount</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Speed</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">=</span> <span class="n">TRANSFORM_TEX</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">,</span> <span class="n">_MainTex</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="k">float</span> <span class="n">time</span> <span class="o">=</span> <span class="n">floor</span><span class="p">(</span><span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_Speed</span><span class="p">);</span>
<span class="k">float</span> <span class="n">row</span> <span class="o">=</span> <span class="n">floor</span><span class="p">(</span><span class="n">time</span> <span class="o">/</span> <span class="n">_HorizontalCount</span><span class="p">);</span>
<span class="k">float</span> <span class="n">column</span> <span class="o">=</span> <span class="n">time</span> <span class="o">-</span> <span class="n">row</span> <span class="o">*</span> <span class="n">_HorizontalCount</span><span class="p">;</span>
<span class="n">half2</span> <span class="n">uv</span> <span class="o">=</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span> <span class="o">+</span> <span class="n">half2</span><span class="p">(</span><span class="n">column</span><span class="p">,</span> <span class="o">-</span><span class="n">row</span><span class="p">);</span>
<span class="n">uv</span><span class="p">.</span><span class="n">x</span> <span class="o">/=</span> <span class="n">_HorizontalCount</span><span class="p">;</span>
<span class="n">uv</span><span class="p">.</span><span class="n">y</span> <span class="o">/=</span> <span class="n">_VerticalCount</span><span class="p">;</span>
<span class="n">fixed4</span> <span class="n">c</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">uv</span><span class="p">);</span>
<span class="n">c</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*=</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Transparent</span><span class="o">/</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h3 id="滚动背景">滚动背景</h3>
<p>很多 2D 游戏都使用了不断滚动的背景来模拟游戏角色在场景中的穿梭,这些背景往往包含了多个层来模拟一种视觉效果。而这些背景的实现往往就是利用了纹理动画。我们将实现一个包含了两层的无限滚动的2D游戏背景。</p>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">ScrollBackGround</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">MainTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_SecTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">DetailTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_SpeedMain</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Main</span> <span class="n">Speed</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_SpeedSec</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Second</span> <span class="n">Speed</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_Multiplier</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Layer</span> <span class="n">Multiplier</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_Alpha</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Alpha</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">Queue</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">IgnoreProjector</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">ZWrite</span> <span class="n">Off</span>
<span class="n">Blend</span> <span class="n">SrcAlpha</span> <span class="n">OneMinusSrcAlpha</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="k">sampler2D</span> <span class="n">_SecTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_SecTex_ST</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_SpeedMain</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_SpeedSec</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Multiplier</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Alpha</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">xy</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">zw</span> <span class="o">+</span> <span class="n">frac</span><span class="p">(</span><span class="n">float2</span><span class="p">(</span><span class="n">_SpeedMain</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">zw</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_SecTex_ST</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="n">_SecTex_ST</span><span class="p">.</span><span class="n">zw</span> <span class="o">+</span> <span class="n">frac</span><span class="p">(</span><span class="n">float2</span><span class="p">(</span><span class="n">_SpeedSec</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">)</span> <span class="o">*</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">half4</span> <span class="n">firstLayer</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
<span class="n">half4</span> <span class="n">secondLayer</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_SecTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">zw</span><span class="p">);</span>
<span class="n">half4</span> <span class="n">c</span> <span class="o">=</span> <span class="n">lerp</span><span class="p">(</span><span class="n">firstLayer</span><span class="p">,</span> <span class="n">secondLayer</span><span class="p">,</span> <span class="n">secondLayer</span><span class="p">.</span><span class="n">a</span><span class="p">);</span>
<span class="n">c</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*=</span> <span class="n">_Multiplier</span><span class="p">;</span>
<span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h2 id="顶点动画">顶点动画</h2>
<h3 id="流动的河流">流动的河流</h3>
<p>该效果的原理是使用正弦函数来模拟水流的波动效果,在顶点着色器中使用正弦值对顶点坐标进行偏移得到对纹理扭曲效果。</p>
<blockquote>
<p>‼ 由于Unity的 <strong>批处理</strong> 会合并所有相关的模型,所以模型各自的 <strong>模型空间</strong> 就会丢失,但我们使用的顶点偏移需要在物体的模型空间下进行偏移。所以我们需要声明一个标签—— <strong>DisableBatching</strong> ,告诉 Unity 需要取消对该 Shader 的批处理操作。</p>
</blockquote>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">RiverAni</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">MainTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_Magnitude</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Magnitude</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_Frequency</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Frequency</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_InWaveLength</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Wave</span> <span class="n">Length</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">_Speed</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Speed</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">Queue</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">IgnoreProjector</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">DisableBatching</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">LOD</span> <span class="mi">100</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">Cull</span> <span class="n">Off</span>
<span class="n">ZWrite</span> <span class="n">Off</span>
<span class="n">Blend</span> <span class="n">SrcAlpha</span> <span class="n">OneMinusSrcAlpha</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Magnitude</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Frequency</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_InWaveLength</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Speed</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">offset</span><span class="p">;</span>
<span class="n">offset</span><span class="p">.</span><span class="n">yzw</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">);</span>
<span class="n">offset</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">_Frequency</span> <span class="o">*</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="c1">// 波动频率</span>
<span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">x</span> <span class="o">*</span> <span class="n">_InWaveLength</span> <span class="c1">// _InWaveLength 控制波长大小</span>
<span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_InWaveLength</span>
<span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">z</span> <span class="o">*</span> <span class="n">_InWaveLength</span><span class="p">)</span> <span class="o">*</span> <span class="n">_Magnitude</span><span class="p">;</span> <span class="c1">// _Magnitud 控制波动大小</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span> <span class="o">+</span> <span class="n">offset</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">=</span> <span class="n">TRANSFORM_TEX</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">,</span> <span class="n">_MainTex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">+=</span> <span class="n">float2</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_Speed</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">fixed4</span> <span class="n">col</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">);</span>
<span class="n">col</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*=</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
<span class="k">return</span> <span class="n">col</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Transparent</span><span class="o">/</span><span class="n">vertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h3 id="广告牌技术">广告牌技术</h3>
<p>另一种常见的顶点动画就是 <strong>广告牌技术</strong> 。广告牌技术会根据视角方向来旋转一个被纹理着色的多边形(通常就是简单的四边形,这个多边形就是广告牌),是的多边形看起来好像总是面对着摄像机。广告牌技术被用于很多应用,比如渲染烟雾、运毒、闪光效果等。</p>
<h4 id="原理">原理</h4>
<p>广告牌技术的本质就是构建旋转矩阵,而我们知道一个变换矩阵需要 3 个 <strong>基向量</strong> 。除此之外,我们还需要指定一个 <strong>锚点</strong> 。这个锚点在旋转的过程中是固定不变的,以此来确定多边形在空间中的位置。广告牌技术使用的基向量通常就是:</p>
<ol>
<li>表面法线(normal)</li>
<li>指向上的方向(up)</li>
<li>指向右的方向(right)</li>
</ol>
<p>广告牌技术的难点在于,如何根据需要来构建 3 个相互正交的基向量。计算过程通常是,我们首先会通过初始计算得到目标的表面法线(例如就是视角方向)和指向上的方向,而两者往往是不垂直的。但是,两者其中之一是固定的。例如:</p>
<ul>
<li>当模拟草丛时,我们希望广告牌的指向上的方向永远是(0,1,0),而法线方向应该随视角变化。</li>
<li>当模拟粒子效果时,我们希望广告牌的法线方向是固定的,即总是指向视角方向,指向上的方向则可以发生变化。</li>
</ul>
<h4 id="计算-以法线方向固定为视角方向为例">计算 (以法线方向固定为视角方向为例)</h4>
<p>假设法线方向是固定的,首先,我们根据初始的表面法线和指向上的方向来计算出目标方向的指向右的方向(通过叉积操作):</p>
<blockquote>
<p>right = up × normal</p>
</blockquote>
<p>对其归一化后,再由法线方向和指向右的方向计算出正交的指向上的方向即可:</p>
<blockquote>
<p>up' = normal × right</p>
</blockquote>
<p>至此,我们就可以得到用于旋转的 3 个正交基了。下图给出了上述计算过程的图示。如果指向上的方向是固定的,计算过程也是类似的。</p>
<p><figure style="flex-grow: 250; flex-basis: 602px">
<a href="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/billboard.png" data-size="1024x408"><img src="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/billboard.png"
srcset="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/billboard_hu8babf3f666abb1e889d80e074e4fbae4_82087_480x0_resize_box_2.png 480w, https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/billboard_hu8babf3f666abb1e889d80e074e4fbae4_82087_1024x0_resize_box_2.png 1024w"
width="1024"
height="408"
loading="lazy"
>
</a>
</figure></p>
<h4 id="实现">实现</h4>
<ol>
<li>首先我们在着色器中定义一个 <strong>_VerticalBillboarding</strong> 标签用于调整是固定法线还是固定指向上的方向</li>
<li>法线方向:将相机坐标转换到模型空间下后减去物体中心</li>
<li>向右方向:通过法线方向和指向上的方向(0,1,0)叉乘得到</li>
<li>向上方向:通过叉乘法线和向右方向后归一化得到</li>
<li>通过上面三个方向旋转正方形</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Billboard</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">MainTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_VeticalBillboarding</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Vertical</span> <span class="n">Billboarding</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mi">1</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">Queue</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">IgnoreProjector</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">DisableBatching</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Cull</span> <span class="n">Off</span>
<span class="n">ZWrite</span> <span class="n">Off</span>
<span class="n">Blend</span> <span class="n">SrcAlpha</span> <span class="n">OneMinusSrcAlpha</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">multi_compile_fwdbase</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_VeticalBillboarding</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">center</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="mo">0</span><span class="p">,</span><span class="mo">0</span><span class="p">,</span><span class="mo">0</span><span class="p">);</span> <span class="c1">// 固定锚点</span>
<span class="n">float3</span> <span class="n">viewDir</span> <span class="o">=</span> <span class="n">mul</span><span class="p">(</span><span class="n">unity_WorldToObject</span><span class="p">,</span> <span class="n">float4</span><span class="p">(</span><span class="n">_WorldSpaceCameraPos</span><span class="p">,</span> <span class="mi">1</span><span class="p">));</span>
<span class="n">float3</span> <span class="n">normalDir</span> <span class="o">=</span> <span class="n">viewDir</span> <span class="o">-</span> <span class="n">center</span><span class="p">;</span> <span class="c1">// 以视角方向作为法线方向</span>
<span class="n">normalDir</span><span class="p">.</span><span class="n">y</span> <span class="o">=</span> <span class="n">normalDir</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_VeticalBillboarding</span><span class="p">;</span>
<span class="n">normalDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">normalDir</span><span class="p">);</span>
<span class="n">float3</span> <span class="n">upDir</span> <span class="o">=</span> <span class="n">abs</span><span class="p">(</span><span class="n">normalDir</span><span class="p">.</span><span class="n">y</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mf">0.999</span> <span class="o">?</span> <span class="n">float3</span><span class="p">(</span><span class="mo">0</span><span class="p">,</span><span class="mo">0</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="o">:</span> <span class="n">float3</span><span class="p">(</span><span class="mo">0</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mo">0</span><span class="p">);</span> <span class="c1">// 暂取一个 upDir 方向</span>
<span class="n">float3</span> <span class="n">rightDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">cross</span><span class="p">(</span><span class="n">normalDir</span><span class="p">,</span> <span class="n">upDir</span><span class="p">));</span> <span class="c1">// 计算向右的方向</span>
<span class="n">upDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">cross</span><span class="p">(</span><span class="n">normalDir</span><span class="p">,</span> <span class="n">rightDir</span><span class="p">));</span> <span class="c1">// 计算向上的方向</span>
<span class="c1">// 以锚点位置和新的基向量重新计算顶点位置</span>
<span class="n">float3</span> <span class="n">centerOff</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">xyz</span> <span class="o">-</span> <span class="n">center</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">localPos</span> <span class="o">=</span> <span class="n">center</span> <span class="o">+</span> <span class="n">rightDir</span> <span class="o">*</span> <span class="n">centerOff</span><span class="p">.</span><span class="n">x</span> <span class="o">+</span> <span class="n">upDir</span> <span class="o">*</span> <span class="n">centerOff</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="n">normalDir</span> <span class="o">*</span> <span class="n">centerOff</span><span class="p">.</span><span class="n">z</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">float4</span><span class="p">(</span><span class="n">localPos</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">));</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">zw</span><span class="p">;</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">fixed4</span> <span class="n">col</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">);</span>
<span class="n">col</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*=</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
<span class="k">return</span> <span class="n">col</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Transparent</span><span class="o">/</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h2 id="顶点动画的阴影">顶点动画的阴影</h2>
<h3 id="问题">问题:</h3>
<p><figure style="flex-grow: 215; flex-basis: 516px">
<a href="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/VertexAniProblem.png" data-size="1024x476"><img src="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/VertexAniProblem.png"
srcset="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/VertexAniProblem_hu931032a658f33c677ce23380fe89e96b_115013_480x0_resize_box_2.png 480w, https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E7%94%BB%E9%9D%A2%E5%8A%A8%E8%B5%B7%E6%9D%A5/VertexAniProblem_hu931032a658f33c677ce23380fe89e96b_115013_1024x0_resize_box_2.png 1024w"
width="1024"
height="476"
loading="lazy"
>
</a>
</figure></p>
<h3 id="原因">原因:</h3>
<p>因为 Unity 的阴影绘制需要调用一个 <strong>ShadowCaster Pass</strong> ,而如果直接使用这些内置的 <strong>ShadowCasterPass</strong> ,这个 Pass 中并没有进行相关的顶点动画。</p>
<h3 id="解决方法">解决方法:</h3>
<p>在 Unity 中自定义的 <strong>ShadowCaster Pass</strong> ,这个 Pass 中,我们将进行统一的顶点变换过程。需要注意的是,在前面的实现中,如果涉及半透明物体我们都把 <strong>Fallback</strong> 设置成了 <strong>Transparent/VertexLit</strong> ,而 <strong>Transparent/VertexLit</strong> 没有定义 <strong>ShadowCaster Pass</strong> ,因此也就不会产生阴影。需要注意的是,我们在 <strong>ShadowCaster Pass</strong> 采用的 <strong>渲染通道</strong> 与 <strong>编译指令</strong> 都与之前 <strong>标准光照着色器</strong> 中采用的有所不同,所以对应的 <strong>阴影内置宏</strong> 也不一样。</p>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unity</span> <span class="n">Shaders</span> <span class="n">Book</span><span class="o">/</span><span class="n">Chapter</span> <span class="mi">11</span><span class="o">/</span><span class="n">Vertex</span> <span class="n">Animation</span> <span class="n">With</span> <span class="n">Shadow</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Main</span> <span class="n">Tex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{</span> <span class="p">}</span>
<span class="n">_Color</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span> <span class="n">Tint</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">_Magnitude</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Distortion</span> <span class="n">Magnitude</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_Frequency</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Distortion</span> <span class="n">Frequency</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_InvWaveLength</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Distortion</span> <span class="n">Inverse</span> <span class="n">Wave</span> <span class="n">Length</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">_Speed</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Speed</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Float</span><span class="p">)</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="c1">// 禁用批处理 = True</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">DisableBatching</span><span class="err">&#34;</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">True</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="c1">// 该Pass同流动效果Shader中的Pass</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Cull</span> <span class="n">Off</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="n">fixed4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Magnitude</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Frequency</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_InvWaveLength</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Speed</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">a2v</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span><span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">texcoord</span><span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span><span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">uv</span><span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span><span class="p">(</span><span class="n">a2v</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">offset</span><span class="p">;</span>
<span class="n">offset</span><span class="p">.</span><span class="n">yzw</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">);</span>
<span class="n">offset</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">_Frequency</span> <span class="o">*</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">x</span> <span class="o">*</span> <span class="n">_InvWaveLength</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_InvWaveLength</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">z</span> <span class="o">*</span> <span class="n">_InvWaveLength</span><span class="p">)</span> <span class="o">*</span> <span class="n">_Magnitude</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span> <span class="o">+</span> <span class="n">offset</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">=</span> <span class="n">TRANSFORM_TEX</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">,</span> <span class="n">_MainTex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">+=</span> <span class="n">float2</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_Speed</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span><span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span><span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">fixed4</span> <span class="n">c</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">);</span>
<span class="n">c</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*=</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
<span class="k">return</span> <span class="n">c</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="c1">// 以阴影投射的方式渲染物体的Pass</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="c1">// 渲染通道 = 阴影投射</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">ShadowCaster</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="c1">// 编译指令会保证Unity可以为相应类型的光照Pass生成所需的Shader变种</span>
<span class="c1">// 注意该指令与之前光照渲染中Pass中的&#34;multi_compile_fwdbase&#34;有所不同</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">multi_compile_shadowcaster</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">float</span> <span class="n">_Magnitude</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Frequency</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_InvWaveLength</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Speed</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="c1">// 使用宏定义阴影投射需要的变量</span>
<span class="n">V2F_SHADOW_CASTER</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span><span class="p">(</span><span class="n">appdata_base</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="c1">// 计算流动动画偏移</span>
<span class="n">float4</span> <span class="n">offset</span><span class="p">;</span>
<span class="n">offset</span><span class="p">.</span><span class="n">yzw</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">);</span>
<span class="n">offset</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="n">_Frequency</span> <span class="o">*</span> <span class="n">_Time</span><span class="p">.</span><span class="n">y</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">x</span> <span class="o">*</span> <span class="n">_InvWaveLength</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">y</span> <span class="o">*</span> <span class="n">_InvWaveLength</span> <span class="o">+</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">.</span><span class="n">z</span> <span class="o">*</span> <span class="n">_InvWaveLength</span><span class="p">)</span> <span class="o">*</span> <span class="n">_Magnitude</span><span class="p">;</span>
<span class="n">v</span><span class="p">.</span><span class="n">vertex</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span> <span class="o">+</span> <span class="n">offset</span><span class="p">;</span>
<span class="c1">// 使用宏计算阴影纹理变量</span>
<span class="n">TRANSFER_SHADOW_CASTER_NORMALOFFSET</span><span class="p">(</span><span class="n">o</span><span class="p">)</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span><span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span><span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="c1">// 使用宏对阴影进行投射,并将结果输出到深度图与阴影映射纹理中</span>
<span class="n">SHADOW_CASTER_FRAGMENT</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">FallBack</span> <span class="err">&#34;</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h2 id="补充说明">补充说明</h2>
<ol>
<li>在之前看到的那样,如果我们在模型空间下进行了一些顶点动画,那么 <strong>批处理</strong> 往往就会破坏这种动画效果。这时,我们可以通过 <strong>SubShader</strong> 的 <strong>DisableBatching</strong> 标签来强制取消对该 Unity Shader 的 <strong>批处理</strong> 。然而,取消批处理会带来一定的性能下降,增加了 <strong>Draw Call</strong> ,因此我们应该尽量避免使用模型空间下的一些绝对位置和方向来进行计算。</li>
<li>在广告牌的例子中,为了避免显示使用模型空间的中心来作为锚点,我们可以利用顶点颜色来存储每个顶点到锚点的距离值,这种做法在商业游戏中很常见。</li>
</ol>
<h2 id="总结">总结</h2>
<p><strong>纹理动画</strong> 中主要是对贴图的 UV 进行操作。</p>
<p><strong>顶点动画</strong> 中是对顶点进行编辑,讲到了两个技术:流动的河流 和 广告牌技术。</p>
<p><strong>顶点动画的阴影</strong> 中需要根据顶点动画自定义 <strong>ShadowCaster Pass</strong>。</p></description></item><item><title>Shader入门精要-高级纹理</title><link>https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E9%AB%98%E7%BA%A7%E7%BA%B9%E7%90%86/</link><pubDate>Fri, 04 Jun 2021 20:00:31 +0800</pubDate><guid>https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E9%AB%98%E7%BA%A7%E7%BA%B9%E7%90%86/</guid><description><img src="https://yantree.github.io/p/shader%E5%85%A5%E9%97%A8%E7%B2%BE%E8%A6%81-%E9%AB%98%E7%BA%A7%E7%BA%B9%E7%90%86/topimg.png" alt="Featured image of post Shader入门精要-高级纹理" /><h1 id="技术美术高级纹理">技术美术——高级纹理</h1>
<h2 id="立方体纹理cubemap">立方体纹理(Cubemap)</h2>
<p><strong>立方体纹理(Cubemap)</strong> 是环境映射(Environment Mapping)的一种实现方法。环境映射可以模拟物体周围的环境,而使用了环境映射的物体可以看起来像镀了层金属一样 <strong>反射出周围的环境</strong>。</p>
<p>优点</p>
<ul>
<li>立方体纹理的实现简单快捷,而且得到的效果也比较好。</li>
</ul>
<p>缺点</p>
<ul>
<li>当场景中引入了新的物体、光源,或者物体发生移动时,我们就需要重新生成立方体纹理。</li>
<li>立方体纹理也仅可以反射环境,但不能反射使用了该立方体纹理的物体本身。这是因为,立方体纹理不能模拟多次反射的结果。</li>
</ul>
<p>综合考虑下来,立方体纹理适用于凸面体,而不太适用于凹面体 ( 因为凹面体会反射自身 )。</p>
<h3 id="立方体纹理用于环境映射">立方体纹理—用于环境映射</h3>
<p>立方体纹理最常见的用处是用于环境映射。通过这种方法,我们可以 <strong>模拟出金属质感的材质</strong>。</p>
<p>创建用于环境映射的立方体纹理的三种方法:</p>
<ol>
<li>直接由一些特殊布局的纹理创建;</li>
<li>手动创建一个Cubemap资源,再把6张图赋给它;</li>
<li>由脚本生成。</li>
</ol>
<p>第一种方法:提供一张具有特殊布局的纹理,例如类似立方体展开图的交叉布局、全景布局等(就类似于人物贴图,换成了球形)。然后只需把该纹理的 <strong>Texture Type</strong> 设置为 <strong>Cubemap</strong> 即可,Unity 会做好剩下的事情。</p>
<p>第二种方法:先在项目资源中创建一个 <strong>Cubemap</strong>,然后把它的 6 张纹理拖拽到它的面板中。在 Unity 5 中,官方推荐使用第一种方法创建立方体纹理,这是因为第一种方法可以对纹理数据进行压缩,即可以支持边缘修正、光滑反射(glossy reflection)和 HDR 等功能。</p>
<p>第三种方法:使用 <strong>Camera.RenderToCubemap</strong> 函数来实现,Camera.RenderToCubemap 函数可以把任意位置观察到的场景图像存储到 6 张图像中,从而创建出该位置上对应的立方体纹理。</p>
<ol>
<li>
<p>创建一个编辑器脚本,用于将摄像机照射到的图片渲染到 Cubemap 中。由于该代码需要添加菜单条目,因此我们需要把它放在 Editor 文件夹下才能正确执行。原理如下:</p>
<p>在 renderFromPosition(由用户指定)位置处动态创建一个摄像机,并调用 Camera.RenderToCubemap 函数把从当前位置观察到的图像渲染到用户指定的立方体纹理 cubemap 中,完成后再销毁临时摄像机。</p>
<div class="highlight"><pre class="chroma"><code class="language-c#" data-lang="c#"><span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">UnityEditor</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">RenderCubemapWizard</span> <span class="p">:</span> <span class="n">ScriptableWizard</span> <span class="p">{</span>
<span class="k">public</span> <span class="n">Transform</span> <span class="n">renderFromPosition</span><span class="p">;</span>
<span class="k">public</span> <span class="n">Cubemap</span> <span class="n">cubemap</span><span class="p">;</span>
<span class="k">void</span> <span class="n">OnWizardUpdate</span> <span class="p">()</span> <span class="p">{</span>
<span class="n">helpString</span> <span class="p">=</span> <span class="s">&#34;选择要渲染的坐标位置和要渲染的cubemap&#34;</span><span class="p">;</span>
<span class="n">isValid</span> <span class="p">=</span> <span class="p">(</span><span class="n">renderFromPosition</span> <span class="p">!=</span> <span class="k">null</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="p">(</span><span class="n">cubemap</span> <span class="p">!=</span> <span class="k">null</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">void</span> <span class="n">OnWizardCreate</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// 创建用于渲染的临时摄像机
</span><span class="c1"></span> <span class="n">GameObject</span> <span class="n">go</span> <span class="p">=</span> <span class="k">new</span> <span class="n">GameObject</span><span class="p">(</span> <span class="s">&#34;CubemapCamera&#34;</span><span class="p">);</span>
<span class="n">go</span><span class="p">.</span><span class="n">AddComponent</span><span class="p">&lt;</span><span class="n">Camera</span><span class="p">&gt;();</span>
<span class="c1">// 把它放到物体坐标上
</span><span class="c1"></span> <span class="n">go</span><span class="p">.</span><span class="n">transform</span><span class="p">.</span><span class="n">position</span> <span class="p">=</span> <span class="n">renderFromPosition</span><span class="p">.</span><span class="n">position</span><span class="p">;</span>
<span class="c1">// 渲染成cubemap
</span><span class="c1"></span> <span class="n">go</span><span class="p">.</span><span class="n">GetComponent</span><span class="p">&lt;</span><span class="n">Camera</span><span class="p">&gt;().</span><span class="n">RenderToCubemap</span><span class="p">(</span><span class="n">cubemap</span><span class="p">);</span>
<span class="c1">// 销毁临时相机
</span><span class="c1"></span> <span class="n">DestroyImmediate</span><span class="p">(</span> <span class="n">go</span> <span class="p">);</span>
<span class="p">}</span>
<span class="na">
</span><span class="na"> [MenuItem(&#34;GameObject/Render into Cubemap&#34;)]</span>
<span class="k">static</span> <span class="k">void</span> <span class="n">RenderCubemap</span> <span class="p">()</span> <span class="p">{</span>
<span class="n">ScriptableWizard</span><span class="p">.</span><span class="n">DisplayWizard</span><span class="p">&lt;</span><span class="n">RenderCubemapWizard</span><span class="p">&gt;(</span>
<span class="s">&#34;Render cubemap&#34;</span><span class="p">,</span> <span class="s">&#34;Render!&#34;</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></li>
<li>
<p>新建一个用于存储的立方体纹理(在 Project 视图下单击右键,选择 Create -&gt; Legacy -&gt; Cubemap来创建)。为了让脚本可以顺利将图像渲染到该立方体纹理中,我们需要在它的面板中勾选 <strong>Readable</strong> 选项。</p>
</li>
<li>
<p>从 Unity 菜单栏选择 GameObject -&gt; Render into Cubemap,打开我们在脚本中实现的用于渲染立方体纹理的窗口,并把第一步创建的 GameObject 和第二步中的纹理分别拖拽到窗口中的 Render From Position 和 Cubemap 选项。</p>
</li>
<li>
<p>单击窗口的 Render!按钮,就可以把从该位置观察到的世界空间下的 6 张图像中渲染到纹理中。</p>
</li>
</ol>
<p>需要注意的是,我们需要为 <strong>Cubemap</strong> 设置大小,即上图中的 <strong>Face size</strong> 选项。<strong>Face size</strong> 值越大,渲染出来的立方体纹理分辨率越大,效果可能更好,单需要占用的内存也越大,这可以由面板最下方显示的内存大小得到。</p>
<h3 id="反射">反射</h3>
<p>模拟反射效果很简单,我们只需要通过 <strong>入射光线的方向</strong> 和 <strong>表面法线方向来计算反射方向</strong>,再利用 <strong>反射方向对立方体纹理采样</strong> 即可。</p>
<p>反射用到 Shader 的是在漫反射模型上进行修改的:</p>
<ol>
<li>增添三个属性,分别用于控制反射颜色、反射程度和捕捉环境映射纹理</li>
<li>在顶点着色器中哪个使用<strong>reflect</strong>函数来计算该顶点的反射方向</li>
</ol>
<blockquote>
<p>reflect ( I, N ) 根据入射光线方向I和表面法向量N计算反射向量,仅对三元向量有效</p>
</blockquote>
<ol start="3">
<li>在片元着色器中使用 <strong>texCUBE</strong> 函数和顶点着色器中获得的反射方向对立方体纹理进行采样,得到反射颜色</li>
<li>最后使用 lerp 对原有漫反射与纹理反射颜色进行插值</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Reflect</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_ReflectColor</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Reflect</span> <span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_ReflectPower</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Reflect</span> <span class="n">Power</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mf">0.2</span>
<span class="n">_CubeMap</span><span class="p">(</span><span class="err">&#34;</span><span class="n">CubeMap</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Cube</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">_Skybox</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Opaque</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">multi_compile_fwdbase</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">Lighting</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">AutoLight</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_ReflectColor</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_ReflectPower</span><span class="p">;</span>
<span class="n">samplerCUBE</span> <span class="n">_CubeMap</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">normal</span> <span class="o">:</span> <span class="n">NORMAL</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldNormal</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldPos</span> <span class="o">:</span> <span class="n">TEXCOORD1</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldRefl</span> <span class="o">:</span> <span class="n">TEXCOOR2</span><span class="p">;</span>
<span class="n">SHADOW_COORDS</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span> <span class="o">=</span> <span class="n">UnityObjectToWorldNormal</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">normal</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldPos</span> <span class="o">=</span> <span class="n">mul</span><span class="p">(</span><span class="n">unity_ObjectToWorld</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">).</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldRefl</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">worldPos</span><span class="p">),</span> <span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">);</span> <span class="c1">// 反射方向</span>
<span class="n">TRANSFER_SHADOW</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">half3</span> <span class="n">worldNormal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">);</span>
<span class="n">half3</span> <span class="n">worldLightDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceLightDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">worldViewDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">halfDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">worldLightDir</span> <span class="o">+</span> <span class="n">worldViewDir</span><span class="p">);</span>
<span class="k">half</span> <span class="n">NdotL</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">dot</span><span class="p">(</span><span class="n">worldNormal</span><span class="p">,</span> <span class="n">worldLightDir</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">reflection</span> <span class="o">=</span> <span class="n">texCUBE</span><span class="p">(</span><span class="n">_CubeMap</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldRefl</span><span class="p">).</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">_ReflectColor</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span> <span class="c1">// 采样 Cubemap 计算反射</span>
<span class="n">half3</span> <span class="n">ambient</span> <span class="o">=</span> <span class="n">UNITY_LIGHTMODEL_AMBIENT</span><span class="p">.</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">half3</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">_LightColor0</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">NdotL</span><span class="p">;</span>
<span class="n">UNITY_LIGHT_ATTENUATION</span><span class="p">(</span><span class="n">atten</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">);</span>
<span class="k">return</span> <span class="n">half4</span><span class="p">(</span><span class="n">ambient</span> <span class="o">+</span> <span class="n">lerp</span><span class="p">(</span><span class="n">diffuse</span><span class="p">,</span> <span class="n">reflection</span><span class="p">,</span> <span class="n">_ReflectPower</span><span class="p">)</span> <span class="o">*</span> <span class="n">atten</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Diffuse</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h3 id="折射">折射</h3>
<p>给定入射角时,我们可以使用 <strong>斯涅尔定律 (Snell’s law )</strong> 来计算反射角。当光从介质 1 沿着和表面法线夹角为 θ1 的方向斜射入介质 2 时,我们可以使用如下公式计算折射光线与法线的夹角θ2:</p>
<blockquote>
<p>η1 sinθ1 = η2 sinθ2</p>
</blockquote>
<p>其中 η1 和 η2 分别是两个介质的 <strong>折射率 (index of refraction )</strong> 。折射率是一项重要的物理常量,例如真空的折射率是 1,而玻璃的折射率一般是 1.5。</p>
<p>折射用到 Shader 中与反射类似,依旧使用 <strong>refract</strong> 函数来计算该顶点的折射方向,但不同的是采用了三个参数:</p>
<ul>
<li>第一个参数即为入射光线方向,它必须是归一化后的矢量;</li>
<li>第二个参数是表面法线,法线方向同样是要归一化后的;</li>
<li>第三个参数是入射光线所在介质的折射率和折射光线所在介质的折射率之间的比值。</li>
</ul>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Refract</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_RefractColor</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Refract</span> <span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_RefractPower</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Refract</span> <span class="n">Power</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">_RefractRatio</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Refract</span> <span class="n">Ratio</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.01</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mf">0.2</span>
<span class="n">_Cubemap</span><span class="p">(</span><span class="err">&#34;</span><span class="n">CubeMap</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Cube</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">_Skybox</span><span class="err">&#34;</span> <span class="p">{</span> <span class="p">}</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Opaque</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">multi_compile_fwdbase</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">Lighting</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">AutoLight</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_RefractColor</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_RefractPower</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_RefractRatio</span><span class="p">;</span>
<span class="n">samplerCUBE</span> <span class="n">_Cubemap</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">normal</span> <span class="o">:</span> <span class="n">NORMAL</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldPos</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldNormal</span> <span class="o">:</span> <span class="n">TEXCOORD1</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldRefra</span> <span class="o">:</span> <span class="n">TEXCOORD2</span><span class="p">;</span>
<span class="n">SHADOW_COORDS</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span> <span class="o">=</span> <span class="n">UnityObjectToWorldNormal</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">normal</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldPos</span> <span class="o">=</span> <span class="n">mul</span><span class="p">(</span><span class="n">unity_ObjectToWorld</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">).</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldRefra</span> <span class="o">=</span> <span class="n">refract</span><span class="p">(</span><span class="o">-</span><span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">worldPos</span><span class="p">)),</span> <span class="n">normalize</span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">),</span> <span class="n">_RefractRatio</span><span class="p">);</span> <span class="c1">// 计算折射方向</span>
<span class="n">TRANSFER_SHADOW</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">half3</span> <span class="n">worldNormal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">);</span>
<span class="n">half3</span> <span class="n">worldLightDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceLightDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">worldViewDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="k">half</span> <span class="n">NdotL</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">dot</span><span class="p">(</span><span class="n">worldNormal</span><span class="p">,</span> <span class="n">worldLightDir</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">refraction</span> <span class="o">=</span> <span class="n">texCUBE</span><span class="p">(</span><span class="n">_Cubemap</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldRefra</span><span class="p">).</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">_RefractColor</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span> <span class="c1">// 采样 Cubemap 计算折射</span>
<span class="n">half3</span> <span class="n">ambient</span> <span class="o">=</span> <span class="n">UNITY_LIGHTMODEL_AMBIENT</span><span class="p">.</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">half3</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">_LightColor0</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">NdotL</span><span class="p">;</span>
<span class="n">UNITY_LIGHT_ATTENUATION</span><span class="p">(</span><span class="n">atten</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">);</span>
<span class="k">return</span> <span class="n">half4</span><span class="p">(</span><span class="n">ambient</span> <span class="o">+</span> <span class="n">lerp</span><span class="p">(</span><span class="n">diffuse</span><span class="p">,</span> <span class="n">refraction</span><span class="p">,</span> <span class="n">_RefractPower</span><span class="p">)</span> <span class="o">*</span> <span class="n">atten</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Reflective</span><span class="o">/</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h3 id="菲涅尔反射">菲涅尔反射</h3>
<p>使用 <strong>菲涅尔反射 (Fresnel reflection )</strong> 来根据视角方向控制反射过程,菲涅尔反射描述了一种光学现象,即当光线照射到物体表面上时,一部分发生反射,一部分进入物体内部,发生折射或散射。被反射的光和入射光之间存在一定的比率关系,这个比率关系可以通过菲涅尔等式进行计算。</p>
<p>一个经常使用的例子是,当你站在湖边,直接低头看脚边的水面时,你会发现水几乎是透明的,你可以直接看到水底的小鱼和石子;但是当你抬头看远处的水面时,会发现几乎看不到水下的情景,而只能看到水面反射的环境。这就是所谓的菲涅尔效果。</p>
<p>真实世界的菲涅尔等式是非常复杂的,但在实时渲染中,我们通常会使用一些近似公式来计算。其中一个著名的计算公式就是 <strong>Schlick菲涅尔</strong> 近似等式:</p>
<blockquote>
<p>FSchlick ( v , n ) = F0 + ( 1 - F0 ) ( 1 - v · n)5</p>
</blockquote>
<p>其中,F0 是一个反射系数,用于控制菲涅尔反射的强度,v 是视角方向,n 是表面法线。另一个应用比较广泛的等式是 <strong>Empricial菲涅尔</strong> 近似等式:</p>
<blockquote>
<p>FEmpricial ( v , n ) = max ( 0 , min ( 1 , bias + scale * ( 1 - v · n ) power))</p>
</blockquote>
<p>其中 bias、scale 和 power 是控制项。</p>
<p>下面将使用 <strong>Schlick菲涅尔</strong> 近似等式来模拟菲涅尔反射。在片元着色器中,我们套用 Schlick 菲涅尔公式来计算菲尼尔比率,然后用该比率插值混合 <strong>漫反射光照</strong> 和 <strong>反射光照</strong> 。</p>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Fresnel</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_Color</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Color</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Color</span><span class="p">)</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span>
<span class="n">_FresnelScale</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Fresnel</span> <span class="n">Scale</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="n">_CubeMap</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Cube</span> <span class="n">Map</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Cube</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">_Skybox</span><span class="err">&#34;</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Opaque</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span><span class="err">&#34;</span><span class="n">LightMode</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">ForwardBase</span><span class="err">&#34;</span><span class="p">}</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">multi_compile_fwdbase</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">Lighting</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">AutoLight</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="n">float4</span> <span class="n">_Color</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_FresnelScale</span><span class="p">;</span>
<span class="n">samplerCUBE</span> <span class="n">_CubeMap</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">normal</span> <span class="o">:</span> <span class="n">NORMAL</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldNormal</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldPos</span> <span class="o">:</span> <span class="n">TEXCOORD1</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldRefl</span> <span class="o">:</span> <span class="n">TEXCOORD2</span><span class="p">;</span>
<span class="n">SHADOW_COORDS</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span> <span class="o">=</span> <span class="n">UnityObjectToWorldNormal</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldPos</span> <span class="o">=</span> <span class="n">mul</span><span class="p">(</span><span class="n">unity_ObjectToWorld</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">).</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">worldRefl</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">worldPos</span><span class="p">),</span> <span class="n">o</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">);</span> <span class="c1">// 计算反射方向</span>
<span class="n">TRANSFER_SHADOW</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">half3</span> <span class="n">worldNormal</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldNormal</span><span class="p">);</span>
<span class="n">half3</span> <span class="n">worldLightDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceLightDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">worldViewDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">));</span>
<span class="k">half</span> <span class="n">NdotL</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="n">dot</span><span class="p">(</span><span class="n">worldNormal</span><span class="p">,</span> <span class="n">worldLightDir</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">reflection</span> <span class="o">=</span> <span class="n">texCUBE</span><span class="p">(</span><span class="n">_CubeMap</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldRefl</span><span class="p">).</span><span class="n">rgb</span><span class="p">;</span> <span class="c1">// 采样 Cubemap</span>
<span class="k">half</span> <span class="n">fresnel</span> <span class="o">=</span> <span class="n">_FresnelScale</span> <span class="o">+</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">_FresnelScale</span><span class="p">)</span> <span class="o">*</span> <span class="n">pow</span><span class="p">((</span><span class="mf">1.0</span> <span class="o">-</span> <span class="n">dot</span><span class="p">(</span><span class="n">worldViewDir</span><span class="p">,</span> <span class="n">worldNormal</span><span class="p">)),</span> <span class="mf">5.0</span><span class="p">);</span> <span class="c1">// 计算 Fresnel 效果</span>
<span class="n">half3</span> <span class="n">ambient</span> <span class="o">=</span> <span class="n">UNITY_LIGHTMODEL_AMBIENT</span><span class="p">.</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">half3</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">_LightColor0</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">_Color</span><span class="p">.</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">NdotL</span><span class="p">;</span>
<span class="n">UNITY_LIGHT_ATTENUATION</span><span class="p">(</span><span class="n">atten</span><span class="p">,</span> <span class="n">i</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">worldPos</span><span class="p">);</span>
<span class="k">return</span> <span class="n">half4</span><span class="p">(</span><span class="n">ambient</span> <span class="o">+</span> <span class="n">lerp</span><span class="p">(</span><span class="n">diffuse</span><span class="p">,</span> <span class="n">reflection</span><span class="p">,</span> <span class="n">saturate</span><span class="p">(</span><span class="n">fresnel</span><span class="p">))</span> <span class="o">*</span> <span class="n">atten</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="err">&#34;</span><span class="n">Reflective</span><span class="o">/</span><span class="n">VertexLit</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h2 id="渲染纹理">渲染纹理</h2>
<p>一个摄像机的渲染结果会输出到颜色缓冲中,并显示到我们的屏幕上。现代的 GPU 允许我们把整个三维场景渲染到一个中间缓存中,即 <strong>渲染目标纹理 (Render Target Texture,RTT)</strong> ,而不是传统的 <strong>帧缓冲或后备缓冲 ( back buffer )</strong> 。与之相关的是 <strong>多重渲染目标 ( Multiple Render Target ,MRT )</strong> ,这种技术指的是 GPU 允许我们把场景同时渲染到多个目标纹理中,而不再需要为每个渲染目标纹理单独渲染完整的场景。延迟渲染就是使用多重渲染目标的一个应用。</p>
<p>Unity 为渲染目标纹理定义了一种专门的纹理类型—— <strong>渲染纹理 ( Render Texture )</strong> 。在 Unity 中使用渲染纹理通常有两种方式:</p>
<ul>
<li>一种方式是在 Project 目录下创建一个 <strong>渲染纹理</strong> ,然后把某个摄像机的 <strong>渲染目标</strong> 设置成该 <strong>渲染纹理</strong> ,这样一来该摄像机的渲染结果就会实时更新到 <strong>渲染纹理</strong> 中,而不会显示在屏幕上。使用这种方法,我们还可以选择纹理的分辨率、滤波模式等纹理属性。</li>
<li>另一种方式是在屏幕后处理时使用 <strong>GrabPass</strong> 命令或 <strong>OnRenderImage</strong> 函数来获取当前屏幕图像,Unity 会把这个屏幕图像放到一张和屏幕分辨率等同的渲染纹理中,下面我们可以在自定义的 Pass 中把它们当成普通纹理来处理,从而实现各种屏幕特效。</li>
</ul>
<h3 id="镜子效果">镜子效果</h3>
<p>关键在于新建一个摄像机,一个 Render Texture,将新建的 Render Texture 当作新建相机的 <strong>Target Texture</strong> ,新建一个 Shader,shader 里声明一个 2D 纹理属性,将新建的 Render Texture 赋值给它。</p>
<ol>
<li>创建一个四边形(Quad),调整它的位置和大小,用于充当镜子。</li>
<li>为了得到从镜子出发观察到的场景图像,我们需要创建一个摄像机,并调整它的位置、裁剪平面、视角等,使得它显示的图像是我们希望的镜子的图像。</li>
<li>由于这个摄像机不需要直接显示在屏幕上,而是用于渲染纹理。因此我们可以直接创建一个 <strong>Texture</strong> 并拖拽到该摄像机的 <strong>Target Texture</strong> 上。</li>
</ol>
<p>关于 Quad 的着色器实现也非常简单,只需要声明一个纹理属性,然后将该纹理进行左右翻转后输出。</p>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Mirror</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">Texture</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Opaque</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">LOD</span> <span class="mi">100</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float2</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">-</span> <span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">x</span><span class="p">;</span> <span class="c1">// 反转 x 轴</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">fixed4</span> <span class="n">col</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">);</span>
<span class="k">return</span> <span class="n">col</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">FallBack</span> <span class="n">Off</span>
<span class="p">}</span>
</code></pre></div><h3 id="玻璃效果">玻璃效果</h3>
<p>Unity 中,还可以在 Unity Shader 中使用一种特殊的 Pass 来完成获取屏幕图像的目的,这就是 <strong>GrabPass</strong> 。通常会使用 <strong>GrabPass</strong> 来实现诸如 <code>玻璃等透明材质的模拟</code> ,与使用简单的透明混合不同,使用 <strong>GrabPass</strong> 可以让我们对物体后面的图像进行更复杂的处理,例如使用法线来模拟折射效果,而不再是简单的和原屏幕颜色混合。</p>
<p>当我们在 Shader 中定义了一个 <strong>GrabPass</strong> 后,Unity 会把当前屏幕的图像绘制在一张纹理中,以便我们在后续的 Pass 中访问它。需要注意的是,在使用 <strong>GrabPass</strong> 的时候,我们需要额外小心物体的渲染队列设置。正如之前所说,<strong>GrabPass</strong> 通常用于渲染透明物体,尽管代码里并不包含混合指令,但我们往往仍然需要把物体的 <strong>渲染队列</strong> 设置成 <strong>透明队列</strong> (即 <strong>“Queue”=“Transparent”</strong> )。这样才能保证渲染物体时,所有的不透明物体都已经被绘制在屏幕上,从而获得正确的屏幕图像。</p>
<h4 id="grabpass-的两种形式">GrabPass 的两种形式</h4>
<ul>
<li>直接使用 <strong>GrabPass{}</strong> ,然后在后续的 Pass 中直接使用 <strong>_GrabTexture</strong> 来访问屏幕图像。但是当场景中有多个物体都使用了这样的形式来抓取屏幕时,这种方法的性能消耗比较大,因为对于每一个使用它的物体,Unity 都会为它单独进行一次昂贵的屏幕抓取操作。但这种方法可以让每个物体得到不同的屏幕图像,这取决于它们的渲染队列及渲染它们时当前的屏幕缓冲中的颜色。</li>
<li>使用 <strong>GrabPass{“TextureName”}</strong> ,正如本节所实现,我们可以在后续的 Pass 中使用 <strong>TextureName</strong> 来访问屏幕图像。使用这种方法同样可以抓取屏幕,但 Unity 只会在每一帧为第一个使用名为 <strong>TextureName</strong> 的纹理的物体执行一次屏幕抓取操作,而这个纹理同时也可以在其它 Pass 中被访问。这种方法更加高效,因为不管场景中有多少物体使用了该命令,每一帧中 Unity 都只会执行一次抓取操作,这也意味着所有物体都会使用同一张屏幕图像。不过,在大多数情况下这已经足够了。</li>
</ul>
<p>在本节中,我们将会使用 <strong>GrabPass</strong> 来模拟一个玻璃效果。</p>
<h4 id="思路">思路</h4>
<p>这种效果实现非常简单,我们首先使用一张法线纹理来修改模型的法线信息,然后使用反射的方法,通过一个 <strong>Cubemap</strong> 来模拟玻璃反射,而在模拟折射时,则使用了 <strong>GrabPass</strong> 获取玻璃后面的屏幕图像,并使用切线空间下的法线对屏幕纹理坐标偏移后,再对屏幕图像进行采样来模拟近似的折射效果。</p>
<h4 id="具体实现">具体实现</h4>
<ol>
<li>搭建环境:建立一个房间,然后放置了一个立方体和球体,其中球体位于立方体内部,这是为了模拟玻璃对内部物体的折射效果。创建一个着色器付给立方体。</li>
<li>在着色器中,我们把 <strong>Queue</strong> 设置成 <strong>Transparent</strong> 可以保证该物体渲染时,其它所有不透明物体都已经被渲染到屏幕上了,否则就可能无法正确得到“透过玻璃看到的图像”。</li>
<li>然后设置 <strong>RenderType</strong> 为 <strong>Opaque</strong> 则是为了在使用 <strong>着色器替换 ( Shader Replacement )</strong> 时,该物体可以在需要时被正确渲染。这通常发生在我们需要得到摄像机的深度和法线纹理时。</li>
<li>通过关键词 <strong>CrabPass</strong> 定义了一个抓取屏幕图像的 Pass。在这个 Pass 中我们定义了一个字符串,该字符串内部的名称决定了抓取到的屏幕图像将会被存入哪个纹理中。实际上,我们可以省略声明该字符串,但直接声明纹理名称的方法往往可以得到更高的性能。</li>
<li>定义了 <strong>_RefractionTex</strong> 和 <strong>_RefractionTex_TexelSize</strong> 变量,这对应了在使用 <strong>GrabPass</strong> 时指定的纹理名称。 <strong>_RefractionTex_TexelSize</strong> 可以让我们得到该像素的纹理大小,例如一个大小为 256×512 的纹理,它的像素大小为(1/256, 1/512)。我们需要在对屏幕图像的采样坐标进行偏移时使用该量。</li>
<li>在顶点着色器中通过调用内置的 <strong>ComputeGrabScreenPos</strong> 函数来得到对应被抓取的屏幕图像的采样坐标。</li>
<li>在片元着色器中我们对法线纹理进行采样,得到切线空间下的法线方向。我们使用该值和 <strong>_Distortion</strong> 属性以及 <strong>_RefractionTex_TexelSize</strong> 来对屏幕图像的采样所需的坐标进行偏移,模拟折射效果。 <strong>_Distortion</strong> 值越大,偏移量越大,玻璃背后的物体看起来变形程度越大。</li>
<li>在对屏幕纹理进行采样时使用了 <strong>齐次除法</strong> 获取视口坐标下的坐标:( i.scrPos.xy / i.scrPos.w )</li>
<li>最后,我们使用 <strong>_RefractAmount</strong> 属性对反射和折射颜色进行混合,作为最终的输出颜色。</li>
</ol>
<div class="highlight"><pre class="chroma"><code class="language-glsl" data-lang="glsl"><span class="n">Shader</span> <span class="err">&#34;</span><span class="n">Unlit</span><span class="o">/</span><span class="n">Glass</span><span class="err">&#34;</span>
<span class="p">{</span>
<span class="n">Properties</span>
<span class="p">{</span>
<span class="n">_MainTex</span> <span class="p">(</span><span class="err">&#34;</span><span class="n">MainTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">white</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_BumpTex</span><span class="p">(</span><span class="err">&#34;</span><span class="n">BumpTex</span><span class="err">&#34;</span><span class="p">,</span> <span class="mi">2</span><span class="n">D</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">bump</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_Cubemap</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Cube</span> <span class="n">Map</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Cube</span><span class="p">)</span> <span class="o">=</span> <span class="err">&#34;</span><span class="n">_Skybox</span><span class="err">&#34;</span> <span class="p">{}</span>
<span class="n">_Distortion</span><span class="p">(</span><span class="err">&#34;</span><span class="n">Distortion</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">_RefractAmount</span><span class="p">(</span><span class="err">&#34;</span><span class="n">RefractAmount</span><span class="err">&#34;</span><span class="p">,</span> <span class="n">Range</span><span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">))</span> <span class="o">=</span> <span class="mf">1.0</span>
<span class="p">}</span>
<span class="n">SubShader</span>
<span class="p">{</span>
<span class="n">Tags</span> <span class="p">{</span> <span class="err">&#34;</span><span class="n">RenderType</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Opaque</span><span class="err">&#34;</span> <span class="err">&#34;</span><span class="n">Queue</span><span class="err">&#34;</span><span class="o">=</span><span class="err">&#34;</span><span class="n">Transparent</span><span class="err">&#34;</span> <span class="p">}</span>
<span class="n">GrabPass</span>
<span class="p">{</span>
<span class="err">&#34;</span><span class="n">_RefractionTex</span><span class="err">&#34;</span> <span class="c1">// GrabPass:用于抓取屏幕图像的 pass,内部字符串决定输出纹理名称</span>
<span class="p">}</span>
<span class="n">Pass</span>
<span class="p">{</span>
<span class="n">CGPROGRAM</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">vertex</span> <span class="n">vert</span>
<span class="err">#</span><span class="n">pragma</span> <span class="n">fragment</span> <span class="n">frag</span>
<span class="err">#</span><span class="n">include</span> <span class="err">&#34;</span><span class="n">UnityCG</span><span class="p">.</span><span class="n">cginc</span><span class="err">&#34;</span>
<span class="k">sampler2D</span> <span class="n">_MainTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_MainTex_ST</span><span class="p">;</span>
<span class="k">sampler2D</span> <span class="n">_BumpTex</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">_BumpTex_ST</span><span class="p">;</span>
<span class="n">samplerCUBE</span> <span class="n">_Cubemap</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_Distortion</span><span class="p">;</span>
<span class="k">float</span> <span class="n">_RefractAmount</span><span class="p">;</span>
<span class="k">sampler2D</span> <span class="n">_RefractionTex</span><span class="p">;</span> <span class="c1">// 获取屏幕图像输出的纹理</span>
<span class="n">float4</span> <span class="n">_RefractionTex_TexelSize</span><span class="p">;</span> <span class="c1">// 获取纹理的纹素大小</span>
<span class="k">struct</span> <span class="n">appdata</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">vertex</span> <span class="o">:</span> <span class="n">POSITION</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">texcoord</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">normal</span> <span class="o">:</span> <span class="n">NORMAL</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">tangent</span> <span class="o">:</span> <span class="n">TANGENT</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">struct</span> <span class="n">v2f</span>
<span class="p">{</span>
<span class="n">float4</span> <span class="n">pos</span> <span class="o">:</span> <span class="n">SV_POSITION</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">uv</span> <span class="o">:</span> <span class="n">TEXCOORD0</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">screenPos</span> <span class="o">:</span> <span class="n">TEXCOORD1</span><span class="p">;</span> <span class="c1">// 屏幕采样坐标</span>
<span class="n">float4</span> <span class="n">TtoW0</span> <span class="o">:</span> <span class="n">TEXCOORD2</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">TtoW1</span> <span class="o">:</span> <span class="n">TEXCOORD3</span><span class="p">;</span>
<span class="n">float4</span> <span class="n">TtoW2</span> <span class="o">:</span> <span class="n">TEXCOORD4</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">v2f</span> <span class="n">vert</span> <span class="p">(</span><span class="n">appdata</span> <span class="n">v</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">v2f</span> <span class="n">o</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">pos</span> <span class="o">=</span> <span class="n">UnityObjectToClipPos</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">xy</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="n">_MainTex_ST</span><span class="p">.</span><span class="n">zw</span><span class="p">;</span> <span class="c1">// 缩放和平移</span>
<span class="n">o</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">zw</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">texcoord</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_BumpTex_ST</span><span class="p">.</span><span class="n">xy</span> <span class="o">+</span> <span class="n">_BumpTex_ST</span><span class="p">.</span><span class="n">zw</span><span class="p">;</span> <span class="c1">// 缩放和平移</span>
<span class="n">o</span><span class="p">.</span><span class="n">screenPos</span> <span class="o">=</span> <span class="n">ComputeGrabScreenPos</span><span class="p">(</span><span class="n">o</span><span class="p">.</span><span class="n">pos</span><span class="p">);</span> <span class="c1">// ComputeGrabScreenPos 获取抓取屏幕的采样坐标</span>
<span class="n">float3</span> <span class="n">worldPos</span> <span class="o">=</span> <span class="n">mul</span><span class="p">(</span><span class="n">unity_ObjectToWorld</span><span class="p">,</span> <span class="n">v</span><span class="p">.</span><span class="n">vertex</span><span class="p">).</span><span class="n">xyz</span><span class="p">;</span>
<span class="n">float3</span> <span class="n">worldNormal</span> <span class="o">=</span> <span class="n">UnityObjectToWorldNormal</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">normal</span><span class="p">);</span>
<span class="n">float3</span> <span class="n">worldTangent</span> <span class="o">=</span> <span class="n">UnityObjectToWorldDir</span><span class="p">(</span><span class="n">v</span><span class="p">.</span><span class="n">tangent</span><span class="p">.</span><span class="n">xyz</span><span class="p">);</span>
<span class="n">float3</span> <span class="n">worldBinormal</span> <span class="o">=</span> <span class="n">cross</span><span class="p">(</span><span class="n">worldNormal</span><span class="p">,</span> <span class="n">worldTangent</span><span class="p">)</span> <span class="o">*</span> <span class="n">v</span><span class="p">.</span><span class="n">tangent</span><span class="p">.</span><span class="n">w</span><span class="p">;</span>
<span class="n">o</span><span class="p">.</span><span class="n">TtoW0</span> <span class="o">=</span> <span class="n">float4</span><span class="p">(</span><span class="n">worldTangent</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">worldBinormal</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">worldNormal</span><span class="p">.</span><span class="n">x</span><span class="p">,</span> <span class="n">worldPos</span><span class="p">.</span><span class="n">x</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">TtoW1</span> <span class="o">=</span> <span class="n">float4</span><span class="p">(</span><span class="n">worldTangent</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="n">worldBinormal</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="n">worldNormal</span><span class="p">.</span><span class="n">y</span><span class="p">,</span> <span class="n">worldPos</span><span class="p">.</span><span class="n">y</span><span class="p">);</span>
<span class="n">o</span><span class="p">.</span><span class="n">TtoW2</span> <span class="o">=</span> <span class="n">float4</span><span class="p">(</span><span class="n">worldTangent</span><span class="p">.</span><span class="n">z</span><span class="p">,</span> <span class="n">worldBinormal</span><span class="p">.</span><span class="n">z</span><span class="p">,</span> <span class="n">worldNormal</span><span class="p">.</span><span class="n">z</span><span class="p">,</span> <span class="n">worldPos</span><span class="p">.</span><span class="n">z</span><span class="p">);</span>
<span class="k">return</span> <span class="n">o</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">fixed4</span> <span class="n">frag</span> <span class="p">(</span><span class="n">v2f</span> <span class="n">i</span><span class="p">)</span> <span class="o">:</span> <span class="n">SV_Target</span>
<span class="p">{</span>
<span class="n">half3</span> <span class="n">worldPos</span> <span class="o">=</span> <span class="n">float3</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">TtoW0</span><span class="p">.</span><span class="n">w</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">TtoW1</span><span class="p">.</span><span class="n">w</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">TtoW2</span><span class="p">.</span><span class="n">w</span><span class="p">);</span>
<span class="n">half3</span> <span class="n">worldLightDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceLightDir</span><span class="p">(</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">worldViewDir</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">UnityWorldSpaceViewDir</span><span class="p">(</span><span class="n">worldPos</span><span class="p">));</span>
<span class="n">half3</span> <span class="n">bump</span> <span class="o">=</span> <span class="n">UnpackNormal</span><span class="p">(</span><span class="n">tex2D</span><span class="p">(</span><span class="n">_BumpTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">zw</span><span class="p">));</span>
<span class="n">half2</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">bump</span><span class="p">.</span><span class="n">xy</span> <span class="o">*</span> <span class="n">_Distortion</span> <span class="o">*</span> <span class="n">_RefractionTex_TexelSize</span><span class="p">.</span><span class="n">xy</span><span class="p">;</span>
<span class="n">i</span><span class="p">.</span><span class="n">screenPos</span><span class="p">.</span><span class="n">xy</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">*</span> <span class="n">i</span><span class="p">.</span><span class="n">screenPos</span><span class="p">.</span><span class="n">z</span> <span class="o">+</span> <span class="n">i</span><span class="p">.</span><span class="n">screenPos</span><span class="p">.</span><span class="n">xy</span><span class="p">;</span>
<span class="n">half3</span> <span class="n">refrCol</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_RefractionTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">screenPos</span><span class="p">.</span><span class="n">xy</span> <span class="o">/</span> <span class="n">i</span><span class="p">.</span><span class="n">screenPos</span><span class="p">.</span><span class="n">w</span><span class="p">).</span><span class="n">rgb</span><span class="p">;</span>
<span class="n">bump</span> <span class="o">=</span> <span class="n">normalize</span><span class="p">(</span><span class="n">half3</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">TtoW0</span><span class="p">.</span><span class="n">xyz</span><span class="p">,</span> <span class="n">bump</span><span class="p">),</span> <span class="n">dot</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">TtoW1</span><span class="p">.</span><span class="n">xyz</span><span class="p">,</span> <span class="n">bump</span><span class="p">),</span> <span class="n">dot</span><span class="p">(</span><span class="n">i</span><span class="p">.</span><span class="n">TtoW2</span><span class="p">.</span><span class="n">xyz</span><span class="p">,</span> <span class="n">bump</span><span class="p">)));</span>
<span class="n">half3</span> <span class="n">reflDir</span> <span class="o">=</span> <span class="n">reflect</span><span class="p">(</span><span class="o">-</span><span class="n">worldViewDir</span><span class="p">,</span> <span class="n">bump</span><span class="p">);</span>
<span class="n">half4</span> <span class="n">texColor</span> <span class="o">=</span> <span class="n">tex2D</span><span class="p">(</span><span class="n">_MainTex</span><span class="p">,</span> <span class="n">i</span><span class="p">.</span><span class="n">uv</span><span class="p">.</span><span class="n">xy</span><span class="p">);</span>
<span class="n">half3</span> <span class="n">reflCol</span> <span class="o">=</span> <span class="n">texCUBE</span><span class="p">(</span><span class="n">_Cubemap</span><span class="p">,</span> <span class="n">reflDir</span><span class="p">).</span><span class="n">rgb</span> <span class="o">*</span> <span class="n">texColor</span><span class="p">.</span><span class="n">rgb</span><span class="p">;</span>
<span class="n">half3</span> <span class="n">col</span> <span class="o">=</span> <span class="n">reflCol</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">_RefractAmount</span><span class="p">)</span> <span class="o">+</span> <span class="n">refrCol</span> <span class="o">*</span> <span class="n">_RefractAmount</span><span class="p">;</span>
<span class="k">return</span> <span class="n">half4</span><span class="p">(</span><span class="n">col</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">ENDCG</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">Fallback</span> <span class="err">&#34;</span><span class="n">Diffuse</span><span class="err">&#34;</span>
<span class="p">}</span>
</code></pre></div><h3 id="渲染纹理-vs-grabpass">渲染纹理 VS GrabPass</h3>
<ul>
<li>GrabPass 的好处在于实现简单,我们只需在 Shader 中写几行代码就可以实现抓取屏幕的问题。</li>
<li>从效率上来讲,使用渲染纹理的效率往往要好于 GrabPass ,尤其在移动设备上。使用渲染纹理我们可以自定义渲染纹理的大小,尽管这种方法需要把部分场景再次渲染一遍,但我们可以通过调整摄像机的渲染层来减少二次渲染时的场景大小,或使用其它方法来控制摄像机是否需要开启。</li>
</ul>
<p>GrabPass 获取到的图像分辨率和显示屏幕是一致的,这意味着在一些高分辨率的设备上可能会造成严重的带宽影响。</p>
<p>在移动设备上,GrabPass 虽然不会重新渲染场景,但它往往需要 CPU 直接读取后备缓冲(back buffer)中的数据,破坏了 CPU 和 GPU 之间的并行性,这是比较耗时的,甚至在一些移动设备上这是不支持的。</p>
<h3 id="命令缓存-command-buffers">命令缓存 (Command Buffers)</h3>
<p>在 Unity5 中,Unity 引入了 <strong>命令缓冲(Command Buffers)</strong> 来允许我们扩展 Unity 的渲染流水线。使用命令缓冲我们也可以得到类似抓屏的效果,它可以在不透明物体渲染后把当前的图像复制到一个临时的渲染目标纹理中,然后在那里进行一些额外的操作,例如模糊等,最后把图像传递给需要使用它的物体进行处理和显示。</p>
<p>有关命令缓冲区更多知识,移步:<a class="link" href="https://docs.unity.cn/cn/current/Manual/GraphicsCommandBuffers.html" target="_blank" rel="noopener"
>图形命令缓冲区 - Unity 手册</a></p>
<h2 id="程序纹理">程序纹理</h2>
<p><strong>程序纹理(Procedural Texture)</strong> 指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或非常真实的自然元素,例如木头、石子等。</p>
<p>使用 <strong>程序纹理</strong> 的好处在于我们可以使用各种参数来控制纹理的外观,而这些属性不仅仅是那些颜色属性,甚至可以是完全不同类型的图案属性,这是我们可以得到更加丰富的动画和视觉效果。</p>
<div class="highlight"><pre class="chroma"><code class="language-c#" data-lang="c#"><span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">System.Collections.Generic</span><span class="p">;</span>
<span class="na">
</span><span class="na">[ExecuteInEditMode]</span><span class="c1">//用于编辑器下运行
</span><span class="c1"></span><span class="k">public</span> <span class="k">class</span> <span class="nc">ProceduralTextureGeneration</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
<span class="c1">// 声明材质
</span><span class="c1"></span> <span class="k">public</span> <span class="n">Material</span> <span class="n">material</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
<span class="cp">#region Material properties//材质属性
</span><span class="cp"></span> <span class="c1">// 纹理大小
</span><span class="c1"></span><span class="na"> [SerializeField, SetProperty(&#34;textureWidth&#34;)]</span>
<span class="c1">// 值通常是2的整数幂
</span><span class="c1"></span> <span class="k">private</span> <span class="kt">int</span> <span class="n">m_textureWidth</span> <span class="p">=</span> <span class="m">512</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">textureWidth</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">m_textureWidth</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">set</span>
<span class="p">{</span>
<span class="n">m_textureWidth</span> <span class="p">=</span> <span class="k">value</span><span class="p">;</span>
<span class="m">_</span><span class="n">UpdateMaterial</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// 纹理背景颜色
</span><span class="c1"></span><span class="na"> [SerializeField, SetProperty(&#34;backgroundColor&#34;)]</span>
<span class="k">private</span> <span class="n">Color</span> <span class="n">m_backgroundColor</span> <span class="p">=</span> <span class="n">Color</span><span class="p">.</span><span class="n">white</span><span class="p">;</span>
<span class="k">public</span> <span class="n">Color</span> <span class="n">backgroundColor</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">m_backgroundColor</span><span class="p">;</span>
<span class="p">}</span>