質問するログイン新規登録

回答編集履歴

1

フィルタ処理の影響を後段に及ぼさないための修正案を追記

2018/10/18 21:58

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -232,4 +232,150 @@
232
232
  ご質問者さんのおっしゃる「反復回数の異なる複数のシェーダーを作る」という方式なら、小反復回数のときの無駄な「グラブ」→「描画」部分を丸ごと削除することができるため、より高速化できると思います。
233
233
 
234
234
  やはり、補助的なC#スクリプト抜きだとちょっと非効率的になってしまいますね。特に多数の曇りガラスオブジェクトがある場合は、オブジェクトの数だけグラブパスが増えますので負荷が気になりそうです。
235
- ただ、この形ですと「曇りガラスオブジェクトの手前に別の曇りガラスオブジェクトがある場合、手前のオブジェクトを透かして見た背後の曇りガラスオブジェクトはさらにぼやけて見える」という視覚的もっともらしさがあると思います。
235
+ ただ、この形ですと「曇りガラスオブジェクトの手前に別の曇りガラスオブジェクトがある場合、手前のオブジェクトを透かして見た背後の曇りガラスオブジェクトはさらにぼやけて見える」という視覚的もっともらしさがあると思います。
236
+
237
+ ### 追記
238
+
239
+ 1回目の平均化処理を行う部分までを下記のように変更してみました。
240
+
241
+ RepeatAve.shader
242
+ ```ShaderLab
243
+ Shader "Custom/RepeatAve"
244
+ {
245
+ Properties
246
+ {
247
+ _Color ("Color", Color) = (1, 1, 1, 1)
248
+ _MainTex ("Diffuse", 2D) = "white" {}
249
+ _Noise ("Noise", 2D) = "gray" {}
250
+ _Range ("Range", Float) = 0.025
251
+ _Blur ("Blur", Float) = 0.005
252
+ [KeywordEnum(R0,R1,R2,R3,R4,R5,R6,R7,R8)] _Repeat ("Repeat", Float) = 0
253
+ _Size ("Size", int) = 3
254
+ }
255
+
256
+ SubShader
257
+ {
258
+ Tags { "Queue" = "Transparent" }
259
+ Cull Off
260
+ ZWrite Off
261
+ Stencil
262
+ {
263
+ Ref 0
264
+ Comp Equal
265
+ }
266
+
267
+ CGINCLUDE
268
+ #pragma target 3.0
269
+ #include "UnityCG.cginc"
270
+ ENDCG
271
+
272
+ GrabPass{"_FirstGrabTexture"}
273
+ pass
274
+ {
275
+ CGPROGRAM
276
+ #pragma multi_compile _ _REPEAT_R1 _REPEAT_R2 _REPEAT_R3 _REPEAT_R4 _REPEAT_R5 _REPEAT_R6 _REPEAT_R7 _REPEAT_R8
277
+ #pragma vertex vertAve
278
+ #pragma fragment fragAveFirst
279
+ #if _REPEAT_R1 | _REPEAT_R2 | _REPEAT_R3 | _REPEAT_R4 | _REPEAT_R5 | _REPEAT_R6 | _REPEAT_R7 | _REPEAT_R8
280
+ #define _AVE
281
+ #endif
282
+ #include "RepeatAve.cginc"
283
+ sampler2D _FirstGrabTexture;
284
+ half4 fragAveFirst(v2fAve i): SV_Target
285
+ {
286
+ float2 uv = i.screenPos.xy / i.screenPos.w;
287
+ half4 frost = 0.0;
288
+ #ifdef _AVE
289
+ for (int m = - (_Size - 1) / 2; m <= (_Size - 1) / 2; m ++)
290
+ {
291
+ for (int n = - (_Size - 1) / 2; n <= (_Size - 1) / 2; n ++)
292
+ {
293
+ frost += tex2D(_FirstGrabTexture, uv + float2(_Blur * m, _Blur * n));
294
+ }
295
+ }
296
+ frost /= _Size * _Size;
297
+ #else
298
+ frost = tex2D(_FirstGrabTexture, uv);
299
+ #endif
300
+ return half4(frost.xyz, 1);
301
+ }
302
+ ENDCG
303
+ }
304
+
305
+ // 省略...残り7回のフィルタ処理および最終合成処理には変更なし
306
+ }
307
+ Fallback Off
308
+ }
309
+ ```
310
+
311
+ RepeatAve.cgincに変更はありません。
312
+ 1回目のGrabPassのみグラブ先を`_FirstGrabTexture`として平均化もそのテクスチャを参照するようにし、2回目以降は先と同様にテクスチャ名指定なしでグラブ・平均化しています。
313
+ テクスチャ名指定ありでグラブすると最初の1回だけグラブが行われるので、フィルタ処理の起点は毎回最初のグラブ結果となります。このため、後で描画されるマテリアルにそれまでのフィルタ処理の影響が及ばなくなり、「曇りガラスを重ねるほどぼけ幅が大きくなる」という現象を回避できるかと思います。
314
+ また、ついでにコメントで申し上げた`ZWrite Off`を加え、さらにステンシルテストを有効にしてステンシルバッファの内容が0の場合だけ描画を行うようにしました。
315
+
316
+ これと併せて、ステンシル値を加算してステンシルバッファの内容を0でなくするマテリアルをマスク用オブジェクトに使用すれば、キューの大小に基づいたマスキングが可能かと思います。
317
+
318
+ StencilMask.shader
319
+ ```ShaderLab
320
+ Shader "Custom/StencilMask"
321
+ {
322
+ Properties
323
+ {
324
+ }
325
+
326
+ SubShader
327
+ {
328
+ Tags { "Queue" = "Transparent" }
329
+ Cull Off
330
+ ZWrite Off
331
+ Stencil
332
+ {
333
+ Pass IncrSat
334
+ }
335
+ ColorMask 0
336
+
337
+ Pass
338
+ {
339
+ CGPROGRAM
340
+ #pragma target 3.0
341
+ #pragma vertex vert
342
+ #pragma fragment frag
343
+ #include "UnityCG.cginc"
344
+ struct v2f
345
+ {
346
+ float4 pos: SV_POSITION;
347
+ };
348
+ v2f vert(appdata_full v)
349
+ {
350
+ v2f o;
351
+ o.pos = UnityObjectToClipPos(v.vertex);
352
+ return o;
353
+ }
354
+ half4 frag(v2f i): SV_Target
355
+ {
356
+ return 0.0;
357
+ }
358
+ ENDCG
359
+ }
360
+ }
361
+ Fallback Off
362
+ }
363
+ ```
364
+
365
+ テストとして、下記3種のマテリアルを作成し...
366
+
367
+ - マテリアルA...赤ガラス、ぼけ幅中、ノイズ効果小、キュー2501
368
+ - マテリアルB...緑ガラス、ぼけ幅大、ノイズ効果大、キュー2511
369
+ - マテリアルC...青ガラス、ぼけ幅小、ノイズ効果なし、キュー2521
370
+
371
+ これらを、`_FirstGrabTexture`を使わない従来通りの方法で描画すると、下図のように先に描画した曇りガラスの影響が後段にも引き継がれますが...
372
+
373
+ ![旧描画方式](3a3fdc17044bfa8212aa736b9917d2c0.png)
374
+
375
+ `_FirstGrabTexture`を使う方式にすると、下図のような結果となります。
376
+
377
+ ![新描画方式](d5703f03f7bdf8c645bdd30f6cccebbd.png)
378
+
379
+ また、キュー2510のマスクオブジェクト(Aの次に描画され、描画領域へのB・Cの描画を防止する)を左右に横切るよう配置、キュー2520のマスクオブジェクト(Bの次に描画され、描画領域へのCの描画を防止する)を上下に横切るよう配置して、描画過程を見てみると下図のようになります。
380
+
381
+ ![マスク処理](9c00cd6241cbe946d4b3c3c69a5e820e.gif)