├── .gitattributes ├── .gitignore ├── LICENSE ├── LICENSE.meta ├── README.md ├── README.md.meta ├── Resources.meta ├── Resources ├── Anamorphic.shader ├── Anamorphic.shader.meta ├── RadialWarpAndGhosts.shader └── RadialWarpAndGhosts.shader.meta ├── Runtime.meta ├── Runtime ├── Anamorphic.cs ├── Anamorphic.cs.meta ├── RadialWarpAndGhosts.cs ├── RadialWarpAndGhosts.cs.meta ├── Secretlab.HDRPLensFlares.asmdef ├── Secretlab.HDRPLensFlares.asmdef.meta ├── Utilities.meta └── Utilities │ ├── Matrix2x2.cs │ └── Matrix2x2.cs.meta ├── package.json └── package.json.meta /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | 13 | # Never ignore Asset meta data 14 | !/[Aa]ssets/**/*.meta 15 | 16 | # Uncomment this line if you wish to ignore the asset store tools plugin 17 | # /[Aa]ssets/AssetStoreTools* 18 | 19 | # Autogenerated Jetbrains Rider plugin 20 | [Aa]ssets/Plugins/Editor/JetBrains* 21 | 22 | # Visual Studio cache directory 23 | .vs/ 24 | 25 | # Gradle cache directory 26 | .gradle/ 27 | 28 | # Autogenerated VS/MD/Consulo solution and project files 29 | ExportedObj/ 30 | .consulo/ 31 | *.csproj 32 | *.unityproj 33 | *.sln 34 | *.suo 35 | *.tmp 36 | *.user 37 | *.userprefs 38 | *.pidb 39 | *.booproj 40 | *.svd 41 | *.pdb 42 | *.mdb 43 | *.opendb 44 | *.VC.db 45 | 46 | # Unity3D generated meta files 47 | *.pidb.meta 48 | *.pdb.meta 49 | *.mdb.meta 50 | 51 | # Unity3D generated file on crash reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | # Crashlytics generated file 59 | crashlytics-build.properties 60 | 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 H. Gregor Molter 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: 58280302b26ea8043a3092fc82fd6d12 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity-HDRP-LensFlares 2 | A collection of different (post-processing) lens flare effects for Unity's HD render pipeline using post-processing stack v3. 3 | 4 | ## Radial Warp and Ghosts 5 | This post processing lens flare effect draws radial warps and ghosts. Based on the great ['Aerobox Lens Flare'](https://github.com/modanhan/Unity-Lens-Flare-2019) shader originally released under the MIT license. 6 | 7 | ![HDRP Lens Flare Effect with radial warp and ghosts](https://cyberdeck.github.io/repositories/unity-hdrp-lensflares/hdrp-lensflare.gif) 8 | 9 | [MP4 1920x1080 video file](https://github.com/CyberDeck/cyberdeck.github.io/blob/master/repositories/unity-hdrp-lensflares/hdrp-lensflare.mp4?raw=true) 10 | 11 | ## Anamorphic (Light Streak) 12 | This post processing lens flare effect draws an anamorphic lens flare effect (light streak). Based on the great ['Kino Light Streak']( https://github.com/keijiro/Kino) shader originally released under the Unlicense license. 13 | In this version you may change the screen orientation of the light streak. Additionally, you may add some fade out effect on the light streak. 14 | 15 | ![Anamorphic Lens Flare Effect](https://cyberdeck.github.io/repositories/unity-hdrp-lensflares/anamorphic-lensflare.gif) 16 | 17 | [MP4 1920x1080 video file](https://github.com/CyberDeck/cyberdeck.github.io/blob/master/repositories/unity-hdrp-lensflares/anamorphic-lensflare.mp4?raw=true) -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e384b9a74d0be294cb6feea9689bd5d0 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3a0101baf1422dd4f80f1d1a248ab59e 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Resources/Anamorphic.shader: -------------------------------------------------------------------------------- 1 | // Shader based on the Kino Streak shader from https://github.com/keijiro/Kino 2 | // Original Shader by Keijiro Takahashi 3 | // Modified Shader Variant Copyright(c) 2020 H. Gregor Molter 4 | 5 | 6 | Shader "Hidden/Shader/LensFlares/Anamorphic" 7 | { 8 | HLSLINCLUDE 9 | 10 | //#define __DEBUG_SHADER 11 | #pragma target 4.5 12 | #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch 13 | 14 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" 15 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" 16 | #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" 17 | 18 | // Helper to create a 2d rotation matrix 19 | float2x2 RotateMatrix(float angle) { 20 | float sin, cos; 21 | sincos(radians(angle), sin, cos); // compute the sin and cosine 22 | float2x2 rotMat = float2x2(cos, -sin, sin, cos); 23 | const float2x2 halfMat = float2x2(float2(0.5, 0.5), float2(0.5, 0.5)); 24 | const float2x2 doubleMat = float2x2(float2(2, 2), float2(2, 2)); 25 | const float2x2 oneMat = float2x2(float2(1, 1), float2(1, 1)); 26 | rotMat *= halfMat; 27 | rotMat += halfMat; 28 | rotMat *= doubleMat; 29 | rotMat -= oneMat; 30 | return rotMat; 31 | } 32 | 33 | // Properties of the Anamorphic Shader 34 | TEXTURE2D_X(_SourceTexture); 35 | TEXTURE2D(_InputTexture); 36 | TEXTURE2D(_OtherTexture); 37 | 38 | float4 _InputTexture_TexelSize; 39 | 40 | float _Intensity; 41 | float _Threshold; // Threshold to draw the anamorphic streaks 42 | float4 _Color; // Color of the streak 43 | float _Angle; 44 | // if _Angle = 0 it is assumed that the base texture sizes are equal the screen sizes. 45 | // If a rotated anamorphic streak shall be shown, it is necessary that the internal textures are bigger (to cope with the rotation). 46 | // By doing so, we have to adjust (scale) the prefilter transformation (copy of the screen pixels to the internal textures) and at the end at the final composition. 47 | float4 _AngleTextureScale; 48 | float _Stretch; 49 | float _Fade; 50 | 51 | struct Attributes 52 | { 53 | uint vertexID : SV_VertexID; 54 | UNITY_VERTEX_INPUT_INSTANCE_ID 55 | }; 56 | 57 | struct Varyings 58 | { 59 | float4 positionCS : SV_POSITION; 60 | float2 texcoord : TEXCOORD0; 61 | UNITY_VERTEX_OUTPUT_STEREO 62 | }; 63 | 64 | Varyings Vert(Attributes input) 65 | { 66 | Varyings output; 67 | UNITY_SETUP_INSTANCE_ID(input); 68 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); 69 | output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID); 70 | output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID); 71 | return output; 72 | } 73 | 74 | #ifdef __DEBUG_SHADER 75 | 76 | // Some helpers to draw lines and rectangles for debugging purpose 77 | float drawLine(float2 uv, float2 p1, float2 p2) { 78 | const float Thickness = 0.002; 79 | float a = abs(distance(p1, uv)); 80 | float b = abs(distance(p2, uv)); 81 | float c = abs(distance(p1, p2)); 82 | 83 | if (a >= c || b >= c) return 0.0; 84 | 85 | float p = (a + b + c) * 0.5; 86 | 87 | // median to (p1, p2) vector 88 | float h = 2 / c * sqrt(p * (p - a) * (p - b) * (p - c)); 89 | 90 | return lerp(1.0, 0.0, smoothstep(0.5 * Thickness, 1.5 * Thickness, h)); 91 | } 92 | 93 | float drawRect(float2 uv, float4 dim) { 94 | return max(max(max( 95 | drawLine(uv, dim.xy, dim.zy), 96 | drawLine(uv, dim.zy, dim.zw)), 97 | drawLine(uv, dim.zw, dim.xw)), 98 | drawLine(uv, dim.xw, dim.xy)); 99 | } 100 | #endif // __DEBUG_SHADER 101 | 102 | // Initial Prefilter for the Anamorphic shader 103 | float4 FragPrefilter(Varyings input) : SV_Target 104 | { 105 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 106 | float2x2 rotMat = RotateMatrix(-_Angle); 107 | float2 uv = input.texcoord; 108 | 109 | #ifndef __DEBUG_SHADER 110 | // Normal Shader 111 | 112 | // Scale and rotate UV 113 | uv = (mul(uv - float2(0.5, 0.5), rotMat) * _AngleTextureScale.xy) + float2(0.5, 0.5); 114 | 115 | // Load the textures (start to comment from here for test pattern to be viewed in the frame debugger) 116 | uint2 ss = uv * _ScreenSize.xy - float2(0, 0.5); 117 | float3 c0 = LOAD_TEXTURE2D_X(_SourceTexture, ss).rgb; 118 | float3 c1 = LOAD_TEXTURE2D_X(_SourceTexture, ss+uint2(0,1)).rgb; 119 | float3 c = (c0 + c1) / 2; 120 | 121 | float br = max(c.r, max(c.g, c.b)); 122 | c *= max(0, br - _Threshold) / max(br, 1e-5); 123 | return float4(c, 1); 124 | #else // Debug Shader 125 | float2 scaled_uv = ((uv - float2(0.5, 0.5)) * _AngleTextureScale.xy) + float2(0.5, 0.5); 126 | float2 rot_scaled_uv = (mul(uv - float2(0.5, 0.5), rotMat) * _AngleTextureScale.xy) + float2(0.5, 0.5); 127 | float2 rot_uv = (mul(uv - float2(0.5, 0.5), rotMat)) + float2(0.5, 0.5); 128 | 129 | const float3 red = float3(1, 0, 0); 130 | const float3 green = float3(0, 1, 0); 131 | const float3 blue = float3(0, 0, 1); 132 | const float3 white = float3(1, 1, 1); 133 | const float3 yellow = float3(1, 1, 0); 134 | const float4 scaled_rect = float4(0.5 - (0.5 / _AngleTextureScale.x), 0.5 - (0.5 / _AngleTextureScale.y), 0.5 + (0.5 / _AngleTextureScale.x), 0.5 + (0.5 / _AngleTextureScale.y)); 135 | 136 | // Little white cross in the middle 137 | float3 c0 = white * max(drawLine(uv, float2(0.475, 0.5), float2(0.525, 0.5)), drawLine(uv, float2(0.5, 0.475), float2(0.5, 0.525))); 138 | // Red Box showing outer area 139 | float3 c1 = red * max(drawRect(uv, float4(0, 0, 1, 1)), drawLine(uv, float2(0, 0.5), float2(1, 0.5))); 140 | // Box with the scaled ared (eg. normalized) 141 | float3 c2 = red * drawRect(uv, scaled_rect); 142 | // Box with target texture size 143 | float3 c6 = yellow * drawRect(uv, float4(0.5 - (_AngleTextureScale.x / 2), 0.5 - (_AngleTextureScale.y / 2), 0.5 + (_AngleTextureScale.x / 2), 0.5 + (_AngleTextureScale.y / 2))); 144 | 145 | // Box scaled and rotated 146 | float3 c3 = blue * drawRect(rot_uv, scaled_rect); 147 | float3 c4 = yellow * drawRect(rot_scaled_uv, float4(0, 0, 1, 1)); 148 | 149 | // Filled box with blue shade scaled and rotated 150 | float3 c5 = float3(0, 0, 0); 151 | if (saturate(rot_scaled_uv.x) == rot_scaled_uv.x && saturate(rot_scaled_uv.y) == rot_scaled_uv.y) { 152 | c5 += float3(0, lerp(0, 1, rot_scaled_uv.x), lerp(0, 1, rot_scaled_uv.y)); 153 | } 154 | 155 | return float4(max(max(max(max(max(max(c0, c1), c2), c3), c4), c5), c6), 1); 156 | #endif 157 | } 158 | 159 | // Downsample 160 | float4 FragDownsample(Varyings input) : SV_Target 161 | { 162 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 163 | 164 | float2 uv = input.texcoord; 165 | const float dx = _InputTexture_TexelSize.x; 166 | 167 | float u0 = uv.x - dx * 5; 168 | float u1 = uv.x - dx * 3; 169 | float u2 = uv.x - dx * 1; 170 | float u3 = uv.x + dx * 1; 171 | float u4 = uv.x + dx * 3; 172 | float u5 = uv.x + dx * 5; 173 | 174 | half3 c0 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u0, uv.y)).rgb; 175 | half3 c1 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u1, uv.y)).rgb; 176 | half3 c2 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u2, uv.y)).rgb; 177 | half3 c3 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u3, uv.y)).rgb; 178 | half3 c4 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u4, uv.y)).rgb; 179 | half3 c5 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u5, uv.y)).rgb; 180 | 181 | return half4((c0 + c1 * 2 + c2 * 3 + c3 * 3 + c4 * 2 + c5) / 12, 1); 182 | } 183 | 184 | // Upsample 185 | float4 FragUpsample(Varyings input) : SV_Target 186 | { 187 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 188 | 189 | float2 uv = input.texcoord; 190 | const float dx = _InputTexture_TexelSize.x * 1.5; 191 | 192 | float u0 = uv.x - dx; 193 | float u1 = uv.x; 194 | float u2 = uv.x + dx; 195 | 196 | float3 c0 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u0, uv.y)).rgb; 197 | float3 c1 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u1, uv.y)).rgb; 198 | float3 c2 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(u2, uv.y)).rgb; 199 | float3 c3 = SAMPLE_TEXTURE2D(_OtherTexture, s_linear_clamp_sampler, uv).rgb; 200 | return float4(lerp(c3, c0 / 4 + c1 / 2 + c2 / 4, _Stretch), 1); 201 | } 202 | 203 | // Blend with old anamorphic streaks to fade out smoothly 204 | float4 FragFade(Varyings input) : SV_Target 205 | { 206 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 207 | float2 uv = input.texcoord; 208 | float3 c0 = SAMPLE_TEXTURE2D(_OtherTexture, s_linear_clamp_sampler, uv).rgb; 209 | float3 c1 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv).rgb; 210 | return float4(lerp(max(c0,c1), c1, unity_DeltaTime.x/_Fade).rgb, 1); 211 | } 212 | 213 | 214 | // Final composition 215 | float4 FragComposition(Varyings input) : SV_Target 216 | { 217 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 218 | float2 uv = input.texcoord; 219 | uint2 ss = uv * _ScreenSize.xy; 220 | float2x2 rotMat = RotateMatrix(_Angle); 221 | 222 | // Apply inverse scale and rotation 223 | uv = (mul(uv - float2(0.5, 0.5), rotMat) / _AngleTextureScale.xy) + float2(0.5, 0.5); 224 | 225 | #ifndef __DEBUG_SHADER 226 | // Normal Shader 227 | float dx = _InputTexture_TexelSize.x * 1.5; 228 | 229 | float3 c0 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(uv.x + dx, uv.y)).rgb; 230 | float3 c1 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv).rgb; 231 | float3 c2 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, float2(uv.x + dx, uv.y)).rgb; 232 | float3 c3 = LOAD_TEXTURE2D_X(_SourceTexture, ss).rgb; 233 | float3 cf = (c0 / 4 + c1 / 2 + c2 / 4) * _Color.rgb * _Intensity * 5; 234 | 235 | return float4(cf + c3, 1); 236 | #else // Debug Shader Variant 237 | float3 c1 = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv).rgb; 238 | return float4(c1, 1); 239 | #endif 240 | } 241 | 242 | // Fill Black 243 | float4 FragFillBlack(Varyings input) : SV_Target 244 | { 245 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 246 | return float4(0, 0, 0, 1); 247 | } 248 | 249 | // Fill Copy 250 | float4 FragCopy(Varyings input) : SV_Target 251 | { 252 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 253 | float2 uv = input.texcoord; 254 | return SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv); 255 | } 256 | 257 | 258 | ENDHLSL 259 | 260 | SubShader 261 | { 262 | ZWrite Off 263 | ZTest Always 264 | Blend Off 265 | Cull Off 266 | 267 | 268 | Pass // 0 - Prefilter 269 | { 270 | Name "Anamorphic - Prefilter" 271 | HLSLPROGRAM 272 | #pragma fragment FragPrefilter 273 | #pragma vertex Vert 274 | ENDHLSL 275 | } 276 | 277 | Pass // 1 - Downsample 278 | { 279 | Name "Anamorphic - Downsample" 280 | HLSLPROGRAM 281 | #pragma fragment FragDownsample 282 | #pragma vertex Vert 283 | ENDHLSL 284 | } 285 | 286 | Pass // 2 - UpSample 287 | { 288 | Name "Anamorphic - Upsample" 289 | HLSLPROGRAM 290 | #pragma fragment FragUpsample 291 | #pragma vertex Vert 292 | ENDHLSL 293 | } 294 | 295 | Pass // 3 - Fade 296 | { 297 | Name "Anamorphic - Fade" 298 | HLSLPROGRAM 299 | #pragma fragment FragFade 300 | #pragma vertex Vert 301 | ENDHLSL 302 | } 303 | 304 | Pass // 4 - Composition 305 | { 306 | Name "Anamorphic - Composition" 307 | HLSLPROGRAM 308 | #pragma fragment FragComposition 309 | #pragma vertex Vert 310 | ENDHLSL 311 | } 312 | 313 | Pass // 5 - Fill with black 314 | { 315 | Name "Anamorphic - Fill Black" 316 | HLSLPROGRAM 317 | #pragma fragment FragFillBlack 318 | #pragma vertex Vert 319 | ENDHLSL 320 | } 321 | 322 | Pass // 6 - Copy 323 | { 324 | Name "Anamorphic - Copy" 325 | HLSLPROGRAM 326 | #pragma fragment FragCopy 327 | #pragma vertex Vert 328 | ENDHLSL 329 | } 330 | } 331 | Fallback Off 332 | } 333 | -------------------------------------------------------------------------------- /Resources/Anamorphic.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 73546217189656d47a9d916adbff30ba 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Resources/RadialWarpAndGhosts.shader: -------------------------------------------------------------------------------- 1 | // Shader based on the Aerobox Flare shader from https://github.com/modanhan/Unity-Lens-Flare-2019 2 | // Original Shader Copyright(c) 2019 Anthony Han 3 | // Modified (HDRP) Shader Variant Copyright(c) 2020 H. Gregor Molter 4 | 5 | Shader "Hidden/Shader/LensFlares/RadialWarpAndGhosts" 6 | { 7 | HLSLINCLUDE 8 | 9 | #pragma target 4.5 10 | #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch 11 | 12 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" 13 | #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl" 14 | #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl" 15 | 16 | struct Attributes 17 | { 18 | uint vertexID : SV_VertexID; 19 | UNITY_VERTEX_INPUT_INSTANCE_ID 20 | }; 21 | 22 | struct Varyings 23 | { 24 | float4 positionCS : SV_POSITION; 25 | float2 texcoord : TEXCOORD0; 26 | UNITY_VERTEX_OUTPUT_STEREO 27 | }; 28 | 29 | Varyings Vert(Attributes input) 30 | { 31 | Varyings output; 32 | UNITY_SETUP_INSTANCE_ID(input); 33 | UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); 34 | output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID); 35 | output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID); 36 | return output; 37 | } 38 | 39 | // Properties of the Flare Shader 40 | TEXTURE2D_X(_SourceTexture); 41 | TEXTURE2D(_InputTexture); 42 | TEXTURE2D(_AddTexture); 43 | TEXTURE2D(_ChromaticAberration_Spectrum); 44 | 45 | float4 _InputTexture_TexelSize; 46 | 47 | float _Intensity; 48 | float _RadialWarpLength; // 0.5f 49 | float _RadialWarpIntensity; // 0.0025f 50 | float _GhostIntensity; // 0.005f 51 | float _AddMultiplier; // 4f 52 | float _Delta; // 0.5f 53 | 54 | // Initial Prefilter for the Flare shader (just copy it) 55 | float4 FragPrefilter(Varyings input) : SV_Target 56 | { 57 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 58 | uint2 ss = input.texcoord * _ScreenSize.xy; 59 | float3 c = LOAD_TEXTURE2D_X(_SourceTexture, ss).rgb; 60 | return float4(c, 1); 61 | } 62 | 63 | static const float gaussian[7] = { 64 | 0.00598, 0.060626, 0.241843, 0.383103, 0.241843, 0.060626, 0.00598 65 | }; 66 | 67 | // Horizontal Blur 68 | float4 FragHBlur(Varyings input) : SV_Target 69 | { 70 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 71 | float2 uv = input.texcoord; 72 | float2 step = float2(_InputTexture_TexelSize.x, 0); 73 | float3 color; 74 | for (int idx = -3; idx <= 3; idx++) { 75 | float3 tColor = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + idx * step).rgb; 76 | color += tColor * gaussian[idx + 3]; 77 | } 78 | return float4(color, 1); 79 | } 80 | 81 | // Vertical Blur 82 | float4 FragVBlur(Varyings input) : SV_Target 83 | { 84 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 85 | float2 uv = input.texcoord; 86 | float2 step = float2(0, _InputTexture_TexelSize.y); 87 | float3 color; 88 | for (int idx = -3; idx <= 3; idx++) { 89 | float3 tColor = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + idx * step).rgb; 90 | color += tColor * gaussian[idx + 3]; 91 | } 92 | return float4(color, 1); 93 | } 94 | 95 | // Radial warp 96 | float4 FragRadialWarp(Varyings input) : SV_Target 97 | { 98 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 99 | float2 uv = input.texcoord; 100 | float2 ghostVec = uv - 0.5; 101 | float2 haloVec = normalize(ghostVec) * -_RadialWarpLength; 102 | float3 color = max(SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + haloVec).rgb - 0.0, 0) 103 | * length(ghostVec) * _RadialWarpIntensity; 104 | return float4(color, 1); 105 | } 106 | 107 | static const float ghosts[9] = { 108 | 0.625, 0.390625, 0.24414, 0.15258, -0.625, -0.390625, -0.24414, -0.15258, -0.09536, 109 | }; 110 | 111 | // Ghosts 112 | float4 FragGhost(Varyings input) : SV_Target 113 | { 114 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 115 | float2 uv = input.texcoord; 116 | float2 ghost_uv = uv - 0.5; 117 | float3 color; 118 | 119 | for (int i = 0; i < 9; i++) { 120 | float t_p = ghosts[i]; 121 | color += SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, ghost_uv * t_p + 0.5).rgb * (t_p * t_p); 122 | } 123 | return float4(color * _GhostIntensity, 1); 124 | } 125 | 126 | 127 | // Chromatic Aberration from two combined textures 128 | float4 FragChromaticAberration(Varyings input) : SV_Target 129 | { 130 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 131 | float2 texcoord = input.texcoord.xy * 2 - 1; 132 | float2 diff_texels = normalize(-texcoord) * pow(dot(texcoord, texcoord), 0.25) * 36; 133 | float2 diff_sampler = diff_texels * _InputTexture_TexelSize.xy; 134 | float2 pos = input.texcoord.xy; 135 | int samples = clamp(int(length(diff_texels)), 3, 18); 136 | float inv_samples = 1.0 / samples; 137 | float2 delta = diff_sampler * inv_samples; 138 | pos -= delta * samples * 0.5; 139 | float3 sum = float3(0,0,0), filterSum=float3(0, 0, 0); 140 | float2 t = float2(0.5 * inv_samples, 0); 141 | for (int i = 0; i < samples; i++) 142 | { 143 | float3 s1 = SAMPLE_TEXTURE2D_LOD(_InputTexture, s_linear_clamp_sampler, pos, 0).rgb; 144 | float3 s2 = SAMPLE_TEXTURE2D_LOD(_AddTexture, s_linear_clamp_sampler, pos, 0).rgb; 145 | float3 s = s1 + s2 * _AddMultiplier; 146 | float3 filter = SAMPLE_TEXTURE2D_LOD(_ChromaticAberration_Spectrum, s_linear_clamp_sampler, t, 0).rgb; 147 | sum += s * filter; 148 | t.x += inv_samples; 149 | filterSum += filter; 150 | pos += delta; 151 | } 152 | return float4(sum / filterSum, 1); 153 | } 154 | 155 | // Box Up Sampling 156 | float4 FragBox(Varyings input) : SV_Target 157 | { 158 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 159 | float2 uv = input.texcoord; 160 | float4 offset = _InputTexture_TexelSize.xyxy * float4(-_Delta, -_Delta, _Delta, _Delta); 161 | float3 color = (SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + offset.xy).rgb 162 | + SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + offset.zy).rgb 163 | + SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + offset.xw).rgb 164 | + SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv + offset.zw).rgb) * 0.25; 165 | return float4(color, 1); 166 | } 167 | 168 | // Final composition 169 | float4 FragComposition(Varyings input) : SV_Target 170 | { 171 | UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); 172 | float2 uv = input.texcoord; 173 | uint2 ss = uv * _ScreenSize.xy; 174 | 175 | float3 original = LOAD_TEXTURE2D_X(_SourceTexture, ss).rgb; 176 | float3 flare = SAMPLE_TEXTURE2D(_InputTexture, s_linear_clamp_sampler, uv).rgb; 177 | 178 | return float4(original+flare*_AddMultiplier, 1); 179 | } 180 | ENDHLSL 181 | 182 | SubShader 183 | { 184 | ZWrite Off 185 | ZTest Always 186 | Blend Off 187 | Cull Off 188 | 189 | 190 | Pass // 0 - Prefilter 191 | { 192 | Name "Radial Warp and Ghosts - Prefilter" 193 | HLSLPROGRAM 194 | #pragma fragment FragPrefilter 195 | #pragma vertex Vert 196 | ENDHLSL 197 | } 198 | 199 | Pass // 1 - Horizontal Blur 200 | { 201 | Name "Radial Warp and Ghosts - HBlur" 202 | HLSLPROGRAM 203 | #pragma fragment FragHBlur 204 | #pragma vertex Vert 205 | ENDHLSL 206 | } 207 | 208 | Pass // 2 - Veritcal Blur 209 | { 210 | Name "Radial Warp and Ghosts - VBlur" 211 | HLSLPROGRAM 212 | #pragma fragment FragVBlur 213 | #pragma vertex Vert 214 | ENDHLSL 215 | } 216 | 217 | Pass // 3 - Radial Warp 218 | { 219 | Name "Radial Warp and Ghosts - Radial Warp" 220 | HLSLPROGRAM 221 | #pragma fragment FragRadialWarp 222 | #pragma vertex Vert 223 | ENDHLSL 224 | } 225 | 226 | Pass // 4 - Ghost 227 | { 228 | Name "Radial Warp and Ghosts - Ghost" 229 | HLSLPROGRAM 230 | #pragma fragment FragGhost 231 | #pragma vertex Vert 232 | ENDHLSL 233 | } 234 | 235 | Pass // 5 - Chromatic Aberration 236 | { 237 | Name "Radial Warp and Ghosts - Chromatic Aberration" 238 | HLSLPROGRAM 239 | #pragma fragment FragChromaticAberration 240 | #pragma vertex Vert 241 | ENDHLSL 242 | } 243 | 244 | Pass // 6 - Box 245 | { 246 | Name "Radial Warp and Ghosts - Box" 247 | HLSLPROGRAM 248 | #pragma fragment FragBox 249 | #pragma vertex Vert 250 | ENDHLSL 251 | } 252 | 253 | Pass // 7 - Composition 254 | { 255 | Name "Radial Warp and Ghosts - Composition" 256 | HLSLPROGRAM 257 | #pragma fragment FragComposition 258 | #pragma vertex Vert 259 | ENDHLSL 260 | } 261 | } 262 | Fallback Off 263 | } 264 | -------------------------------------------------------------------------------- /Resources/RadialWarpAndGhosts.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 760ab4e086c520447a9a175bfdba54b7 3 | ShaderImporter: 4 | externalObjects: {} 5 | defaultTextures: [] 6 | nonModifiableTextures: [] 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Runtime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7ed6a0dcbc56c314282991272464c232 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Anamorphic.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering; 3 | using UnityEngine.Rendering.HighDefinition; 4 | using GraphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat; 5 | using System; 6 | using System.Collections.Generic; 7 | using UnityEngine.Experimental.Rendering.RenderGraphModule; 8 | using SecretLab.Utilities; 9 | 10 | namespace SecretLab.PostProcessing { 11 | 12 | [Serializable, VolumeComponentMenu("Post-processing/Custom/HDRP Lens Flares/Anamorphic")] 13 | public sealed class Anamorphic : CustomPostProcessVolumeComponent, IPostProcessComponent { 14 | #region Effect parameters 15 | 16 | [Tooltip("Controls the intensity of the effect.")] 17 | public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f); 18 | [Tooltip("Defines the threshold for anamorphic streaks.")] 19 | public ClampedFloatParameter threshold = new ClampedFloatParameter(1f, 0f, 10f); 20 | [Tooltip("Direction of the effect.")] 21 | public ClampedFloatParameter direction = new ClampedFloatParameter(0f, 0f, 180f); 22 | [Tooltip("Defines how stretched the anamorphic light streak is.")] 23 | public ClampedFloatParameter stretch = new ClampedFloatParameter(1f, 0f, 1f); 24 | [Tooltip("If enabled the anamorphic light streak will be blended with light streaks from old frame, i.e. faded out.")] 25 | public ClampedFloatParameter fade = new ClampedFloatParameter(0f, 0f, 0.3f); 26 | public ColorParameter tint = new ColorParameter(Color.white); 27 | 28 | #endregion 29 | 30 | #region Private members 31 | 32 | const string kShaderName = "Hidden/Shader/LensFlares/Anamorphic"; 33 | static class ShaderIDs { 34 | internal static readonly int SourceTexture = Shader.PropertyToID("_SourceTexture"); 35 | internal static readonly int InputTexture = Shader.PropertyToID("_InputTexture"); 36 | internal static readonly int OtherTexture = Shader.PropertyToID("_OtherTexture"); 37 | internal static readonly int Intensity = Shader.PropertyToID("_Intensity"); 38 | internal static readonly int Threshold = Shader.PropertyToID("_Threshold"); 39 | internal static readonly int Stretch = Shader.PropertyToID("_Stretch"); 40 | internal static readonly int Color = Shader.PropertyToID("_Color"); 41 | internal static readonly int Angle = Shader.PropertyToID("_Angle"); 42 | internal static readonly int Fade = Shader.PropertyToID("_Fade"); 43 | internal static readonly int AngleTextureScale = Shader.PropertyToID("_AngleTextureScale"); 44 | } 45 | const int PREFILTER_PASS = 0; 46 | const int DOWNSAMPLE_PASS = 1; 47 | const int UPSAMPLE_PASS = 2; 48 | const int FADE_PASS = 3; 49 | const int COMPOSITION_PASS = 4; 50 | const int FILLBLACK_PASS = 5; 51 | const int COPY_PASS = 6; 52 | 53 | Material _material; 54 | MaterialPropertyBlock _prop; 55 | // Image pyramid storage 56 | // Use different downsample pyramids for each camera. Store them with the camera GUIDs. 57 | Dictionary _pyramids; 58 | 59 | AnamorphicPyramid GetPyramid(HDCamera camera) { 60 | AnamorphicPyramid candid; 61 | var cameraID = camera.camera.GetInstanceID(); 62 | 63 | if (_pyramids.TryGetValue(cameraID, out candid)) { 64 | // Reallocate the RTs when the screen size was changed or direction of the effect. 65 | if (!candid.SizeValid(camera, direction.value)) { 66 | candid.Reallocate(camera, direction.value); 67 | } 68 | } else { 69 | // None found: Allocate a new pyramid. 70 | _pyramids[cameraID] = candid = new AnamorphicPyramid(camera, direction.value); 71 | } 72 | 73 | return candid; 74 | } 75 | 76 | #endregion 77 | 78 | #region Post Process implementation 79 | public bool IsActive() { 80 | if (_material == null || intensity.value == 0f) { 81 | // Disable if material is missing or the effect intensity is zero. 82 | return false; 83 | } 84 | return true; 85 | } 86 | 87 | // Do not post-process the scene view 88 | public override bool visibleInSceneView => false; 89 | 90 | // Do not forget to add this post process in the Custom Post Process Orders list (Project Settings > HDRP Default Settings). 91 | //public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess; 92 | public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.BeforePostProcess; 93 | 94 | public override void Setup() { 95 | _material = CoreUtils.CreateEngineMaterial(kShaderName); 96 | _prop = new MaterialPropertyBlock(); 97 | _pyramids = new Dictionary(); 98 | } 99 | 100 | public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle srcRT, RTHandle dstRT) { 101 | var pyramid = GetPyramid(camera); 102 | 103 | _material.SetTexture(ShaderIDs.SourceTexture, srcRT); 104 | _material.SetFloat(ShaderIDs.Intensity, intensity.value); 105 | _material.SetFloat(ShaderIDs.Threshold, threshold.value); 106 | _material.SetFloat(ShaderIDs.Stretch, stretch.value); 107 | _material.SetFloat(ShaderIDs.Angle, pyramid.angle); 108 | _material.SetVector(ShaderIDs.AngleTextureScale, pyramid.textureScale); 109 | _material.SetColor(ShaderIDs.Color, tint.value); 110 | _material.SetFloat(ShaderIDs.Fade, fade.value); 111 | 112 | if (pyramid.needsClear) { 113 | HDUtils.DrawFullScreen(cmd, _material, pyramid.fadeIn, _prop, FILLBLACK_PASS); 114 | } else if (fade.value>0 && Application.isPlaying) { 115 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid.fadeOut); 116 | HDUtils.DrawFullScreen(cmd, _material, pyramid.fadeIn, _prop, COPY_PASS); 117 | } 118 | 119 | // Source -> Prefilter -> down] 120 | HDUtils.DrawFullScreen(cmd, _material, pyramid[0].down, _prop, PREFILTER_PASS); 121 | 122 | // Do the downsampling 123 | var level = 1; 124 | for (; level < AnamorphicPyramid.MaxMipLevel && pyramid[level].down != null; level++) { 125 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[level - 1].down); 126 | HDUtils.DrawFullScreen(cmd, _material, pyramid[level].down, _prop, DOWNSAMPLE_PASS); 127 | } 128 | 129 | // Do the upsampling 130 | var lastRT = pyramid[--level].down; 131 | for (level--; level >= 1; level--) { 132 | var mip = pyramid[level]; 133 | _prop.SetTexture(ShaderIDs.InputTexture, lastRT); 134 | _prop.SetTexture(ShaderIDs.OtherTexture, mip.down); 135 | HDUtils.DrawFullScreen(cmd, _material, mip.up, _prop, UPSAMPLE_PASS); 136 | lastRT = mip.up; 137 | } 138 | 139 | if (fade.value > 0 && Application.isPlaying) { 140 | _prop.SetTexture(ShaderIDs.InputTexture, lastRT); 141 | _prop.SetTexture(ShaderIDs.OtherTexture, pyramid.fadeIn); 142 | HDUtils.DrawFullScreen(cmd, _material, pyramid.fadeOut, _prop, FADE_PASS); 143 | lastRT = pyramid.fadeOut; 144 | } 145 | 146 | // (Source,down[0]) -> Composition -> Destination 147 | _prop.SetTexture(ShaderIDs.InputTexture, lastRT); 148 | HDUtils.DrawFullScreen(cmd, _material, dstRT, _prop, COMPOSITION_PASS); 149 | } 150 | 151 | public override void Cleanup() { 152 | CoreUtils.Destroy(_material); 153 | foreach (var pyramid in _pyramids.Values) { 154 | pyramid.Release(); 155 | } 156 | } 157 | private static (float sin, float cos) DirectionSinCos(float degree) { 158 | return (Mathf.Sin(degree * Mathf.Deg2Rad), Mathf.Cos(degree * Mathf.Deg2Rad)); 159 | } 160 | 161 | #endregion 162 | 163 | #region Image pyramid class used in Flare effect 164 | sealed class AnamorphicPyramid { 165 | const GraphicsFormat QUALITY = GraphicsFormat.R32G32B32A32_SFloat; 166 | const GraphicsFormat OPTIMAL = GraphicsFormat.R16G16B16A16_SFloat; 167 | const GraphicsFormat FAST = GraphicsFormat.B10G11R11_UFloatPack32; 168 | const GraphicsFormat RTFormat = FAST; 169 | public const int MaxMipLevel = 16; 170 | 171 | int _baseWidth, _baseHeight; 172 | float _degree; 173 | Matrix2x2 _rotation; 174 | Vector2 _scale; 175 | bool _needsClear; 176 | 177 | 178 | public Vector2 textureScale { get { return _scale; } } 179 | public bool needsClear { get { bool r = _needsClear; _needsClear = false; return r; } } 180 | 181 | readonly (RTHandle down, RTHandle up)[] _mips = new (RTHandle,RTHandle)[MaxMipLevel]; 182 | public RTHandle fadeIn, fadeOut; 183 | 184 | public float angle { get { return _degree; } } 185 | 186 | public Matrix2x2 rotation { get { return _rotation; } } 187 | 188 | public (RTHandle down, RTHandle up) this[int index] { 189 | get { return _mips[index]; } 190 | } 191 | 192 | public AnamorphicPyramid(HDCamera camera, float degree) { 193 | Allocate(camera, degree); 194 | } 195 | 196 | public bool SizeValid(HDCamera camera, float degree) { 197 | return _baseHeight == camera.actualHeight && _baseWidth == camera.actualWidth && _degree == degree; 198 | } 199 | 200 | public void Reallocate(HDCamera camera, float degree) { 201 | Release(); 202 | Allocate(camera, degree); 203 | } 204 | 205 | public void Release() { 206 | foreach (var mip in _mips) { 207 | if (mip.up != null) { 208 | RTHandles.Release(mip.up); 209 | } 210 | if (mip.down != null) { 211 | RTHandles.Release(mip.down); 212 | } 213 | } 214 | if (fadeIn != null) { 215 | RTHandles.Release(fadeIn); 216 | } 217 | if (fadeOut != null) { 218 | RTHandles.Release(fadeOut); 219 | } 220 | } 221 | 222 | void Allocate(HDCamera camera, float degree) { 223 | _degree = degree; 224 | _baseHeight = camera.actualHeight; 225 | _baseWidth = camera.actualWidth; 226 | _rotation = Matrix2x2.Rotation(-degree); 227 | 228 | var width = _baseWidth; 229 | var height = _baseHeight; 230 | 231 | // Normal rectangle bounds for non-rotated anamorphic light streaks 232 | Vector2[] p = new Vector2[4]; 233 | p[0] = new Vector2(-0.5f, -0.5f); 234 | p[1] = new Vector2(0.5f, -0.5f); 235 | p[2] = new Vector2(-0.5f, 0.5f); 236 | p[3] = new Vector2(0.5f, 0.5f); 237 | 238 | Vector2 pMin = new Vector2(float.MaxValue, float.MaxValue); 239 | Vector2 pMax = new Vector2(float.MinValue, float.MinValue); 240 | 241 | // Rotate bounding box and set dimension of rotated rectangle 242 | foreach (var p_i in p) { 243 | var i = _rotation * p_i; 244 | pMin.x = Mathf.Min(pMin.x, i.x); 245 | pMin.y = Mathf.Min(pMin.y, i.y); 246 | pMax.x = Mathf.Max(pMax.x, i.x); 247 | pMax.y = Mathf.Max(pMax.y, i.y); 248 | } 249 | 250 | width = (int)Mathf.Ceil(((pMax.x - pMin.x)*width))+16; 251 | height = (int)Mathf.Ceil(((pMax.y - pMin.y)*height))+16; 252 | _scale = new Vector4((float)width / (float)_baseWidth, (float)height / (float)_baseHeight); 253 | height /= 2; 254 | 255 | // First level do not feature an up-sampling texture 256 | _mips[0] = (RTHandles.Alloc(width, height, colorFormat: RTFormat), null); 257 | 258 | for (var i = 1; i < MaxMipLevel; i++) { 259 | // Divide width by 2 260 | width /= 2; 261 | //Debug.Log("Level "+i+": " + width + "x" + height); 262 | if (width < 4 || height < 4) { 263 | // Texture would be to small 264 | _mips[i] = (null, null); 265 | } else { 266 | _mips[i] = (RTHandles.Alloc(width, height, colorFormat: RTFormat), RTHandles.Alloc(width, height, colorFormat: RTFormat)); 267 | } 268 | if (i == 1) { 269 | fadeIn = RTHandles.Alloc(width, height, colorFormat: RTFormat); 270 | fadeOut = RTHandles.Alloc(width, height, colorFormat: RTFormat); 271 | _needsClear = true; 272 | } 273 | } 274 | } 275 | } 276 | #endregion 277 | } 278 | 279 | } -------------------------------------------------------------------------------- /Runtime/Anamorphic.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7be571a9333ab834987cbd3906865b83 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/RadialWarpAndGhosts.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEngine.Rendering; 3 | using UnityEngine.Rendering.HighDefinition; 4 | using GraphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat; 5 | using System; 6 | using System.Collections.Generic; 7 | 8 | namespace SecretLab.PostProcessing { 9 | 10 | [Serializable, VolumeComponentMenu("Post-processing/Custom/HDRP Lens Flares/Radial Warps and Ghosts")] 11 | public sealed class RadialWarpAndGhosts : CustomPostProcessVolumeComponent, IPostProcessComponent { 12 | #region Effect parameters 13 | 14 | [Tooltip("Controls the intensity of the effect.")] 15 | public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f); 16 | [Tooltip("Controls the blur when scaling up the different effect levels.")] 17 | public ClampedFloatParameter blur = new ClampedFloatParameter(0.5f, 0f, 1f); 18 | [Tooltip("LUT-Texture for the chromatic aberration effect.")] 19 | public TextureParameter spectralLut = new TextureParameter(null); 20 | [Header("Radial Warp and Ghosts")] 21 | public ClampedFloatParameter radialWarpIntensity = new ClampedFloatParameter(0.0025f, 0f, 1f); 22 | public ClampedFloatParameter radialWarpLength = new ClampedFloatParameter(0.5f, 0f, 1f); 23 | [Tooltip("Multiplier for the radial warp when mixing radial warp and ghosts together.")] 24 | public ClampedFloatParameter chromaticAberrationMultiplier = new ClampedFloatParameter(3.0f, 0f, 20f); 25 | public ClampedFloatParameter ghostIntensity = new ClampedFloatParameter(0.005f, 0f, 1f); 26 | [Header("Downsampling")] 27 | [Tooltip("Scaling factor of each sampling step.")] 28 | public ClampedFloatParameter factor = new ClampedFloatParameter(2f, 1f, 4f); 29 | [Tooltip("Controls the amount of downsampling steps.")] 30 | public ClampedIntParameter levels = new ClampedIntParameter(1, 1, 4); 31 | 32 | 33 | #endregion 34 | 35 | #region Private members 36 | 37 | const string kShaderName = "Hidden/Shader/LensFlares/RadialWarpAndGhosts"; 38 | static class ShaderIDs { 39 | internal static readonly int SourceTexture = Shader.PropertyToID("_SourceTexture"); 40 | internal static readonly int InputTexture = Shader.PropertyToID("_InputTexture"); 41 | internal static readonly int AddTexture = Shader.PropertyToID("_AddTexture"); 42 | internal static readonly int ChromaticAberration_Spectrum = Shader.PropertyToID("_ChromaticAberration_Spectrum"); 43 | internal static readonly int Delta = Shader.PropertyToID("_Delta"); 44 | internal static readonly int Intensity = Shader.PropertyToID("_Intensity"); 45 | internal static readonly int GhostIntensity = Shader.PropertyToID("_GhostIntensity"); 46 | internal static readonly int RadialWarpIntensity = Shader.PropertyToID("_RadialWarpIntensity"); 47 | internal static readonly int RadialWarpLength = Shader.PropertyToID("_RadialWarpLength"); 48 | internal static readonly int AddMultiplier = Shader.PropertyToID("_AddMultiplier"); 49 | } 50 | const int PREFILTER_PASS = 0; 51 | const int HBLUR_PASS = 1; 52 | const int VBLUR_PASS = 2; 53 | const int RADIALWARP_PASS = 3; 54 | const int GHOST_PASS = 4; 55 | const int CHROMATIC_ABERRATION_PASS = 5; 56 | const int BOX_PASS = 6; 57 | const int COMPOSITION_PASS = 7; 58 | 59 | Material _material; 60 | MaterialPropertyBlock _prop; 61 | // Image pyramid storage 62 | // Use different downsample pyramids for each camera. Store them with the camera GUIDs. 63 | Dictionary _pyramids; 64 | 65 | FlarePyramid GetPyramid(HDCamera camera) { 66 | FlarePyramid candid; 67 | var cameraID = camera.camera.GetInstanceID(); 68 | 69 | if (_pyramids.TryGetValue(cameraID, out candid)) { 70 | // Reallocate the RTs when the screen size was changed or factor or levels. 71 | if (!candid.SizeValid(camera, levels.value + 1, factor.value)) { 72 | candid.Reallocate(camera, levels.value + 1, factor.value); 73 | } 74 | } else { 75 | // None found: Allocate a new pyramid. 76 | _pyramids[cameraID] = candid = new FlarePyramid(camera, levels.value + 1, factor.value); 77 | } 78 | 79 | return candid; 80 | } 81 | 82 | #endregion 83 | 84 | #region Post Process implementation 85 | public bool IsActive() { 86 | if (_material == null || intensity.value == 0f) { 87 | // Disable if material is missing or the effect intensity is zero. 88 | return false; 89 | } 90 | if (ghostIntensity.value == 0f && (radialWarpIntensity == 0f || chromaticAberrationMultiplier == 0f)) { 91 | // Disable if both ghost and radial warp are disabled. 92 | return false; 93 | } 94 | return true; 95 | } 96 | 97 | // Do not post-process the scene view 98 | public override bool visibleInSceneView => false; 99 | 100 | // Do not forget to add this post process in the Custom Post Process Orders list (Project Settings > HDRP Default Settings). 101 | //public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess; 102 | public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.BeforePostProcess; 103 | 104 | 105 | public override void Setup() { 106 | _material = CoreUtils.CreateEngineMaterial(kShaderName); 107 | _prop = new MaterialPropertyBlock(); 108 | _pyramids = new Dictionary(); 109 | } 110 | 111 | public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle srcRT, RTHandle dstRT) { 112 | var pyramid = GetPyramid(camera); 113 | int MAX_DOWNSAMPLE = pyramid.max_downsample; 114 | 115 | _material.SetFloat(ShaderIDs.Intensity, intensity.value); 116 | _material.SetTexture(ShaderIDs.SourceTexture, srcRT); 117 | _material.SetTexture(ShaderIDs.ChromaticAberration_Spectrum, spectralLut.value); 118 | _material.SetFloat(ShaderIDs.RadialWarpLength, radialWarpLength.value); 119 | _material.SetFloat(ShaderIDs.RadialWarpIntensity, radialWarpIntensity.value); 120 | _material.SetFloat(ShaderIDs.GhostIntensity, ghostIntensity.value); 121 | 122 | // Source -> Prefilter -> vblur[0] 123 | HDUtils.DrawFullScreen(cmd, _material, pyramid[0].vblur, _prop, PREFILTER_PASS); 124 | 125 | // {H,V}Blur down by MAX_DOWNSAMPLE levels 126 | for (int i = 1; i <= pyramid.max_downsample; i++) { 127 | // vblur[i-1] -> hblur -> hblur[i] 128 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[i - 1].vblur); 129 | HDUtils.DrawFullScreen(cmd, _material, pyramid[i].hblur, _prop, HBLUR_PASS); 130 | // hblur[i] -> vblur -> vblur[i] 131 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[i].hblur); 132 | HDUtils.DrawFullScreen(cmd, _material, pyramid[i].vblur, _prop, HBLUR_PASS); 133 | } 134 | 135 | var downsample = pyramid[MAX_DOWNSAMPLE].vblur; 136 | 137 | // downsample -> radial warp -> radialWarped 138 | _prop.SetTexture(ShaderIDs.InputTexture, downsample); 139 | HDUtils.DrawFullScreen(cmd, _material, pyramid.radialWarped, _prop, RADIALWARP_PASS); 140 | 141 | // downsample -> ghost -> ghosts 142 | _prop.SetTexture(ShaderIDs.InputTexture, downsample); 143 | HDUtils.DrawFullScreen(cmd, _material, pyramid.ghosts, _prop, GHOST_PASS); 144 | 145 | // (ghosts+radialWarped*4) -> chromatic aberration-> aberration 146 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid.ghosts); 147 | _prop.SetTexture(ShaderIDs.AddTexture, pyramid.radialWarped); 148 | _prop.SetFloat(ShaderIDs.AddMultiplier, chromaticAberrationMultiplier.value); 149 | HDUtils.DrawFullScreen(cmd, _material, pyramid.aberration, _prop, CHROMATIC_ABERRATION_PASS); 150 | 151 | // aberration -> hblur -> hblur[MAX_DOWNSAMPLE] 152 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid.aberration); 153 | HDUtils.DrawFullScreen(cmd, _material, pyramid[MAX_DOWNSAMPLE].hblur, _prop, HBLUR_PASS); 154 | 155 | // hblur[MAX_DOWNSAMPLE] -> vblur -> vblur[MAX_DOWNSAMPLE] 156 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[MAX_DOWNSAMPLE].hblur); 157 | HDUtils.DrawFullScreen(cmd, _material, pyramid[MAX_DOWNSAMPLE].vblur, _prop, VBLUR_PASS); 158 | 159 | // Box up by MAX_DOWNSAMPLE levels, using vblur[i] for each stage 160 | _prop.SetFloat(ShaderIDs.Delta, blur.value); 161 | for (var i = MAX_DOWNSAMPLE - 1; i >= 0; i--) { 162 | // vblur[i+1] -> box -> vblur[i] 163 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[i + 1].vblur); 164 | HDUtils.DrawFullScreen(cmd, _material, pyramid[i].vblur, _prop, BOX_PASS); 165 | } 166 | 167 | // Again {H,V} Blur level 0 168 | // vblur[0] -> hblur -> hblur[0] 169 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[0].vblur); 170 | HDUtils.DrawFullScreen(cmd, _material, pyramid[0].hblur, _prop, HBLUR_PASS); 171 | 172 | // hblur[0] -> vblur -> vblur[0] 173 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[0].hblur); 174 | HDUtils.DrawFullScreen(cmd, _material, pyramid[0].vblur, _prop, VBLUR_PASS); 175 | 176 | // (Source,vblur[0]) -> Composition -> Destination 177 | _prop.SetFloat(ShaderIDs.AddMultiplier, intensity.value); 178 | _prop.SetTexture(ShaderIDs.InputTexture, pyramid[0].vblur); 179 | HDUtils.DrawFullScreen(cmd, _material, dstRT, _prop, COMPOSITION_PASS); 180 | } 181 | 182 | public override void Cleanup() { 183 | CoreUtils.Destroy(_material); 184 | foreach (var pyramid in _pyramids.Values) { 185 | pyramid.Release(); 186 | } 187 | } 188 | #endregion 189 | 190 | #region Image pyramid class used in Flare effect 191 | sealed class FlarePyramid { 192 | const GraphicsFormat QUALITY = GraphicsFormat.R32G32B32A32_SFloat; 193 | const GraphicsFormat OPTIMAL = GraphicsFormat.R16G16B16A16_SFloat; 194 | const GraphicsFormat FAST = GraphicsFormat.B10G11R11_UFloatPack32; 195 | const GraphicsFormat RTFormat = FAST; 196 | 197 | int _baseWidth, _baseHeight, _blurLevels; 198 | float _factor; 199 | 200 | public RTHandle radialWarped; 201 | public RTHandle ghosts; 202 | public RTHandle aberration; 203 | (RTHandle hblur, RTHandle vblur)[] _blurs; 204 | 205 | public int max_downsample { get { return _blurLevels - 1; } } 206 | 207 | public (RTHandle hblur, RTHandle vblur) this[int index] { 208 | get { return _blurs[index]; } 209 | } 210 | 211 | public FlarePyramid(HDCamera camera, int blurLevels, float factor) { 212 | Allocate(camera, blurLevels, factor); 213 | } 214 | 215 | public bool SizeValid(HDCamera camera, int blurLevels, float factor) { 216 | return _baseHeight == camera.actualHeight && _baseWidth == camera.actualWidth && _blurLevels == blurLevels && _factor == factor; 217 | } 218 | 219 | public void Reallocate(HDCamera camera, int blurLevels, float factor) { 220 | Release(); 221 | Allocate(camera, blurLevels, factor); 222 | } 223 | 224 | public void Release() { 225 | if (radialWarped != null) { 226 | RTHandles.Release(radialWarped); 227 | } 228 | if (ghosts != null) { 229 | RTHandles.Release(ghosts); 230 | } 231 | if (aberration != null) { 232 | RTHandles.Release(aberration); 233 | } 234 | foreach (var blur in _blurs) { 235 | if (blur.hblur != null) { 236 | RTHandles.Release(blur.hblur); 237 | } 238 | if (blur.vblur != null) { 239 | RTHandles.Release(blur.vblur); 240 | } 241 | } 242 | } 243 | 244 | void Allocate(HDCamera camera, int blurLevels, float factor) { 245 | _blurLevels = blurLevels; 246 | _factor = factor; 247 | _baseHeight = camera.actualHeight; 248 | _baseWidth = camera.actualWidth; 249 | 250 | _blurs = new (RTHandle, RTHandle)[blurLevels]; 251 | 252 | var width = _baseWidth; 253 | var height = _baseHeight; 254 | 255 | var downsampledWidth = _baseWidth; 256 | var downsampledHeight = _baseHeight; 257 | 258 | _blurs[0] = (RTHandles.Alloc(width, height, colorFormat: RTFormat), RTHandles.Alloc(width, height, colorFormat: RTFormat)); 259 | for (var i = 1; i < _blurs.Length; i++) { 260 | width = (int)((float)width / factor); 261 | height = (int)((float)height / factor); 262 | if (width < 4 || height < 4) { 263 | // Texture would be to small to have an effect. Limit blurLevels. 264 | _blurLevels = Math.Min(_blurLevels, i); 265 | _blurs[i] = (null, null); 266 | } else { 267 | downsampledWidth = width; 268 | downsampledHeight = height; 269 | _blurs[i] = (RTHandles.Alloc(width, height, colorFormat: RTFormat), RTHandles.Alloc(width, height, colorFormat: RTFormat)); 270 | } 271 | } 272 | 273 | radialWarped = RTHandles.Alloc(downsampledWidth, downsampledHeight, colorFormat: RTFormat); 274 | ghosts = RTHandles.Alloc(downsampledWidth, downsampledHeight, colorFormat: RTFormat); 275 | aberration = RTHandles.Alloc(downsampledWidth, downsampledHeight, colorFormat: RTFormat); 276 | } 277 | } 278 | #endregion 279 | } 280 | 281 | } -------------------------------------------------------------------------------- /Runtime/RadialWarpAndGhosts.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 18d9b636cc583934f99ae66c48d10bf1 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Runtime/Secretlab.HDRPLensFlares.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Secretlab.HDRPLensFlares", 3 | "references": [ 4 | "Unity.RenderPipelines.Core.Runtime", 5 | "Unity.RenderPipelines.HighDefinition.Runtime" 6 | ], 7 | "includePlatforms": [ 8 | ], 9 | "excludePlatforms": [], 10 | "allowUnsafeCode": false, 11 | "overrideReferences": false, 12 | "precompiledReferences": [], 13 | "autoReferenced": true, 14 | "defineConstraints": [], 15 | "versionDefines": [], 16 | "noEngineReferences": false 17 | } -------------------------------------------------------------------------------- /Runtime/Secretlab.HDRPLensFlares.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6344785f8e0c3c04f95b25fdf2933b6b 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Runtime/Utilities.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 24f6083d0ad8ea642bd415ec2de383e6 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Runtime/Utilities/Matrix2x2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using UnityEngine; 5 | 6 | namespace SecretLab.Utilities { 7 | // Incomplete helper class for matrix (2x2) caluclations 8 | 9 | [Serializable] 10 | public class Matrix2x2 { 11 | float[,] matrix = new float[2, 2]; 12 | 13 | public Matrix2x2(float v00, float v01, float v10, float v11) { 14 | Set(v00, v01, v10, v11); 15 | } 16 | 17 | public Matrix2x2() { 18 | Set(1, 0, 0, 1); 19 | } 20 | 21 | public Matrix2x2(Matrix2x2 other) { 22 | Set(other[0, 0], other[0, 1], other[1, 0], other[1, 1]); 23 | } 24 | 25 | public float this[int row, int col] { 26 | get { 27 | return matrix[row, col]; 28 | } 29 | set { 30 | matrix[row, col] = value; 31 | } 32 | } 33 | 34 | public void Set(float v1, float v2, float v3, float v4) { 35 | matrix[0, 0] = v1; 36 | matrix[0, 1] = v2; 37 | matrix[1, 0] = v3; 38 | matrix[1, 1] = v4; 39 | } 40 | 41 | public void SetRotation(float degree) { 42 | float rad = Mathf.Deg2Rad * degree; 43 | Set(Mathf.Cos(rad), -Mathf.Sin(rad), Mathf.Sin(rad), Mathf.Cos(rad)); 44 | } 45 | 46 | public static Matrix2x2 Rotation(float degree) { 47 | Matrix2x2 result = new Matrix2x2(); 48 | result.SetRotation(degree); 49 | result *= 0.5f; 50 | result += 0.5f; 51 | result = (result * 2.0f) - 1.0f; 52 | return result; 53 | } 54 | 55 | public static Matrix2x2 operator *(Matrix2x2 m, float scalar) { 56 | Matrix2x2 result = new Matrix2x2(m); 57 | for (int i = 0; i < 2; i++) { 58 | for (int j = 0; j < 2; j++) { 59 | result[i, j] *= scalar; 60 | } 61 | } 62 | return result; 63 | } 64 | 65 | public static Matrix2x2 operator +(Matrix2x2 m, float scalar) { 66 | Matrix2x2 result = new Matrix2x2(m); 67 | for (int i = 0; i < 2; i++) { 68 | for (int j = 0; j < 2; j++) { 69 | result[i, j] += scalar; 70 | } 71 | } 72 | return result; 73 | } 74 | 75 | public static Matrix2x2 operator -(Matrix2x2 m, float scalar) { 76 | return m + (-scalar); 77 | } 78 | 79 | public static Vector2 operator *(Matrix2x2 m, Vector2 v) { 80 | return new Vector2( 81 | m[0, 0] * v.x + m[0, 1] * v.y, 82 | m[1, 0] * v.x + m[1, 1] * v.y); 83 | } 84 | 85 | public override string ToString() { 86 | string str = "["; 87 | for (int i = 0; i < 2; i++) { 88 | str += "["; 89 | for (int j = 0; j < 2; j++) { 90 | str += this[i, j] + ", "; 91 | } 92 | str += i < 1 ? "]," : "]"; 93 | } 94 | return str + "]"; 95 | } 96 | } 97 | } -------------------------------------------------------------------------------- /Runtime/Utilities/Matrix2x2.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f1bf5ccb229d3c645b2b914d38d8158b 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "de.secretlab.hdrplensflares", 3 | "version": "0.1.1", 4 | "displayName": "HDRP Lens Flares", 5 | "description": "Collection of post-processing HDRP lens flare effects.", 6 | "unity": "2019.3", 7 | "unityRelease": "11f1", 8 | "keywords": [ 9 | "HDRP", 10 | "Lens Flare" 11 | ], 12 | "author": { 13 | "name": "H. Gregor Molter", 14 | "email": "gregor.molter@secretlab.de" 15 | } 16 | } -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 4d1bdfc2a143bd743b4f79b96d2d07db 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | --------------------------------------------------------------------------------