252 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			HLSL
		
	
	
	
	
	
		
		
			
		
	
	
			252 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			HLSL
		
	
	
	
	
	
|   | // | ||
|  | // Kino/Bloom v2 - Bloom filter for Unity | ||
|  | // | ||
|  | // Copyright (C) 2015, 2016 Keijiro Takahashi | ||
|  | // | ||
|  | // Permission is hereby granted, free of charge, to any person obtaining a copy | ||
|  | // of this software and associated documentation files (the "Software"), to deal | ||
|  | // in the Software without restriction, including without limitation the rights | ||
|  | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
|  | // copies of the Software, and to permit persons to whom the Software is | ||
|  | // furnished to do so, subject to the following conditions: | ||
|  | // | ||
|  | // The above copyright notice and this permission notice shall be included in | ||
|  | // all copies or substantial portions of the Software. | ||
|  | // | ||
|  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
|  | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
|  | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
|  | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
|  | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
|  | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
|  | // THE SOFTWARE. | ||
|  | // | ||
|  | 
 | ||
|  | #include "UnityCG.cginc" | ||
|  | 
 | ||
|  | // Mobile: use RGBM instead of float/half RGB | ||
|  | #define USE_RGBM defined(SHADER_API_MOBILE) | ||
|  | 
 | ||
|  | sampler2D _MainTex; | ||
|  | sampler2D _BaseTex; | ||
|  | float2 _MainTex_TexelSize; | ||
|  | float2 _BaseTex_TexelSize; | ||
|  | half4 _MainTex_ST; | ||
|  | half4 _BaseTex_ST; | ||
|  | 
 | ||
|  | float _PrefilterOffs; | ||
|  | half _Threshold; | ||
|  | half3 _Curve; | ||
|  | float _SampleScale; | ||
|  | half _Intensity; | ||
|  | 
 | ||
|  | // Brightness function | ||
|  | half Brightness(half3 c) | ||
|  | { | ||
|  |     return max(max(c.r, c.g), c.b); | ||
|  | } | ||
|  | 
 | ||
|  | // 3-tap median filter | ||
|  | half3 Median(half3 a, half3 b, half3 c) | ||
|  | { | ||
|  |     return a + b + c - min(min(a, b), c) - max(max(a, b), c); | ||
|  | } | ||
|  | 
 | ||
|  | // Clamp HDR value within a safe range | ||
|  | half3 SafeHDR(half3 c) { return min(c, 65000); } | ||
|  | half4 SafeHDR(half4 c) { return min(c, 65000); } | ||
|  | 
 | ||
|  | // RGBM encoding/decoding | ||
|  | half4 EncodeHDR(float3 rgb) | ||
|  | { | ||
|  | #if USE_RGBM | ||
|  |     rgb *= 1.0 / 8; | ||
|  |     float m = max(max(rgb.r, rgb.g), max(rgb.b, 1e-6)); | ||
|  |     m = ceil(m * 255) / 255; | ||
|  |     return half4(rgb / m, m); | ||
|  | #else | ||
|  |     return half4(rgb, 0); | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | float3 DecodeHDR(half4 rgba) | ||
|  | { | ||
|  | #if USE_RGBM | ||
|  |     return rgba.rgb * rgba.a * 8; | ||
|  | #else | ||
|  |     return rgba.rgb; | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | // Downsample with a 4x4 box filter | ||
|  | half3 DownsampleFilter(float2 uv) | ||
|  | { | ||
|  |     float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1); | ||
|  | 
 | ||
|  |     half3 s; | ||
|  |     s  = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.xw)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zw)); | ||
|  | 
 | ||
|  |     return s * (1.0 / 4); | ||
|  | } | ||
|  | 
 | ||
|  | // Downsample with a 4x4 box filter + anti-flicker filter | ||
|  | half3 DownsampleAntiFlickerFilter(float2 uv) | ||
|  | { | ||
|  |     float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1); | ||
|  | 
 | ||
|  |     half3 s1 = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | ||
|  |     half3 s2 = DecodeHDR(tex2D(_MainTex, uv + d.zy)); | ||
|  |     half3 s3 = DecodeHDR(tex2D(_MainTex, uv + d.xw)); | ||
|  |     half3 s4 = DecodeHDR(tex2D(_MainTex, uv + d.zw)); | ||
|  | 
 | ||
|  |     // Karis's luma weighted average (using brightness instead of luma) | ||
|  |     half s1w = 1 / (Brightness(s1) + 1); | ||
|  |     half s2w = 1 / (Brightness(s2) + 1); | ||
|  |     half s3w = 1 / (Brightness(s3) + 1); | ||
|  |     half s4w = 1 / (Brightness(s4) + 1); | ||
|  |     half one_div_wsum = 1 / (s1w + s2w + s3w + s4w); | ||
|  | 
 | ||
|  |     return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div_wsum; | ||
|  | } | ||
|  | 
 | ||
|  | half3 UpsampleFilter(float2 uv) | ||
|  | { | ||
|  | #if HIGH_QUALITY | ||
|  |     // 9-tap bilinear upsampler (tent filter) | ||
|  |     float4 d = _MainTex_TexelSize.xyxy * float4(1, 1, -1, 0) * _SampleScale; | ||
|  | 
 | ||
|  |     half3 s; | ||
|  |     s  = DecodeHDR(tex2D(_MainTex, uv - d.xy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv - d.wy)) * 2; | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv - d.zy)); | ||
|  | 
 | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zw)) * 2; | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv       )) * 4; | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.xw)) * 2; | ||
|  | 
 | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.wy)) * 2; | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.xy)); | ||
|  | 
 | ||
|  |     return s * (1.0 / 16); | ||
|  | #else | ||
|  |     // 4-tap bilinear upsampler | ||
|  |     float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1) * (_SampleScale * 0.5); | ||
|  | 
 | ||
|  |     half3 s; | ||
|  |     s  = DecodeHDR(tex2D(_MainTex, uv + d.xy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zy)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.xw)); | ||
|  |     s += DecodeHDR(tex2D(_MainTex, uv + d.zw)); | ||
|  | 
 | ||
|  |     return s * (1.0 / 4); | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | // | ||
|  | // Vertex shader | ||
|  | // | ||
|  | 
 | ||
|  | v2f_img vert(appdata_img v) | ||
|  | { | ||
|  |     v2f_img o; | ||
|  |     o.pos = UnityObjectToClipPos(v.vertex); | ||
|  |     o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST); | ||
|  |     return o; | ||
|  | } | ||
|  | 
 | ||
|  | struct v2f_multitex | ||
|  | { | ||
|  |     float4 pos : SV_POSITION; | ||
|  |     float2 uvMain : TEXCOORD0; | ||
|  |     float2 uvBase : TEXCOORD1; | ||
|  | }; | ||
|  | 
 | ||
|  | v2f_multitex vert_multitex(appdata_img v) | ||
|  | { | ||
|  |     v2f_multitex o; | ||
|  |     o.pos = UnityObjectToClipPos(v.vertex); | ||
|  |     o.uvMain = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST); | ||
|  |     o.uvBase = UnityStereoScreenSpaceUVAdjust(v.texcoord, _BaseTex_ST); | ||
|  | #if UNITY_UV_STARTS_AT_TOP | ||
|  |     if (_BaseTex_TexelSize.y < 0.0) | ||
|  |         o.uvBase.y = 1.0 - v.texcoord.y; | ||
|  | #endif | ||
|  |     return o; | ||
|  | } | ||
|  | 
 | ||
|  | // | ||
|  | // fragment shader | ||
|  | // | ||
|  | 
 | ||
|  | half4 frag_prefilter(v2f_img i) : SV_Target | ||
|  | { | ||
|  |     float2 uv = i.uv + _MainTex_TexelSize.xy * _PrefilterOffs; | ||
|  | 
 | ||
|  | #if ANTI_FLICKER | ||
|  |     float3 d = _MainTex_TexelSize.xyx * float3(1, 1, 0); | ||
|  |     half4 s0 = SafeHDR(tex2D(_MainTex, uv)); | ||
|  |     half3 s1 = SafeHDR(tex2D(_MainTex, uv - d.xz).rgb); | ||
|  |     half3 s2 = SafeHDR(tex2D(_MainTex, uv + d.xz).rgb); | ||
|  |     half3 s3 = SafeHDR(tex2D(_MainTex, uv - d.zy).rgb); | ||
|  |     half3 s4 = SafeHDR(tex2D(_MainTex, uv + d.zy).rgb); | ||
|  |     half3 m = Median(Median(s0.rgb, s1, s2), s3, s4); | ||
|  | #else | ||
|  |     half4 s0 = SafeHDR(tex2D(_MainTex, uv)); | ||
|  |     half3 m = s0.rgb; | ||
|  | #endif | ||
|  | 
 | ||
|  | #if UNITY_COLORSPACE_GAMMA | ||
|  |     m = GammaToLinearSpace(m); | ||
|  | #endif | ||
|  |     // Pixel brightness | ||
|  |     half br = Brightness(m); | ||
|  | 
 | ||
|  |     // Under-threshold part: quadratic curve | ||
|  |     half rq = clamp(br - _Curve.x, 0, _Curve.y); | ||
|  |     rq = _Curve.z * rq * rq; | ||
|  | 
 | ||
|  |     // Combine and apply the brightness response curve. | ||
|  |     m *= max(rq, br - _Threshold) / max(br, 1e-5); | ||
|  | 
 | ||
|  |     return EncodeHDR(m); | ||
|  | } | ||
|  | 
 | ||
|  | half4 frag_downsample1(v2f_img i) : SV_Target | ||
|  | { | ||
|  | #if ANTI_FLICKER | ||
|  |     return EncodeHDR(DownsampleAntiFlickerFilter(i.uv)); | ||
|  | #else | ||
|  |     return EncodeHDR(DownsampleFilter(i.uv)); | ||
|  | #endif | ||
|  | } | ||
|  | 
 | ||
|  | half4 frag_downsample2(v2f_img i) : SV_Target | ||
|  | { | ||
|  |     return EncodeHDR(DownsampleFilter(i.uv)); | ||
|  | } | ||
|  | 
 | ||
|  | half4 frag_upsample(v2f_multitex i) : SV_Target | ||
|  | { | ||
|  |     half3 base = DecodeHDR(tex2D(_BaseTex, i.uvBase)); | ||
|  |     half3 blur = UpsampleFilter(i.uvMain); | ||
|  |     return EncodeHDR(base + blur); | ||
|  | } | ||
|  | 
 | ||
|  | half4 frag_upsample_final(v2f_multitex i) : SV_Target | ||
|  | { | ||
|  |     half4 base = tex2D(_BaseTex, i.uvBase); | ||
|  |     half3 blur = UpsampleFilter(i.uvMain); | ||
|  | #if UNITY_COLORSPACE_GAMMA | ||
|  |     base.rgb = GammaToLinearSpace(base.rgb); | ||
|  | #endif | ||
|  |     half3 cout = base.rgb + blur * _Intensity; | ||
|  | #if UNITY_COLORSPACE_GAMMA | ||
|  |     cout = LinearToGammaSpace(cout); | ||
|  | #endif | ||
|  |     return half4(cout, base.a); | ||
|  | } |