Bloom.cginc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. // Copyright (C) 2015, 2016 Keijiro Takahashi
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19. // THE SOFTWARE.
  20. //
  21. #include "UnityCG.cginc"
  22. // Mobile: use RGBM instead of float/half RGB
  23. #define USE_RGBM defined(SHADER_API_MOBILE)
  24. sampler2D _MainTex;
  25. sampler2D _BaseTex;
  26. float2 _MainTex_TexelSize;
  27. float2 _BaseTex_TexelSize;
  28. half4 _MainTex_ST;
  29. half4 _BaseTex_ST;
  30. float _PrefilterOffs;
  31. half _Threshold;
  32. half3 _Curve;
  33. float _SampleScale;
  34. half _Intensity;
  35. // Brightness function
  36. half Brightness(half3 c)
  37. {
  38. return max(max(c.r, c.g), c.b);
  39. }
  40. // 3-tap median filter
  41. half3 Median(half3 a, half3 b, half3 c)
  42. {
  43. return a + b + c - min(min(a, b), c) - max(max(a, b), c);
  44. }
  45. // Clamp HDR value within a safe range
  46. half3 SafeHDR(half3 c) { return min(c, 65000); }
  47. half4 SafeHDR(half4 c) { return min(c, 65000); }
  48. // RGBM encoding/decoding
  49. half4 EncodeHDR(float3 rgb)
  50. {
  51. #if USE_RGBM
  52. rgb *= 1.0 / 8;
  53. float m = max(max(rgb.r, rgb.g), max(rgb.b, 1e-6));
  54. m = ceil(m * 255) / 255;
  55. return half4(rgb / m, m);
  56. #else
  57. return half4(rgb, 0);
  58. #endif
  59. }
  60. float3 DecodeHDR(half4 rgba)
  61. {
  62. #if USE_RGBM
  63. return rgba.rgb * rgba.a * 8;
  64. #else
  65. return rgba.rgb;
  66. #endif
  67. }
  68. // Downsample with a 4x4 box filter
  69. half3 DownsampleFilter(float2 uv)
  70. {
  71. float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1);
  72. half3 s;
  73. s = DecodeHDR(tex2D(_MainTex, uv + d.xy));
  74. s += DecodeHDR(tex2D(_MainTex, uv + d.zy));
  75. s += DecodeHDR(tex2D(_MainTex, uv + d.xw));
  76. s += DecodeHDR(tex2D(_MainTex, uv + d.zw));
  77. return s * (1.0 / 4);
  78. }
  79. // Downsample with a 4x4 box filter + anti-flicker filter
  80. half3 DownsampleAntiFlickerFilter(float2 uv)
  81. {
  82. float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1);
  83. half3 s1 = DecodeHDR(tex2D(_MainTex, uv + d.xy));
  84. half3 s2 = DecodeHDR(tex2D(_MainTex, uv + d.zy));
  85. half3 s3 = DecodeHDR(tex2D(_MainTex, uv + d.xw));
  86. half3 s4 = DecodeHDR(tex2D(_MainTex, uv + d.zw));
  87. // Karis's luma weighted average (using brightness instead of luma)
  88. half s1w = 1 / (Brightness(s1) + 1);
  89. half s2w = 1 / (Brightness(s2) + 1);
  90. half s3w = 1 / (Brightness(s3) + 1);
  91. half s4w = 1 / (Brightness(s4) + 1);
  92. half one_div_wsum = 1 / (s1w + s2w + s3w + s4w);
  93. return (s1 * s1w + s2 * s2w + s3 * s3w + s4 * s4w) * one_div_wsum;
  94. }
  95. half3 UpsampleFilter(float2 uv)
  96. {
  97. #if HIGH_QUALITY
  98. // 9-tap bilinear upsampler (tent filter)
  99. float4 d = _MainTex_TexelSize.xyxy * float4(1, 1, -1, 0) * _SampleScale;
  100. half3 s;
  101. s = DecodeHDR(tex2D(_MainTex, uv - d.xy));
  102. s += DecodeHDR(tex2D(_MainTex, uv - d.wy)) * 2;
  103. s += DecodeHDR(tex2D(_MainTex, uv - d.zy));
  104. s += DecodeHDR(tex2D(_MainTex, uv + d.zw)) * 2;
  105. s += DecodeHDR(tex2D(_MainTex, uv )) * 4;
  106. s += DecodeHDR(tex2D(_MainTex, uv + d.xw)) * 2;
  107. s += DecodeHDR(tex2D(_MainTex, uv + d.zy));
  108. s += DecodeHDR(tex2D(_MainTex, uv + d.wy)) * 2;
  109. s += DecodeHDR(tex2D(_MainTex, uv + d.xy));
  110. return s * (1.0 / 16);
  111. #else
  112. // 4-tap bilinear upsampler
  113. float4 d = _MainTex_TexelSize.xyxy * float4(-1, -1, +1, +1) * (_SampleScale * 0.5);
  114. half3 s;
  115. s = DecodeHDR(tex2D(_MainTex, uv + d.xy));
  116. s += DecodeHDR(tex2D(_MainTex, uv + d.zy));
  117. s += DecodeHDR(tex2D(_MainTex, uv + d.xw));
  118. s += DecodeHDR(tex2D(_MainTex, uv + d.zw));
  119. return s * (1.0 / 4);
  120. #endif
  121. }
  122. //
  123. // Vertex shader
  124. //
  125. v2f_img vert(appdata_img v)
  126. {
  127. v2f_img o;
  128. o.pos = UnityObjectToClipPos(v.vertex);
  129. o.uv = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);
  130. return o;
  131. }
  132. struct v2f_multitex
  133. {
  134. float4 pos : SV_POSITION;
  135. float2 uvMain : TEXCOORD0;
  136. float2 uvBase : TEXCOORD1;
  137. };
  138. v2f_multitex vert_multitex(appdata_img v)
  139. {
  140. v2f_multitex o;
  141. o.pos = UnityObjectToClipPos(v.vertex);
  142. o.uvMain = UnityStereoScreenSpaceUVAdjust(v.texcoord, _MainTex_ST);
  143. o.uvBase = UnityStereoScreenSpaceUVAdjust(v.texcoord, _BaseTex_ST);
  144. #if UNITY_UV_STARTS_AT_TOP
  145. if (_BaseTex_TexelSize.y < 0.0)
  146. o.uvBase.y = 1.0 - v.texcoord.y;
  147. #endif
  148. return o;
  149. }
  150. // fragment shader
  151. half4 frag_prefilter(v2f_img i) : SV_Target
  152. {
  153. float2 uv = i.uv + _MainTex_TexelSize.xy * _PrefilterOffs;
  154. #if ANTI_FLICKER
  155. float3 d = _MainTex_TexelSize.xyx * float3(1, 1, 0);
  156. half4 s0 = SafeHDR(tex2D(_MainTex, uv));
  157. half3 s1 = SafeHDR(tex2D(_MainTex, uv - d.xz).rgb);
  158. half3 s2 = SafeHDR(tex2D(_MainTex, uv + d.xz).rgb);
  159. half3 s3 = SafeHDR(tex2D(_MainTex, uv - d.zy).rgb);
  160. half3 s4 = SafeHDR(tex2D(_MainTex, uv + d.zy).rgb);
  161. half3 m = Median(Median(s0.rgb, s1, s2), s3, s4);
  162. #else
  163. half4 s0 = SafeHDR(tex2D(_MainTex, uv));
  164. half3 m = s0.rgb;
  165. #endif
  166. #if UNITY_COLORSPACE_GAMMA
  167. m = GammaToLinearSpace(m);
  168. #endif
  169. // Pixel brightness
  170. half br = Brightness(m);
  171. // Under-threshold part: quadratic curve
  172. half rq = clamp(br - _Curve.x, 0, _Curve.y);
  173. rq = _Curve.z * rq * rq;
  174. // Combine and apply the brightness response curve.
  175. m *= max(rq, br - _Threshold) / max(br, 1e-5);
  176. return EncodeHDR(m);
  177. }
  178. half4 frag_downsample1(v2f_img i) : SV_Target
  179. {
  180. #if ANTI_FLICKER
  181. return EncodeHDR(DownsampleAntiFlickerFilter(i.uv));
  182. #else
  183. return EncodeHDR(DownsampleFilter(i.uv));
  184. #endif
  185. }
  186. half4 frag_downsample2(v2f_img i) : SV_Target
  187. {
  188. return EncodeHDR(DownsampleFilter(i.uv));
  189. }
  190. half4 frag_upsample(v2f_multitex i) : SV_Target
  191. {
  192. half3 base = DecodeHDR(tex2D(_BaseTex, i.uvBase));
  193. half3 blur = UpsampleFilter(i.uvMain);
  194. return EncodeHDR(base + blur);
  195. }
  196. half4 frag_upsample_final(v2f_multitex i) : SV_Target
  197. {
  198. half4 base = tex2D(_BaseTex, i.uvBase);
  199. half3 blur = UpsampleFilter(i.uvMain);
  200. #if UNITY_COLORSPACE_GAMMA
  201. base.rgb = GammaToLinearSpace(base.rgb);
  202. #endif
  203. half3 cout = base.rgb + blur * _Intensity;
  204. #if UNITY_COLORSPACE_GAMMA
  205. cout = LinearToGammaSpace(cout);
  206. #endif
  207. return half4(cout, base.a);
  208. }