f(x,y)=1/(2πσ)exp(-(x^2+y^2)/(2σ^2))
unityのshaderで上記の式を基にしたガウシアンブラーを実装したいと考えています。フィルタサイズは気にしていませんが、σの値をプロパティで変えることで、ブラーの強さを調節するプログラムにしたいと考えています。
しかし、フィルタサイズは3×3で、σの値が不可変のshaderプログラムしか作成できませんでした。どのようにすればσの値が可変な上記の式を実装できるか分からず質問させていただきました。下記は現在のプログラムです。
shader
1Shader "Custom/Frost" 2{ 3 Properties 4 { 5 _Color("Color", Color) = (1, 1, 1, 1) 6 7 _MainTex("Diffuse", 2D) = "white" {} 8 _Noise("Noise", 2D) = "black" {} 9 10 _Range("Range", Float) = 0.025 11 _Blur("Blur", Float) = 0.005 12 } 13 14 SubShader 15 { 16 Cull Off 17 18 GrabPass{ "_Frost" } 19 20 CGINCLUDE 21 #include "UnityCG.cginc" 22 23 half4 _Color; 24 25 sampler2D _MainTex; 26 float4 _MainTex_ST; 27 28 sampler2D _Frost; 29 sampler2D _Noise; 30 float4 _Noise_ST; 31 32 half _Range; 33 half _Blur; 34 35 ENDCG 36 37 Pass 38 { 39 CGPROGRAM 40 #pragma target 3.0 41 #pragma vertex vert 42 #pragma fragment frag 43 44 struct v2f 45 { 46 float4 pos : SV_POSITION; 47 48 float3 uv : TEXCOORD; 49 float4 screenPos : TEXCOORD1; 50 float3 ray : TEXCOORD2; 51 }; 52 53 v2f vert(appdata_full v) 54 { 55 v2f o; 56 o.pos = UnityObjectToClipPos(v.vertex); 57 o.uv = v.texcoord; 58 o.screenPos = ComputeScreenPos(o.pos); 59 o.ray = UnityObjectToViewPos(v.vertex).xyz * float3(-1, -1, 1); 60 return o; 61 } 62 63 half4 frag(v2f i) : SV_Target 64 { 65 i.ray = i.ray * (_ProjectionParams.z / i.ray.z); 66 float2 uv = i.screenPos.xy / i.screenPos.w; 67 68 float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy; 69 70 frostUV -= 0.5; 71 frostUV *= _Range; 72 frostUV += uv; 73 74 half4 frost = 4 * tex2D(_Frost, frostUV); 75 frost += tex2D(_Frost, frostUV + float2(_Blur, _Blur)); 76 frost += 2 * tex2D(_Frost, frostUV + float2(_Blur, 0)); 77 frost += tex2D(_Frost, frostUV + float2(_Blur, -_Blur)); 78 frost += 2 * tex2D(_Frost, frostUV + float2(0, _Blur)); 79 frost += 2 * tex2D(_Frost, frostUV + float2(0, -_Blur)); 80 frost += tex2D(_Frost, frostUV + float2(-_Blur, _Blur)); 81 frost += 2 * tex2D(_Frost, frostUV + float2(-_Blur, 0)); 82 frost += tex2D(_Frost, frostUV + float2(-_Blur, -_Blur)); 83 frost *= 0.0625; 84 85 half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw); 86 87 half alpha = _Color.a * diffuse.a; 88 89 return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1); 90 } 91 92 ENDCG 93 } 94 } 95 96 Fallback Off 97} 98
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
こんな感じでやってみましたが、ご参考になりますでしょうか?
ShaderLab
1Shader "Custom/Frost" 2{ 3 Properties 4 { 5 _Color("Color", Color) = (1, 1, 1, 1) 6 7 _MainTex("Diffuse", 2D) = "white" {} 8 _Noise("Noise", 2D) = "black" {} 9 10 _Range("Range", Float) = 0.025 11 // _Blur("Blur", Float) = 0.005 // Blurは廃止、サンプリング位置ずらしはテクセルサイズに基づいた形に変更 12 _Sigma("Sigma", Range(0.01, 8.0)) = 1.0 // σを追加 13 } 14 15 SubShader 16 { 17 // スカイボックスも含めてグラブするため、キューをTransparentに変更 18 Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" } 19 20 Cull Off 21 22 GrabPass{ "_Frost" } 23 24 CGINCLUDE 25 #include "UnityCG.cginc" 26 27 half4 _Color; 28 29 sampler2D _MainTex; 30 float4 _MainTex_ST; 31 32 sampler2D _Frost; 33 34 // グラブテクスチャのテクセルサイズを追加 35 float4 _Frost_TexelSize; 36 37 sampler2D _Noise; 38 float4 _Noise_ST; 39 40 half _Range; 41 // half _Blur; // _Blurは廃止 42 float _Sigma; // _Sigmaを追加 43 44 // 重み計算用関数 45 // ご質問者さんの重み関数 1/(2*π*σ)*exp(-(x^2+y^2)/(2*σ^2)) と同様ですが、係数は1に変更しました 46 // 係数を付けて正規化した重みで有限の範囲をたたみ込むと、重みの総和が1より小さくなってしまうため 47 // できあがったぼかし画像は、もとの画像より暗くなってしまうと思われます 48 // そこで重み関数には係数を付けず、たたみ込みの際に重みの総和を求めて、最後にたたみ込み結果を 49 // 重みの総和で割ることで明るさが維持されるようにしました 50 inline float getWeight(float2 xy) 51 { 52 return exp(-dot(xy, xy) / (2.0 * _Sigma * _Sigma)); 53 } 54 55 // カーネルサイズ計算用関数 56 // たたみ込み範囲の片側幅...カーネルの一辺の長さ2*n+1のnをいくつにするかですが、さしあたり 57 // 中心から最も遠い点(カーネルの角)における重みが十分小さくなる(0.0001を切る)大きさにしました 58 // σ=1でn=4となり、サンプリング回数は(2*4+1)^2=81回になります(多分...) 59 // σ=2で225回、σ=4で729回、σ=8で2601回...といった具合に、2乗のオーダーでサンプリング回数が 60 // 増大しますので、あんまりσを大きくしすぎるのは控えた方がいいでしょう 61 // ぼかしを縦方向と横方向の二段階に分けることで、サンプリング回数の増大を1乗のオーダーに 62 // 抑えるテクニックもありますので、負荷を軽減したい場合は採用を検討してみてもいいでしょう 63 inline int getKernelN() 64 { 65 return (int)ceil(_Sigma * sqrt(-log(0.0001))); 66 } 67 68 ENDCG 69 70 Pass 71 { 72 CGPROGRAM 73 #pragma target 3.0 74 #pragma vertex vert 75 #pragma fragment frag 76 77 struct v2f 78 { 79 float4 pos : SV_POSITION; 80 81 float3 uv : TEXCOORD; 82 float4 screenPos : TEXCOORD1; 83 float3 ray : TEXCOORD2; 84 }; 85 86 v2f vert(appdata_full v) 87 { 88 v2f o; 89 o.pos = UnityObjectToClipPos(v.vertex); 90 o.uv = v.texcoord; 91 o.screenPos = ComputeGrabScreenPos(o.pos); 92 o.ray = UnityObjectToViewPos(v.vertex).xyz * float3(-1, -1, 1); 93 return o; 94 } 95 96 half4 frag(v2f i) : SV_Target 97 { 98 // rayは使用していないようですが、一応残しておきました 99 i.ray = i.ray * (_ProjectionParams.z / i.ray.z); 100 101 float2 uv = i.screenPos.xy / i.screenPos.w; 102 103 float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy; 104 105 frostUV -= 0.5; 106 frostUV *= _Range; 107 frostUV += uv; 108 109 // 霜のついたガラスを表現するためサンプリング位置にノイズを加えているようですが、 110 // もし純粋にぼかし効果だけをかけたい場合は、uvをそのまま使うといいでしょう 111 // frostUV = uv; 112 113 int kernelN = getKernelN(); 114 float weightSum = 0.0; 115 float4 frost = 0.0; 116 117 // 注目しているピクセルを中心に、-kernelN ~ +kernelNの範囲をたたみ込む 118 for (int m = -kernelN; m <= kernelN; m++) 119 { 120 for (int n = -kernelN; n <= kernelN; n++) 121 { 122 float2 texelOffset = float2(n, m); 123 float weight = getWeight(texelOffset); 124 weightSum += weight; 125 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 126 } 127 } 128 129 // 最後に、重みの総和で割る 130 frost /= weightSum; 131 132 half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw); 133 134 half alpha = _Color.a * diffuse.a; 135 136 return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1); 137 } 138 139 ENDCG 140 } 141 } 142 143 Fallback Off 144}
コード中のコメントでもちょっと言及しましたが、このままではσを大きくすると、毎フレーム描画では重さが気になるかもしれません。
追加のレンダーテクスチャが必要になるためコードはちょっとややこしくなるでしょうが、wgld.org | WebGL: gaussian フィルタ |で紹介されているような縦横二段掛け方式とか、品質とパフォーマンスを両立する独自の技術|YEBIS 3で解説されているような低解像度化してぼかし処理を行うテクニックを用いれば、もっと軽量化できるかもしれませんね。
σ = 1、ノイズ効果なし
σ = 1、ノイズ効果あり
σ = 8、ノイズ効果なし
σ = 8、ノイズ効果あり
ループ展開版
非展開版と比べると、シェーダーのインポートに時間がかかるかもしれません。
KERNEL_N_MAX
をもっと小さくすると、対応可能なぼかし幅は小さくなってしまうものの、インポート速度・動作速度は改善するかと思います。
ShaderLab
1Shader "Custom/Frost" 2{ 3 Properties 4 { 5 _Color("Color", Color) = (1, 1, 1, 1) 6 7 _MainTex("Diffuse", 2D) = "white" {} 8 _Noise("Noise", 2D) = "black" {} 9 10 _Range("Range", Float) = 0.025 11 _Sigma("Sigma", Range(0.01, 8.0)) = 1.0 12 } 13 14 SubShader 15 { 16 Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" } 17 18 Cull Off 19 20 GrabPass{ "_Frost" } 21 22 CGINCLUDE 23 #include "UnityCG.cginc" 24 25 half4 _Color; 26 27 sampler2D _MainTex; 28 float4 _MainTex_ST; 29 30 sampler2D _Frost; 31 float4 _Frost_TexelSize; 32 33 sampler2D _Noise; 34 float4 _Noise_ST; 35 36 half _Range; 37 float _Sigma; 38 39 // 重み計算用関数 40 inline float getWeight(float2 xy) 41 { 42 return exp(-dot(xy, xy) / (2.0 * _Sigma * _Sigma)); 43 } 44 45 // カーネルサイズ計算用関数 46 inline int getKernelN() 47 { 48 return (int)ceil(_Sigma * sqrt(-log(0.0001))); 49 } 50 51 // 最大kernelN...(int)ceil(8 * sqrt(-ln(0.0001))) 52 #define KERNEL_N_MAX 25 53 54 ENDCG 55 56 Pass 57 { 58 CGPROGRAM 59 #pragma target 3.0 60 #pragma vertex vert 61 #pragma fragment frag 62 63 struct v2f 64 { 65 float4 pos : SV_POSITION; 66 67 float3 uv : TEXCOORD; 68 float4 screenPos : TEXCOORD1; 69 float3 ray : TEXCOORD2; 70 }; 71 72 v2f vert(appdata_full v) 73 { 74 v2f o; 75 o.pos = UnityObjectToClipPos(v.vertex); 76 o.uv = v.texcoord; 77 o.screenPos = ComputeGrabScreenPos(o.pos); 78 o.ray = UnityObjectToViewPos(v.vertex).xyz * float3(-1, -1, 1); 79 return o; 80 } 81 82 half4 frag(v2f i) : SV_Target 83 { 84 i.ray = i.ray * (_ProjectionParams.z / i.ray.z); 85 86 float2 uv = i.screenPos.xy / i.screenPos.w; 87 88 float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy; 89 90 frostUV -= 0.5; 91 frostUV *= _Range; 92 frostUV += uv; 93 // frostUV = uv; 94 95 int kernelN = getKernelN(); 96 97 // ループ展開版 98 float2 texelOffset = float2(0, 0); 99 float weight = getWeight(texelOffset); 100 float weightSum = weight; 101 float4 frost = weight * tex2D(_Frost, frostUV); 102 103 [unroll(KERNEL_N_MAX)] 104 for (int n = 0; n < kernelN; n++) 105 { 106 int x = n + 1; 107 108 texelOffset = float2(x, 0); 109 weight = getWeight(texelOffset); 110 weightSum += 2 * weight; 111 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 112 texelOffset = float2(-x, 0); 113 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 114 } 115 116 [unroll(KERNEL_N_MAX)] 117 for (int m = 0; m < kernelN; m++) 118 { 119 int y = m + 1; 120 121 texelOffset = float2(0, y); 122 weight = getWeight(texelOffset); 123 weightSum += 2 * weight; 124 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 125 texelOffset = float2(0, -y); 126 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 127 128 [unroll(KERNEL_N_MAX)] 129 for (int n = 0; n < kernelN; n++) 130 { 131 int x = n + 1; 132 133 texelOffset = float2(x, y); 134 weight = getWeight(texelOffset); 135 weightSum += 4 * weight; 136 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 137 texelOffset = float2(-x, y); 138 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 139 texelOffset = float2(x, -y); 140 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 141 texelOffset = float2(-x, -y); 142 frost += weight * tex2D(_Frost, frostUV + texelOffset * _Frost_TexelSize.xy); 143 } 144 } 145 146 frost /= weightSum; 147 148 half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw); 149 150 half alpha = _Color.a * diffuse.a; 151 152 return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1); 153 } 154 155 ENDCG 156 } 157 } 158 159 Fallback Off 160}
投稿2018/10/01 16:14
編集2018/10/02 21:55総合スコア10807
0
軽量化について
文字数の限界につき別回答ですがご容赦ください。
下記のようなアプローチを試してみました。
- シェーダーはぼかし処理を担当するFrostBlurと、ぼかし画像合成を担当するFrostCompositionに分ける。
FrostCompositionは旧Frostを改造、FrostBlurはイメージエフェクトシェーダーに近い作りになっている。
- カメラが不透明オブジェクト・背景の描画を終え、透明オブジェクトに取りかかる前の位置にコマンドバッファを挿入し、ここでぼかし処理を行う。
- 画面全体をレンダーテクスチャにコピーしぼかしをかける。
ぼかしカーネルは一次元9点で固定、重みもシェーダー内に直書きし、これを縦横二段掛けとして高速化を狙った。
- ぼかし幅が固定されている代わりに、ぼかし量の異なる多段階の画像を作っておくことで可変ぼかしに対応できるようにする。
ぼかし後の画像を縦横半分にし、再びぼかしをかける操作を繰り返し、解像度が半々に小さくなるぼかし画像群を得る。
- 曇りガラスオブジェクトの描画の際には、各ぼかしレベルから採った色をBlurに応じて混合する。ノイズテクスチャによるサンプリング位置ずらしは旧Frostと同様に行う。
コマンドバッファ挿入用スクリプト
これを曇りガラスオブジェクトにアタッチした上で、さらにマテリアルはFrostCompositionを使用する。
C#
1using System.Collections.Generic; 2using UnityEngine; 3using UnityEngine.Rendering; 4 5[ExecuteInEditMode] 6public class Frost : MonoBehaviour 7{ 8 private const CameraEvent FrostBlurEvent = CameraEvent.AfterImageEffectsOpaque; 9 private static Material blurMaterial; 10 private static readonly Dictionary<Camera, CameraInfo> Cameras = new Dictionary<Camera, CameraInfo>(); 11 private static readonly int GrabTex = Shader.PropertyToID("_GrabTex"); 12 private static readonly int[] FrostTex = 13 { 14 Shader.PropertyToID("_FrostTex0"), 15 Shader.PropertyToID("_FrostTex1"), 16 Shader.PropertyToID("_FrostTex2"), 17 Shader.PropertyToID("_FrostTex3") 18 }; 19 private static readonly int[] FrostTexT = 20 { 21 Shader.PropertyToID("_FrostTex0T"), 22 Shader.PropertyToID("_FrostTex1T"), 23 Shader.PropertyToID("_FrostTex2T"), 24 Shader.PropertyToID("_FrostTex3T") 25 }; 26 27 private static void AddCommandBufferIfNeeded(Camera cam) 28 { 29 if (Cameras.ContainsKey(cam)) 30 { 31 return; 32 } 33 34 var cb = new CommandBuffer 35 { 36 name = "FrostBlur" 37 }; 38 cam.AddCommandBuffer(FrostBlurEvent, cb); 39 Cameras.Add(cam, new CameraInfo(cb, 0, 0)); 40 } 41 42 private static void CleanCameras() 43 { 44 foreach (var pair in Cameras) 45 { 46 if (pair.Key != null) 47 { 48 pair.Key.RemoveCommandBuffer(FrostBlurEvent, pair.Value.CommandBuffer); 49 } 50 } 51 52 Cameras.Clear(); 53 } 54 55 private void OnEnable() 56 { 57 CleanCameras(); 58 } 59 60 private void OnDisable() 61 { 62 CleanCameras(); 63 } 64 65 private void OnWillRenderObject() 66 { 67 if (!this.enabled || !this.gameObject.activeInHierarchy) 68 { 69 CleanCameras(); 70 return; 71 } 72 var cam = Camera.current; 73 if (cam == null) 74 { 75 return; 76 } 77 AddCommandBufferIfNeeded(cam); 78 var camInfo = Cameras[cam]; 79 var width = cam.pixelWidth; 80 var height = cam.pixelHeight; 81 if ((width == camInfo.Width) && (height == camInfo.Height)) 82 { 83 return; 84 } 85 var cb = camInfo.CommandBuffer; 86 Cameras[cam] = new CameraInfo(cb, width, height); 87 var blur = blurMaterial; 88 if (blur == null) 89 { 90 blur = new Material(Shader.Find("Hidden/FrostBlur")); 91 blurMaterial = blur; 92 } 93 94 // 必要に応じ(画面サイズが変わった場合)コマンドバッファを再構成する 95 cb.Clear(); 96 // 一時テクスチャを取得 97 // _GrabTexは画面と同サイズ、_FrostTex0~4と_FrostTex0T~4Tは 98 // 0が画面と同サイズで、以降はサイズを半々に小さくする 99 cb.GetTemporaryRT(GrabTex, width, height, 0, FilterMode.Bilinear); 100 for (int i = 0, w = width, h = height; i < 4; i++, w >>= 1, h >>= 1) 101 { 102 cb.GetTemporaryRT(FrostTex[i], w, h, 0, FilterMode.Bilinear); 103 cb.GetTemporaryRT(FrostTexT[i], w, h, 0, FilterMode.Bilinear); 104 } 105 // 現在のレンダリング結果を_GrabTexにコピー 106 cb.Blit(BuiltinRenderTextureType.CurrentActive, GrabTex); 107 // _FrostTex0に_GrabTexをぼかしたものを格納 108 cb.Blit(GrabTex, FrostTex[0]); // そのままコピー 109 cb.Blit(FrostTex[0], FrostTexT[0], blur, 0); // 水平ぼかしコピー 110 cb.Blit(FrostTexT[0], FrostTex[0], blur, 1); // 垂直ぼかしコピー 111 // 段階的に縮小テクスチャへコピーしつつ、さらにぼかしをかけていく 112 for (var i = 1; i < 4; i++) 113 { 114 cb.Blit(FrostTex[i - 1], FrostTex[i]); // 縮小コピー 115 cb.Blit(FrostTex[i], FrostTexT[i], blur, 0); // 水平ぼかしコピー 116 cb.Blit(FrostTexT[i], FrostTex[i], blur, 1); // 垂直ぼかしコピー 117 } 118 // Blitなどのコストを無視すれば、ぼかしテクスチャ総面積は画面面積×1.33で 119 // サンプリング回数はピクセルあたり9×縦横2回で18回、さらに後でグラブテクスチャと 120 // 各ぼかしテクスチャから1回ずつサンプリングするので合計23回となり、 121 // 総サンプリング回数はおよそ画面面積×30.5回となるはず... 122 // 当初の方式でのσ=8クラスのぼかし(およそ描画面積×2600回のサンプリングが必要)と 123 // 比べると、同等のぼかし量ながらだいぶ処理コストを削減できたのではないでしょうか? 124 } 125 126 private struct CameraInfo 127 { 128 public readonly CommandBuffer CommandBuffer; 129 public readonly int Width; 130 public readonly int Height; 131 132 public CameraInfo(CommandBuffer cb, int w, int h) 133 { 134 this.CommandBuffer = cb; 135 this.Width = w; 136 this.Height = h; 137 } 138 } 139}
ぼかし処理担当シェーダー
上記Frostスクリプトが内部で使用する。
ShaderLab
1Shader "Hidden/FrostBlur" 2{ 3 Properties 4 { 5 _MainTex ("Texture", 2D) = "white" {} 6 } 7 SubShader 8 { 9 Cull Off ZWrite Off ZTest Always 10 11 CGINCLUDE 12 #include "UnityCG.cginc" 13 struct appdata 14 { 15 float4 vertex : POSITION; 16 float2 uv : TEXCOORD0; 17 }; 18 struct v2f 19 { 20 float2 uv : TEXCOORD0; 21 float4 vertex : SV_POSITION; 22 }; 23 v2f vert (appdata v) 24 { 25 v2f o; 26 o.vertex = UnityObjectToClipPos(v.vertex); 27 o.uv = v.uv; 28 return o; 29 } 30 sampler2D _MainTex; 31 float4 _MainTex_TexelSize; 32 // 二項係数に基づく重み 33 #define WS 256.0 34 #define W0 (70.0 / WS) 35 #define W1 (56.0 / WS) 36 #define W2 (28.0 / WS) 37 #define W3 (8.0 / WS) 38 #define W4 (1.0 / WS) 39 ENDCG 40 41 // パス0...水平ぼかし 42 Pass 43 { 44 CGPROGRAM 45 #pragma vertex vert 46 #pragma fragment frag 47 fixed4 frag (v2f i) : SV_Target 48 { 49 float2 scale = _MainTex_TexelSize.xy; 50 fixed4 col = W0 * tex2D(_MainTex, i.uv); 51 col += W1 * tex2D(_MainTex, i.uv + scale * float2(1, 0)); 52 col += W1 * tex2D(_MainTex, i.uv + scale * float2(-1, 0)); 53 col += W2 * tex2D(_MainTex, i.uv + scale * float2(2, 0)); 54 col += W2 * tex2D(_MainTex, i.uv + scale * float2(-2, 0)); 55 col += W3 * tex2D(_MainTex, i.uv + scale * float2(3, 0)); 56 col += W3 * tex2D(_MainTex, i.uv + scale * float2(-3, 0)); 57 col += W4 * tex2D(_MainTex, i.uv + scale * float2(4, 0)); 58 col += W4 * tex2D(_MainTex, i.uv + scale * float2(-4, 0)); 59 return col; 60 } 61 ENDCG 62 } 63 64 // パス1...垂直ぼかし 65 Pass 66 { 67 CGPROGRAM 68 #pragma vertex vert 69 #pragma fragment frag 70 fixed4 frag (v2f i) : SV_Target 71 { 72 float2 scale = _MainTex_TexelSize.xy; 73 fixed4 col = W0 * tex2D(_MainTex, i.uv); 74 col += W1 * tex2D(_MainTex, i.uv + scale * float2(0, 1)); 75 col += W1 * tex2D(_MainTex, i.uv + scale * float2(0, -1)); 76 col += W2 * tex2D(_MainTex, i.uv + scale * float2(0, 2)); 77 col += W2 * tex2D(_MainTex, i.uv + scale * float2(0, -2)); 78 col += W3 * tex2D(_MainTex, i.uv + scale * float2(0, 3)); 79 col += W3 * tex2D(_MainTex, i.uv + scale * float2(0, -3)); 80 col += W4 * tex2D(_MainTex, i.uv + scale * float2(0, 4)); 81 col += W4 * tex2D(_MainTex, i.uv + scale * float2(0, -4)); 82 return col; 83 } 84 ENDCG 85 } 86 } 87}
ぼかしテクスチャ合成担当シェーダー
旧Frostと同様に、曇りガラスオブジェクトにはこれを使ったマテリアルをセットする。
ShaderLab
1Shader "Custom/FrostComposition" 2{ 3 Properties 4 { 5 _Color("Color", Color) = (1, 1, 1, 1) 6 7 _MainTex("Diffuse", 2D) = "white" {} 8 _Noise("Noise", 2D) = "black" {} 9 10 _Range("Range", Float) = 0.025 11 _Blur("Blur", Range(0.0, 1.0)) = 0.5 12 } 13 14 SubShader 15 { 16 Tags{ "Queue" = "Transparent" "RenderType" = "Transparent" } 17 18 Cull Off 19 20 CGINCLUDE 21 #include "UnityCG.cginc" 22 half4 _Color; 23 sampler2D _MainTex; 24 float4 _MainTex_ST; 25 sampler2D _GrabTex; 26 sampler2D _FrostTex0; 27 sampler2D _FrostTex1; 28 sampler2D _FrostTex2; 29 sampler2D _FrostTex3; 30 sampler2D _Noise; 31 float4 _Noise_ST; 32 half _Range; 33 float _Blur; 34 ENDCG 35 36 Pass 37 { 38 CGPROGRAM 39 #pragma target 3.0 40 #pragma vertex vert 41 #pragma fragment frag 42 43 struct v2f 44 { 45 float4 pos : SV_POSITION; 46 float3 uv : TEXCOORD; 47 float4 screenPos : TEXCOORD1; 48 }; 49 50 v2f vert(appdata_full v) 51 { 52 v2f o; 53 o.pos = UnityObjectToClipPos(v.vertex); 54 o.uv = v.texcoord; 55 o.screenPos = ComputeGrabScreenPos(o.pos); 56 return o; 57 } 58 59 half4 frag(v2f i) : SV_Target 60 { 61 float2 uv = i.screenPos.xy / i.screenPos.w; 62 float2 frostUV = tex2D(_Noise, i.uv * _Noise_ST.xy + _Noise_ST.zw).xy; 63 64 frostUV -= 0.5; 65 frostUV *= _Range; 66 frostUV += uv; 67 // frostUV = uv; 68 69 float t = pow(_Blur, 0.5) * 4; 70 float4 frost = smoothstep(1, 0, t) * tex2D(_GrabTex, frostUV); 71 frost += smoothstep(1, 0, abs(t - 1)) * tex2D(_FrostTex0, frostUV); 72 frost += smoothstep(1, 0, abs(t - 2)) * tex2D(_FrostTex1, frostUV); 73 frost += smoothstep(1, 0, abs(t - 3)) * tex2D(_FrostTex2, frostUV); 74 frost += smoothstep(1, 0, 4 - t) * tex2D(_FrostTex3, frostUV); 75 76 half4 diffuse = tex2D(_MainTex, i.uv * _MainTex_ST.xy + _MainTex_ST.zw); 77 78 half alpha = _Color.a * diffuse.a; 79 80 return half4(frost.xyz + (diffuse.rgb * _Color.rgb * alpha), 1); 81 } 82 ENDCG 83 } 84 } 85 Fallback Off 86}
ぼかし画像を作る過程は下図のようになります。GIFでも階調を表現できるよう、白黒で撮影しました。
FrostCompositionでは、Blurに応じて下図のような比率で各ぼかし画像を混合しました。
旧Frostと比較するとプロファイラーで見た際のレンダリング負荷(黄緑色領域)が削減されましたが、新Frostは低解像度のぼかし画像を引き伸ばしたものですので、よく見るとピクセル由来のブロック感が出ています。
σに応じて広い範囲をきっちりサンプリングしていた旧Frostに対し、新Frostは複数のぼかし画像を混ぜているだけですので、画質面では劣ってしまいます。一応ガウス分布っぽい広がりのぼかしにはしたつもりですが、「ガウシアンブラー」だと言い張るにはちょっと怪しいかもしれません...
ノイズ効果と組み合わせれば、粗が目立たなくなってマシなように思います。
投稿2018/10/03 22:17
編集2018/10/10 13:29総合スコア10807
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/10 20:23 編集
2018/10/11 03:59
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/02 04:57
2018/10/02 06:09
2018/10/02 06:28
2018/10/02 06:55
2018/10/02 07:09
2018/10/02 07:31
2018/10/02 22:11 編集
2018/10/03 04:48
2018/10/04 21:10