├── 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 | --------------------------------------------------------------------------------