├── CameraDualFilteringBlur.cs
├── CameraDualFilteringBlur.cs.meta
├── DynamicDualFilteringBlur.cs
├── DynamicDualFilteringBlur.cs.meta
├── LICENSE
├── LICENSE.meta
├── README.md
├── README.md.meta
├── Shader.meta
└── Shader
├── DualFiltering.shader
└── DualFiltering.shader.meta
/CameraDualFilteringBlur.cs:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2021 Akihiro Noguchi
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | using UnityEngine;
24 |
25 | [ExecuteAlways, ImageEffectAllowedInSceneView]
26 | public class CameraDualFilteringBlur : MonoBehaviour
27 | {
28 | [HideInInspector, SerializeField] private Shader blurShader;
29 |
30 | [SerializeField, Range(0f, 16.0f)] private float blurSize = 1;
31 |
32 | public float BlueSize
33 | {
34 | get => blurSize;
35 | set => blurSize = Mathf.Max(0.0f, value);
36 | }
37 |
38 | [SerializeField, Range(1, 4)] private int iterations = 2;
39 |
40 | [SerializeField] private int referenceHeight = 1024;
41 |
42 | private readonly DynamicDualFilteringBlur _filteringBlur = new DynamicDualFilteringBlur();
43 |
44 | private Material _blurMat;
45 |
46 | private void Awake()
47 | {
48 | _filteringBlur.Configure(iterations, referenceHeight);
49 | }
50 |
51 | private void OnPostRender()
52 | {
53 | if (_blurMat == null)
54 | {
55 | _blurMat = new Material(blurShader)
56 | {
57 | hideFlags = HideFlags.HideAndDontSave
58 | };
59 | }
60 |
61 | var src = RenderTexture.active;
62 | var dst = RenderTexture.active;
63 | #if UNITY_EDITOR
64 | // For quick parameter adjustments
65 | _filteringBlur.Configure(iterations, referenceHeight);
66 | #endif
67 | _filteringBlur.Execute(_blurMat, src, dst, blurSize);
68 | }
69 |
70 | private void OnDestroy()
71 | {
72 | if (_blurMat == null) return;
73 | #if UNITY_EDITOR
74 | if (!Application.isPlaying)
75 | {
76 | DestroyImmediate(_blurMat);
77 | return;
78 | }
79 | #endif
80 | Destroy(_blurMat);
81 | }
82 | }
--------------------------------------------------------------------------------
/CameraDualFilteringBlur.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b527d077aa6ad864f814b948c6639120
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences:
7 | - blurShader: {fileID: 4800000, guid: 5feaab583ed81674e8d8b7f94bb7d37c, type: 3}
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/DynamicDualFilteringBlur.cs:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2021 Akihiro Noguchi
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | using System.Collections.Generic;
24 | using UnityEngine;
25 |
26 | ///
27 | /// Fast blur filter with linear blur radius scale parameter.
28 | ///
29 | /// This implementation uses Dual Filtering (or Dual Kawase Blur) by Marius Bjørge.
30 | /// https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_notes.pdf
31 | ///
32 | /// However, it is not possible to linearly scale blur radius when using this technique (as far as I know).
33 | /// Therefore, this implementation fixes the number of Dual Filtering passes, and resizes the input texture to
34 | /// simulate blur radius scale. For example, Dual Filtering input texture is halved to double the blur radius.
35 | ///
36 | /// Unfortunately, there is still a problem with simulating blur radius scales in a range of (0.0, 1.0).
37 | /// This implementation approximates such range by alpha blending the blur result, which looks good enough for a small
38 | /// iteration configuration (such as 2).
39 | ///
40 | public class DynamicDualFilteringBlur
41 | {
42 | private readonly struct Resolution
43 | {
44 | public readonly int Width;
45 | public readonly int Height;
46 |
47 | public float XTexelSize => Width <= 0 ? 0.0f : 1.0f / Width;
48 | public float YTexelSize => Height <= 0 ? 0.0f : 1.0f / Height;
49 |
50 | public Resolution(float width, float height)
51 | {
52 | Width = Mathf.CeilToInt(width);
53 | Height = Mathf.CeilToInt(height);
54 | }
55 | }
56 |
57 | private int _fixedBlurScale;
58 | private float _referenceHeightRcp;
59 |
60 | private readonly List _baseDownsamplePasses = new List();
61 | private readonly List _blurDownsamplePasses = new List();
62 |
63 | private static readonly int OpacityPropertyID = Shader.PropertyToID("_Opacity");
64 | private static readonly int DstTexelSizePropertyID = Shader.PropertyToID("_DstTexelSize");
65 |
66 | private enum BlurShaderPass
67 | {
68 | DownSample = 0,
69 | UpSample = 1,
70 | UpSampleOpacity = 2
71 | }
72 |
73 | private const int DefaultReferenceHeight = 1024;
74 | private const int DefaultIterations = 2;
75 |
76 | private RenderTexture _tmpTex = null;
77 |
78 | public DynamicDualFilteringBlur(int iterations = DefaultIterations, int referenceHeight = DefaultReferenceHeight)
79 | {
80 | Configure(iterations, referenceHeight);
81 | }
82 |
83 | public void Configure(int iterations = DefaultIterations, int referenceHeight = DefaultReferenceHeight)
84 | {
85 | // Fallbacks
86 | if (referenceHeight <= 0)
87 | {
88 | referenceHeight = DefaultReferenceHeight;
89 | }
90 |
91 | if (iterations <= 0)
92 | {
93 | iterations = DefaultReferenceHeight;
94 | }
95 |
96 | _referenceHeightRcp = 1.0f / referenceHeight;
97 | _fixedBlurScale = 1;
98 | for (; iterations > 0; --iterations)
99 | {
100 | _fixedBlurScale *= 2;
101 | }
102 | }
103 |
104 | private void PrepareDownsamplePasses(int width, int height, float scale, ICollection passes)
105 | {
106 | passes.Clear();
107 | var currentWidth = (float)width;
108 | var currentHeight = (float)height;
109 |
110 | while (scale > 1.0f)
111 | {
112 | // Halve the texture size at maximum
113 | var currentScale = Mathf.Min(scale, 2.0f);
114 | var currentRatio = 1.0f / currentScale;
115 | currentWidth *= currentRatio;
116 | currentHeight *= currentRatio;
117 | passes.Add(new Resolution(currentWidth, currentHeight));
118 | scale *= currentRatio;
119 | }
120 | }
121 |
122 | private void RenderSwap(ref RenderTexture src, Resolution targetRes, Material mat, int pass = 0)
123 | {
124 | var next = RenderTexture.GetTemporary(targetRes.Width, targetRes.Height, 0);
125 | if (mat == null)
126 | {
127 | Graphics.Blit(src, next);
128 | }
129 | else
130 | {
131 | Graphics.Blit(src, next, mat, pass);
132 | }
133 |
134 | RenderTexture.ReleaseTemporary(_tmpTex);
135 | _tmpTex = next;
136 | src = next;
137 | }
138 |
139 | private void DualFilteringBlur(Material blurMat, RenderTexture src, float opacity)
140 | {
141 | PrepareDownsamplePasses(src.width, src.height, _fixedBlurScale, _blurDownsamplePasses);
142 |
143 | // Downsample
144 | for (var i = 0; i < _blurDownsamplePasses.Count; ++i)
145 | {
146 | var targetRes = _blurDownsamplePasses[i];
147 | blurMat.SetVector(DstTexelSizePropertyID,
148 | new Vector4(targetRes.XTexelSize, targetRes.YTexelSize, 0, 0));
149 | RenderSwap(ref src, targetRes, blurMat, (int)BlurShaderPass.DownSample);
150 | }
151 |
152 | // Upsample
153 | for (var i = _blurDownsamplePasses.Count - 2; i >= 0; --i)
154 | {
155 | var targetRes = _blurDownsamplePasses[i];
156 | blurMat.SetVector(DstTexelSizePropertyID,
157 | new Vector4(targetRes.XTexelSize, targetRes.YTexelSize, 0, 0));
158 | RenderSwap(ref src, targetRes, blurMat, (int)BlurShaderPass.UpSample);
159 | }
160 |
161 | PrepareBlurFinalPassTexture();
162 | var dst = _currentBlurFinalPassTexture;
163 |
164 | // Final blur pass
165 | blurMat.SetVector(DstTexelSizePropertyID, new Vector4(1f / dst.width, 1f / dst.height, 0, 0));
166 | if (opacity < 1.0f)
167 | {
168 | blurMat.SetFloat(OpacityPropertyID, opacity);
169 | Graphics.Blit(src, dst, blurMat, (int)BlurShaderPass.UpSampleOpacity);
170 | }
171 | else
172 | {
173 | Graphics.Blit(src, dst, blurMat, (int)BlurShaderPass.UpSample);
174 | }
175 |
176 | RenderTexture.ReleaseTemporary(_tmpTex);
177 | _tmpTex = null;
178 | }
179 |
180 | private RenderTexture _currentBlurFinalPassTexture;
181 |
182 | private void PrepareBlurFinalPassTexture()
183 | {
184 | if (_currentBlurFinalPassTexture != null) return;
185 |
186 | var targetSize = _baseDownsamplePasses[_baseDownsamplePasses.Count - 1];
187 | _currentBlurFinalPassTexture = RenderTexture.GetTemporary(targetSize.Width, targetSize.Height, 0);
188 | }
189 |
190 | public void Execute(Material blurMat, RenderTexture src, RenderTexture dst, float blurSize)
191 | {
192 | if (blurMat == null) return;
193 |
194 | var currentSize = Mathf.Max(0.0f, blurSize);
195 | if (currentSize == 0.0f) return;
196 |
197 | currentSize *= src.height * _referenceHeightRcp;
198 |
199 | if (currentSize <= 1.0f)
200 | {
201 | // Size less than 1 is handled by fading the result of fixed Dual Filtering
202 | _currentBlurFinalPassTexture = dst;
203 | DualFilteringBlur(blurMat, src, currentSize);
204 | _currentBlurFinalPassTexture = null;
205 | }
206 | else
207 | {
208 | // Downsample to scale, apply, upscale
209 | PrepareDownsamplePasses(src.width, src.height, currentSize, _baseDownsamplePasses);
210 |
211 | // Downsample
212 | for (var i = 0; i < _baseDownsamplePasses.Count; ++i)
213 | {
214 | RenderSwap(ref src, _baseDownsamplePasses[i], null);
215 | }
216 |
217 | // We don't allocate the final pass texture just yet.
218 | // The source texture will be released after the first pass in the blur process, making it available
219 | // to be reused for the final pass.
220 | // It is important to delay temporary render texture allocation until we really need it.
221 | _currentBlurFinalPassTexture = null;
222 | DualFilteringBlur(blurMat, src, 1);
223 | // The final pass texture was automatically created by the blur process
224 | src = _tmpTex = _currentBlurFinalPassTexture;
225 | _currentBlurFinalPassTexture = null;
226 |
227 | // Upscale
228 | for (var i = _baseDownsamplePasses.Count - 2; i >= 0; --i)
229 | {
230 | RenderSwap(ref src, _baseDownsamplePasses[i], null);
231 | }
232 |
233 | Graphics.Blit(src, dst);
234 | RenderTexture.ReleaseTemporary(_tmpTex);
235 | _tmpTex = null;
236 | }
237 | }
238 | }
--------------------------------------------------------------------------------
/DynamicDualFilteringBlur.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: afed36930ffe462ba0c0bb9511dd7706
3 | timeCreated: 1635677556
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Akihiro Noguchi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: d616cdc3972184d408afe773e12700df
3 | DefaultImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dynamic Dual Filtering
2 | This is a Unity implementation of Dual Filtering Blur (or Dual Kawase Filtering Blur) by Marius Bjørge with approximated linear radius scale parameters.
3 |
4 | The Dual Filtering technique is very fast and memory bandwidth friendly for many scenarios where blurring is needed, but it doesn't work very well when gradually blurring an image. However, this is a common scenario for any game, which makes this technique less versatile.
5 |
6 | This is my attempt to approximate linear radius scale adjustments by mixing existing blur techniques.
7 |
8 | The blur result is reasonably good in terms of artifacts even when the input image and the blur radius are animating, as you can see in the video.
9 |
10 |
11 |
12 |
13 | Static Image
14 |
15 |
16 |
17 | Usage
18 | ---
19 | For Unity built-in pipeline, attach the `CameraDualFilteringBlur` component to a camera, and configure the parameters.
20 |
21 | URP support is a work in progress.
22 |
23 | License
24 | ---
25 | ```
26 | MIT License
27 |
28 | Copyright (c) 2021 Akihiro Noguchi
29 |
30 | Permission is hereby granted, free of charge, to any person obtaining a copy
31 | of this software and associated documentation files (the "Software"), to deal
32 | in the Software without restriction, including without limitation the rights
33 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 | copies of the Software, and to permit persons to whom the Software is
35 | furnished to do so, subject to the following conditions:
36 |
37 | The above copyright notice and this permission notice shall be included in all
38 | copies or substantial portions of the Software.
39 |
40 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
43 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
46 | SOFTWARE.
47 | ```
48 |
--------------------------------------------------------------------------------
/README.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e240d89c27e71d747a72ba9e44f48386
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/Shader.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 223e60b6684d7354eaf95c496de3bb78
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/Shader/DualFiltering.shader:
--------------------------------------------------------------------------------
1 | // MIT License
2 | //
3 | // Copyright (c) 2021 Akihiro Noguchi
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | Shader "ImageEffect/DualFilter"
24 | {
25 | Properties
26 | {
27 | _MainTex ("Texture", 2D) = "white" {}
28 | }
29 |
30 | SubShader
31 | {
32 | // No culling or depth
33 | Cull Off ZWrite Off ZTest Always
34 |
35 | HLSLINCLUDE
36 | #include "UnityCG.cginc"
37 |
38 | sampler2D _MainTex;
39 | float4 _DstTexelSize;
40 | half _Opacity;
41 |
42 | struct appdata
43 | {
44 | float4 vertex : POSITION;
45 | float2 uv : TEXCOORD0;
46 | };
47 |
48 | struct downV2f
49 | {
50 | float4 vertex : SV_POSITION;
51 | float2 uv0 : TEXCOORD0;
52 | float2 uv1 : TEXCOORD1;
53 | float2 uv2 : TEXCOORD2;
54 | float2 uv3 : TEXCOORD3;
55 | float2 uv4 : TEXCOORD4;
56 | };
57 |
58 | downV2f downVert(appdata v)
59 | {
60 | const float2 halfTexel = _DstTexelSize.xy * 0.5;
61 | downV2f o;
62 | o.vertex = UnityObjectToClipPos(v.vertex);
63 | o.uv0 = v.uv;
64 | o.uv1 = v.uv + float2(-halfTexel.x, -halfTexel.y);
65 | o.uv2 = v.uv + float2(halfTexel.x, -halfTexel.y);
66 | o.uv3 = v.uv + float2(-halfTexel.x, halfTexel.y);
67 | o.uv4 = v.uv + float2(halfTexel.x, halfTexel.y);
68 | return o;
69 | }
70 |
71 | half4 downFrag(downV2f i) : SV_Target
72 | {
73 | return tex2D(_MainTex, i.uv0) * 0.5h +
74 | tex2D(_MainTex, i.uv1) * (1.0h / 8.0h) +
75 | tex2D(_MainTex, i.uv2) * (1.0h / 8.0h) +
76 | tex2D(_MainTex, i.uv3) * (1.0h / 8.0h) +
77 | tex2D(_MainTex, i.uv4) * (1.0h / 8.0h);
78 | }
79 |
80 | struct upV2f
81 | {
82 | float4 vertex : SV_POSITION;
83 | float2 uv0 : TEXCOORD0;
84 | float2 uv1 : TEXCOORD1;
85 | float2 uv2 : TEXCOORD2;
86 | float2 uv3 : TEXCOORD3;
87 | float2 uv4 : TEXCOORD4;
88 | float2 uv5 : TEXCOORD5;
89 | float2 uv6 : TEXCOORD6;
90 | float2 uv7 : TEXCOORD7;
91 | };
92 |
93 | upV2f upVert(appdata v)
94 | {
95 | const float2 halfTexel = _DstTexelSize.xy * 0.5;
96 | upV2f o;
97 | o.vertex = UnityObjectToClipPos(v.vertex);
98 | o.uv0 = v.uv + float2(-halfTexel.x, -halfTexel.y) * 2.0f;
99 | o.uv1 = v.uv + float2(halfTexel.x, -halfTexel.y) * 2.0f;
100 | o.uv2 = v.uv + float2(-halfTexel.x, halfTexel.y) * 2.0f;
101 | o.uv3 = v.uv + float2(halfTexel.x, halfTexel.y) * 2.0f;
102 | o.uv4 = v.uv + float2(0, -halfTexel.y) * 4.0f;
103 | o.uv5 = v.uv + float2(-halfTexel.x, 0) * 4.0f;
104 | o.uv6 = v.uv + float2(halfTexel.x, 0) * 4.0f;
105 | o.uv7 = v.uv + float2(0, halfTexel.y) * 4.0f;
106 | return o;
107 | }
108 |
109 | half4 upSample(upV2f i)
110 | {
111 | return tex2D(_MainTex, i.uv0) * (1.0h / 6.0h) +
112 | tex2D(_MainTex, i.uv1) * (1.0h / 6.0h) +
113 | tex2D(_MainTex, i.uv2) * (1.0h / 6.0h) +
114 | tex2D(_MainTex, i.uv3) * (1.0h / 6.0h) +
115 | tex2D(_MainTex, i.uv4) * (1.0h / 12.0h) +
116 | tex2D(_MainTex, i.uv5) * (1.0h / 12.0h) +
117 | tex2D(_MainTex, i.uv6) * (1.0h / 12.0h) +
118 | tex2D(_MainTex, i.uv7) * (1.0h / 12.0h);
119 | }
120 |
121 | half4 upFrag(upV2f i) : SV_Target
122 | {
123 | return upSample(i);
124 | }
125 |
126 | half4 upFragOpacity(upV2f i) : SV_Target
127 | {
128 | half4 res = upSample(i);
129 | res.a = _Opacity;
130 | return res;
131 | }
132 | ENDHLSL
133 |
134 | Pass
135 | {
136 | Name "Downsample"
137 |
138 | HLSLPROGRAM
139 | #pragma vertex downVert
140 | #pragma fragment downFrag
141 | ENDHLSL
142 | }
143 |
144 | Pass
145 | {
146 | Name "Upsample"
147 |
148 | HLSLPROGRAM
149 | #pragma vertex upVert
150 | #pragma fragment upFrag
151 | ENDHLSL
152 | }
153 |
154 | Pass
155 | {
156 | Name "Upsample with Opacity"
157 | Blend SrcAlpha OneMinusSrcAlpha
158 |
159 | HLSLPROGRAM
160 | #pragma vertex upVert
161 | #pragma fragment upFragOpacity
162 | ENDHLSL
163 | }
164 | }
165 | }
--------------------------------------------------------------------------------
/Shader/DualFiltering.shader.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5feaab583ed81674e8d8b7f94bb7d37c
3 | ShaderImporter:
4 | externalObjects: {}
5 | defaultTextures: []
6 | nonModifiableTextures: []
7 | preprocessorOverride: 0
8 | userData:
9 | assetBundleName:
10 | assetBundleVariant:
11 |
--------------------------------------------------------------------------------