回答編集履歴
1
変数名変更、ループ展開版を追記
test
CHANGED
@@ -26,7 +26,7 @@
|
|
26
26
|
|
27
27
|
// _Blur("Blur", Float) = 0.005 // Blurは廃止、サンプリング位置ずらしはテクセルサイズに基づいた形に変更
|
28
28
|
|
29
|
-
|
29
|
+
_Sigma("Sigma", Range(0.01, 8.0)) = 1.0 // σを追加
|
30
30
|
|
31
31
|
}
|
32
32
|
|
@@ -36,7 +36,327 @@
|
|
36
36
|
|
37
37
|
{
|
38
38
|
|
39
|
-
|
39
|
+
// スカイボックスも含めてグラブするため、キューをTransparentに変更
|
40
|
+
|
41
|
+
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
Cull Off
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
GrabPass{ "_Frost" }
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
CGINCLUDE
|
54
|
+
|
55
|
+
#include "UnityCG.cginc"
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
half4 _Color;
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
sampler2D _MainTex;
|
64
|
+
|
65
|
+
float4 _MainTex_ST;
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
sampler2D _Frost;
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
// グラブテクスチャのテクセルサイズを追加
|
74
|
+
|
75
|
+
float4 _Frost_TexelSize;
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
sampler2D _Noise;
|
80
|
+
|
81
|
+
float4 _Noise_ST;
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
half _Range;
|
86
|
+
|
87
|
+
// half _Blur; // _Blurは廃止
|
88
|
+
|
89
|
+
float _Sigma; // _Sigmaを追加
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
// 重み計算用関数
|
94
|
+
|
95
|
+
// ご質問者さんの重み関数 1/(2*π*σ)*exp(-(x^2+y^2)/(2*σ^2)) と同様ですが、係数は1に変更しました
|
96
|
+
|
97
|
+
// 係数を付けて正規化した重みで有限の範囲をたたみ込むと、重みの総和が1より小さくなってしまうため
|
98
|
+
|
99
|
+
// できあがったぼかし画像は、もとの画像より暗くなってしまうと思われます
|
100
|
+
|
101
|
+
// そこで重み関数には係数を付けず、たたみ込みの際に重みの総和を求めて、最後にたたみ込み結果を
|
102
|
+
|
103
|
+
// 重みの総和で割ることで明るさが維持されるようにしました
|
104
|
+
|
105
|
+
inline float getWeight(float2 xy)
|
106
|
+
|
107
|
+
{
|
108
|
+
|
109
|
+
return exp(-dot(xy, xy) / (2.0 * _Sigma * _Sigma));
|
110
|
+
|
111
|
+
}
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
// カーネルサイズ計算用関数
|
116
|
+
|
117
|
+
// たたみ込み範囲の片側幅...カーネルの一辺の長さ2*n+1のnをいくつにするかですが、さしあたり
|
118
|
+
|
119
|
+
// 中心から最も遠い点(カーネルの角)における重みが十分小さくなる(0.0001を切る)大きさにしました
|
120
|
+
|
121
|
+
// σ=1でn=4となり、サンプリング回数は(2*4+1)^2=81回になります(多分...)
|
122
|
+
|
123
|
+
// σ=2で225回、σ=4で729回、σ=8で2601回...といった具合に、2乗のオーダーでサンプリング回数が
|
124
|
+
|
125
|
+
// 増大しますので、あんまりσを大きくしすぎるのは控えた方がいいでしょう
|
126
|
+
|
127
|
+
// ぼかしを縦方向と横方向の二段階に分けることで、サンプリング回数の増大を1乗のオーダーに
|
128
|
+
|
129
|
+
// 抑えるテクニックもありますので、負荷を軽減したい場合は採用を検討してみてもいいでしょう
|
130
|
+
|
131
|
+
inline int getKernelN()
|
132
|
+
|
133
|
+
{
|
134
|
+
|
135
|
+
return (int)ceil(_Sigma * sqrt(-log(0.0001)));
|
136
|
+
|
137
|
+
}
|
138
|
+
|
139
|
+
|
140
|
+
|
141
|
+
ENDCG
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
Pass
|
146
|
+
|
147
|
+
{
|
148
|
+
|
149
|
+
CGPROGRAM
|
150
|
+
|
151
|
+
#pragma target 3.0
|
152
|
+
|
153
|
+
#pragma vertex vert
|
154
|
+
|
155
|
+
#pragma fragment frag
|
156
|
+
|
157
|
+
|
158
|
+
|
159
|
+
struct v2f
|
160
|
+
|
161
|
+
{
|
162
|
+
|
163
|
+
float4 pos : SV_POSITION;
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
float3 uv : TEXCOORD;
|
168
|
+
|
169
|
+
float4 screenPos : TEXCOORD1;
|
170
|
+
|
171
|
+
float3 ray : TEXCOORD2;
|
172
|
+
|
173
|
+
};
|
174
|
+
|
175
|
+
|
176
|
+
|
177
|
+
v2f vert(appdata_full v)
|
178
|
+
|
179
|
+
{
|
180
|
+
|
181
|
+
v2f o;
|
182
|
+
|
183
|
+
o.pos = UnityObjectToClipPos(v.vertex);
|
184
|
+
|
185
|
+
o.uv = v.texcoord;
|
186
|
+
|
187
|
+
o.screenPos = ComputeGrabScreenPos(o.pos);
|
188
|
+
|
189
|
+
o.ray = UnityObjectToViewPos(v.vertex).xyz * float3(-1, -1, 1);
|
190
|
+
|
191
|
+
return o;
|
192
|
+
|
193
|
+
}
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
half4 frag(v2f i) : SV_Target
|
198
|
+
|
199
|
+
{
|
200
|
+
|
201
|
+
// rayは使用していないようですが、一応残しておきました
|
202
|
+
|
203
|
+
i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
float2 uv = i.screenPos.xy / i.screenPos.w;
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy;
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
frostUV -= 0.5;
|
216
|
+
|
217
|
+
frostUV *= _Range;
|
218
|
+
|
219
|
+
frostUV += uv;
|
220
|
+
|
221
|
+
|
222
|
+
|
223
|
+
// 霜のついたガラスを表現するためサンプリング位置にノイズを加えているようですが、
|
224
|
+
|
225
|
+
// もし純粋にぼかし効果だけをかけたい場合は、uvをそのまま使うといいでしょう
|
226
|
+
|
227
|
+
// frostUV = uv;
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
int kernelN = getKernelN();
|
232
|
+
|
233
|
+
float weightSum = 0.0;
|
234
|
+
|
235
|
+
float4 frost = 0.0;
|
236
|
+
|
237
|
+
|
238
|
+
|
239
|
+
// 注目しているピクセルを中心に、-kernelN ~ +kernelNの範囲をたたみ込む
|
240
|
+
|
241
|
+
for (int m = -kernelN; m <= kernelN; m++)
|
242
|
+
|
243
|
+
{
|
244
|
+
|
245
|
+
for (int n = -kernelN; n <= kernelN; n++)
|
246
|
+
|
247
|
+
{
|
248
|
+
|
249
|
+
float2 texelOffset = float2(n, m);
|
250
|
+
|
251
|
+
float weight = getWeight(texelOffset);
|
252
|
+
|
253
|
+
weightSum += weight;
|
254
|
+
|
255
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
256
|
+
|
257
|
+
}
|
258
|
+
|
259
|
+
}
|
260
|
+
|
261
|
+
|
262
|
+
|
263
|
+
// 最後に、重みの総和で割る
|
264
|
+
|
265
|
+
frost /= weightSum;
|
266
|
+
|
267
|
+
|
268
|
+
|
269
|
+
half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw);
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
half alpha = _Color.a * diffuse.a;
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1);
|
278
|
+
|
279
|
+
}
|
280
|
+
|
281
|
+
|
282
|
+
|
283
|
+
ENDCG
|
284
|
+
|
285
|
+
}
|
286
|
+
|
287
|
+
}
|
288
|
+
|
289
|
+
|
290
|
+
|
291
|
+
Fallback Off
|
292
|
+
|
293
|
+
}
|
294
|
+
|
295
|
+
```
|
296
|
+
|
297
|
+
|
298
|
+
|
299
|
+
コード中のコメントでもちょっと言及しましたが、このままではσを大きくすると、毎フレーム描画では重さが気になるかもしれません。
|
300
|
+
|
301
|
+
追加のレンダーテクスチャが必要になるためコードはちょっとややこしくなるでしょうが、[wgld.org | WebGL: gaussian フィルタ |](https://wgld.org/d/webgl/w057.html)で紹介されているような縦横二段掛け方式とか、[品質とパフォーマンスを両立する独自の技術|YEBIS 3](https://www.siliconstudio.co.jp/middleware/yebis/jp/features/processing_algorithms/)で解説されているような低解像度化してぼかし処理を行うテクニックを用いれば、もっと軽量化できるかもしれませんね。
|
302
|
+
|
303
|
+
|
304
|
+
|
305
|
+
σ = 1、ノイズ効果なし
|
306
|
+
|
307
|
+
![1](ccbf520945b052ca6f82bca2fec63008.png)
|
308
|
+
|
309
|
+
σ = 1、ノイズ効果あり
|
310
|
+
|
311
|
+
![F1](f97d3d3faa656cc5a8db3702cc13ff2d.png)
|
312
|
+
|
313
|
+
σ = 8、ノイズ効果なし
|
314
|
+
|
315
|
+
![8](59f729096d3d242d5c5e10ae710db25b.png)
|
316
|
+
|
317
|
+
σ = 8、ノイズ効果あり
|
318
|
+
|
319
|
+
![F8](edd8232aac26f3a693c9d12261d30d8d.png)
|
320
|
+
|
321
|
+
|
322
|
+
|
323
|
+
### ループ展開版
|
324
|
+
|
325
|
+
非展開版と比べると、シェーダーのインポートに時間がかかるかもしれません。
|
326
|
+
|
327
|
+
`KERNEL_N_MAX`をもっと小さくすると、対応可能なぼかし幅は小さくなってしまうものの、インポート速度・動作速度は改善するかと思います。
|
328
|
+
|
329
|
+
```ShaderLab
|
330
|
+
|
331
|
+
Shader "Custom/Frost"
|
332
|
+
|
333
|
+
{
|
334
|
+
|
335
|
+
Properties
|
336
|
+
|
337
|
+
{
|
338
|
+
|
339
|
+
_Color("Color", Color) = (1, 1, 1, 1)
|
340
|
+
|
341
|
+
|
342
|
+
|
343
|
+
_MainTex("Diffuse", 2D) = "white" {}
|
344
|
+
|
345
|
+
_Noise("Noise", 2D) = "black" {}
|
346
|
+
|
347
|
+
|
348
|
+
|
349
|
+
_Range("Range", Float) = 0.025
|
350
|
+
|
351
|
+
_Sigma("Sigma", Range(0.01, 8.0)) = 1.0
|
352
|
+
|
353
|
+
}
|
354
|
+
|
355
|
+
|
356
|
+
|
357
|
+
SubShader
|
358
|
+
|
359
|
+
{
|
40
360
|
|
41
361
|
Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" }
|
42
362
|
|
@@ -68,10 +388,6 @@
|
|
68
388
|
|
69
389
|
sampler2D _Frost;
|
70
390
|
|
71
|
-
|
72
|
-
|
73
|
-
// グラブテクスチャのテクセルサイズを追加
|
74
|
-
|
75
391
|
float4 _Frost_TexelSize;
|
76
392
|
|
77
393
|
|
@@ -84,24 +400,12 @@
|
|
84
400
|
|
85
401
|
half _Range;
|
86
402
|
|
87
|
-
// half _Blur; // _Blurは廃止
|
88
|
-
|
89
|
-
float _Sigma;
|
403
|
+
float _Sigma;
|
90
404
|
|
91
405
|
|
92
406
|
|
93
407
|
// 重み計算用関数
|
94
408
|
|
95
|
-
// ご質問者さんの重み関数 1/(2*π*σ)*exp(-(x^2+y^2)/(2*σ^2)) と同様ですが、係数は1に変更しました
|
96
|
-
|
97
|
-
// 係数を付けて正規化した重みで有限の範囲をたたみ込むと、重みの総和が1より小さくなってしまうため
|
98
|
-
|
99
|
-
// できあがったぼかし画像は、もとの画像より暗くなってしまうと思われます
|
100
|
-
|
101
|
-
// そこで重み関数には係数を付けず、たたみ込みの際に重みの総和を求めて、最後にたたみ込み結果を
|
102
|
-
|
103
|
-
// 重みの総和で割ることで明るさが維持されるようにしました
|
104
|
-
|
105
409
|
inline float getWeight(float2 xy)
|
106
410
|
|
107
411
|
{
|
@@ -114,20 +418,6 @@
|
|
114
418
|
|
115
419
|
// カーネルサイズ計算用関数
|
116
420
|
|
117
|
-
// たたみ込み範囲の片側幅...カーネルの一辺の長さ2*n+1のnをいくつにするかですが、さしあたり
|
118
|
-
|
119
|
-
// 中心から最も遠い点(カーネルの角)における重みが十分小さくなる(0.0001を切る)大きさにしました
|
120
|
-
|
121
|
-
// σ=1でn=4となり、サンプリング回数は(2*4+1)^2=81回になります(多分...)
|
122
|
-
|
123
|
-
// σ=2で225回、σ=4で729回、σ=8で2601回...といった具合に、2乗のオーダーでサンプリング回数が
|
124
|
-
|
125
|
-
// 増大しますので、あんまりσを大きくしすぎるのは控えた方がいいでしょう
|
126
|
-
|
127
|
-
// ぼかしを縦方向と横方向の二段階に分けることで、サンプリング回数の増大を1乗のオーダーに
|
128
|
-
|
129
|
-
// 抑えるテクニックもありますので、負荷を軽減したい場合は採用を検討してみてもいいでしょう
|
130
|
-
|
131
421
|
inline int getKernelN()
|
132
422
|
|
133
423
|
{
|
@@ -138,6 +428,12 @@
|
|
138
428
|
|
139
429
|
|
140
430
|
|
431
|
+
// 最大kernelN...(int)ceil(8 * sqrt(-ln(0.0001)))
|
432
|
+
|
433
|
+
#define KERNEL_N_MAX 25
|
434
|
+
|
435
|
+
|
436
|
+
|
141
437
|
ENDCG
|
142
438
|
|
143
439
|
|
@@ -198,8 +494,6 @@
|
|
198
494
|
|
199
495
|
{
|
200
496
|
|
201
|
-
// rayは使用していないようですが、一応残しておきました
|
202
|
-
|
203
497
|
i.ray = i.ray * (_ProjectionParams.z / i.ray.z);
|
204
498
|
|
205
499
|
|
@@ -218,50 +512,112 @@
|
|
218
512
|
|
219
513
|
frostUV += uv;
|
220
514
|
|
221
|
-
|
222
|
-
|
223
|
-
// 霜のついたガラスを表現するためサンプリング位置にノイズを加えているようですが、
|
224
|
-
|
225
|
-
// もし純粋にぼかし効果だけをかけたい場合は、uvをそのまま使うといいでしょう
|
226
|
-
|
227
515
|
// frostUV = uv;
|
228
516
|
|
229
517
|
|
230
518
|
|
231
519
|
int kernelN = getKernelN();
|
232
520
|
|
521
|
+
|
522
|
+
|
523
|
+
// ループ展開版
|
524
|
+
|
525
|
+
float2 texelOffset = float2(0, 0);
|
526
|
+
|
527
|
+
float weight = getWeight(texelOffset);
|
528
|
+
|
233
|
-
|
529
|
+
float weightSum = weight;
|
234
|
-
|
530
|
+
|
235
|
-
float4 frost =
|
531
|
+
float4 frost = weight * tex2D(_Frost, frostUV);
|
236
|
-
|
237
|
-
|
238
|
-
|
532
|
+
|
533
|
+
|
534
|
+
|
239
|
-
|
535
|
+
[unroll(KERNEL_N_MAX)]
|
240
|
-
|
536
|
+
|
241
|
-
|
537
|
+
for (int n = 0; n < kernelN; n++)
|
538
|
+
|
539
|
+
{
|
540
|
+
|
541
|
+
int x = n + 1;
|
542
|
+
|
543
|
+
|
544
|
+
|
545
|
+
texelOffset = float2(x, 0);
|
546
|
+
|
547
|
+
weight = getWeight(texelOffset);
|
548
|
+
|
549
|
+
weightSum += 2 * weight;
|
550
|
+
|
551
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
552
|
+
|
553
|
+
texelOffset = float2(-x, 0);
|
554
|
+
|
555
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
556
|
+
|
557
|
+
}
|
558
|
+
|
559
|
+
|
560
|
+
|
561
|
+
[unroll(KERNEL_N_MAX)]
|
562
|
+
|
563
|
+
for (int m = 0; m < kernelN; m++)
|
242
564
|
|
243
565
|
{
|
244
566
|
|
567
|
+
int y = m + 1;
|
568
|
+
|
569
|
+
|
570
|
+
|
571
|
+
texelOffset = float2(0, y);
|
572
|
+
|
573
|
+
weight = getWeight(texelOffset);
|
574
|
+
|
575
|
+
weightSum += 2 * weight;
|
576
|
+
|
577
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
578
|
+
|
579
|
+
texelOffset = float2(0, -y);
|
580
|
+
|
581
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
582
|
+
|
583
|
+
|
584
|
+
|
585
|
+
[unroll(KERNEL_N_MAX)]
|
586
|
+
|
245
|
-
for (int
|
587
|
+
for (int n = 0; n < kernelN; n++)
|
246
588
|
|
247
589
|
{
|
248
590
|
|
591
|
+
int x = n + 1;
|
592
|
+
|
593
|
+
|
594
|
+
|
249
|
-
|
595
|
+
texelOffset = float2(x, y);
|
250
|
-
|
596
|
+
|
251
|
-
|
597
|
+
weight = getWeight(texelOffset);
|
252
|
-
|
598
|
+
|
253
|
-
weightSum += weight;
|
599
|
+
weightSum += 4 * weight;
|
254
600
|
|
255
601
|
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
256
602
|
|
603
|
+
texelOffset = float2(-x, y);
|
604
|
+
|
605
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
606
|
+
|
607
|
+
texelOffset = float2(x, -y);
|
608
|
+
|
609
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
610
|
+
|
611
|
+
texelOffset = float2(-x, -y);
|
612
|
+
|
613
|
+
frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy);
|
614
|
+
|
257
615
|
}
|
258
616
|
|
259
617
|
}
|
260
618
|
|
261
619
|
|
262
620
|
|
263
|
-
// 最後に、重みの総和で割る
|
264
|
-
|
265
621
|
frost /= weightSum;
|
266
622
|
|
267
623
|
|
@@ -293,27 +649,3 @@
|
|
293
649
|
}
|
294
650
|
|
295
651
|
```
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
コード中のコメントでもちょっと言及しましたが、このままではσを大きくすると、毎フレーム描画では重さが気になるかもしれません。
|
300
|
-
|
301
|
-
追加のレンダーテクスチャが必要になるためコードはちょっとややこしくなるでしょうが、[wgld.org | WebGL: gaussian フィルタ |](https://wgld.org/d/webgl/w057.html)で紹介されているような縦横二段掛け方式とか、[品質とパフォーマンスを両立する独自の技術|YEBIS 3](https://www.siliconstudio.co.jp/middleware/yebis/jp/features/processing_algorithms/)で解説されているような低解像度化してぼかし処理を行うテクニックを用いれば、もっと軽量化できるかもしれませんね。
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
σ = 1、ノイズ効果なし
|
306
|
-
|
307
|
-
![1](ccbf520945b052ca6f82bca2fec63008.png)
|
308
|
-
|
309
|
-
σ = 1、ノイズ効果あり
|
310
|
-
|
311
|
-
![F1](f97d3d3faa656cc5a8db3702cc13ff2d.png)
|
312
|
-
|
313
|
-
σ = 8、ノイズ効果なし
|
314
|
-
|
315
|
-
![8](59f729096d3d242d5c5e10ae710db25b.png)
|
316
|
-
|
317
|
-
σ = 8、ノイズ効果あり
|
318
|
-
|
319
|
-
![F8](edd8232aac26f3a693c9d12261d30d8d.png)
|