ScalableAO.hlsl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. #ifndef UNITY_POSTFX_AMBIENT_OCCLUSION
  2. #define UNITY_POSTFX_AMBIENT_OCCLUSION
  3. #include "../StdLib.hlsl"
  4. #include "../Colors.hlsl"
  5. #include "Fog.hlsl"
  6. // --------
  7. // Options for further customization
  8. // --------
  9. // By default, a 5-tap Gaussian with the linear sampling technique is used
  10. // in the bilateral noise filter. It can be replaced with a 7-tap Gaussian
  11. // with adaptive sampling by enabling the macro below. Although the
  12. // differences are not noticeable in most cases, it may provide preferable
  13. // results with some special usage (e.g. NPR without textureing).
  14. // #define BLUR_HIGH_QUALITY
  15. // By default, a fixed sampling pattern is used in the AO estimator. Although
  16. // this gives preferable results in most cases, a completely random sampling
  17. // pattern could give aesthetically better results. Disable the macro below
  18. // to use such a random pattern instead of the fixed one.
  19. #define FIX_SAMPLING_PATTERN
  20. // The SampleNormal function normalizes samples from G-buffer because
  21. // they're possibly unnormalized. We can eliminate this if it can be said
  22. // that there is no wrong shader that outputs unnormalized normals.
  23. // #define VALIDATE_NORMALS
  24. // The constant below determines the contrast of occlusion. This allows
  25. // users to control over/under occlusion. At the moment, this is not exposed
  26. // to the editor because it's rarely useful.
  27. static const float kContrast = 0.6;
  28. // The constant below controls the geometry-awareness of the bilateral
  29. // filter. The higher value, the more sensitive it is.
  30. static const float kGeometryCoeff = 0.8;
  31. // The constants below are used in the AO estimator. Beta is mainly used
  32. // for suppressing self-shadowing noise, and Epsilon is used to prevent
  33. // calculation underflow. See the paper (Morgan 2011 http://goo.gl/2iz3P)
  34. // for further details of these constants.
  35. static const float kBeta = 0.002;
  36. // --------
  37. // System built-in variables
  38. TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
  39. TEXTURE2D_SAMPLER2D(_CameraGBufferTexture2, sampler_CameraGBufferTexture2);
  40. TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture);
  41. TEXTURE2D_SAMPLER2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture);
  42. float4 _MainTex_TexelSize;
  43. float4 _AOParams;
  44. float3 _AOColor;
  45. // Sample count
  46. #if !defined(SHADER_API_GLES)
  47. #define SAMPLE_COUNT _AOParams.w
  48. #else
  49. // GLES2: In many cases, dynamic looping is not supported.
  50. #define SAMPLE_COUNT 3
  51. #endif
  52. // Source texture properties
  53. TEXTURE2D_SAMPLER2D(_SAOcclusionTexture, sampler_SAOcclusionTexture);
  54. float4 _SAOcclusionTexture_TexelSize;
  55. // Other parameters
  56. #define INTENSITY _AOParams.x
  57. #define RADIUS _AOParams.y
  58. #define DOWNSAMPLE _AOParams.z
  59. // Accessors for packed AO/normal buffer
  60. half4 PackAONormal(half ao, half3 n)
  61. {
  62. return half4(ao, n * 0.5 + 0.5);
  63. }
  64. half GetPackedAO(half4 p)
  65. {
  66. return p.r;
  67. }
  68. half3 GetPackedNormal(half4 p)
  69. {
  70. return p.gba * 2.0 - 1.0;
  71. }
  72. // Boundary check for depth sampler
  73. // (returns a very large value if it lies out of bounds)
  74. float CheckBounds(float2 uv, float d)
  75. {
  76. float ob = any(uv < 0) + any(uv > 1);
  77. #if defined(UNITY_REVERSED_Z)
  78. ob += (d <= 0.00001);
  79. #else
  80. ob += (d >= 0.99999);
  81. #endif
  82. return ob * 1e8;
  83. }
  84. // Depth/normal sampling functions
  85. float SampleDepth(float2 uv)
  86. {
  87. float d = Linear01Depth(SAMPLE_DEPTH_TEXTURE_LOD(_CameraDepthTexture, sampler_CameraDepthTexture, UnityStereoTransformScreenSpaceTex(uv), 0));
  88. return d * _ProjectionParams.z + CheckBounds(uv, d);
  89. }
  90. float3 SampleNormal(float2 uv)
  91. {
  92. #if defined(SOURCE_GBUFFER)
  93. float3 norm = SAMPLE_TEXTURE2D(_CameraGBufferTexture2, sampler_CameraGBufferTexture2, uv).xyz;
  94. norm = norm * 2 - any(norm); // gets (0,0,0) when norm == 0
  95. norm = mul((float3x3)unity_WorldToCamera, norm);
  96. #if defined(VALIDATE_NORMALS)
  97. norm = normalize(norm);
  98. #endif
  99. return norm;
  100. #else
  101. float4 cdn = SAMPLE_TEXTURE2D(_CameraDepthNormalsTexture, sampler_CameraDepthNormalsTexture, uv);
  102. return DecodeViewNormalStereo(cdn) * float3(1.0, 1.0, -1.0);
  103. #endif
  104. }
  105. float SampleDepthNormal(float2 uv, out float3 normal)
  106. {
  107. normal = SampleNormal(UnityStereoTransformScreenSpaceTex(uv));
  108. return SampleDepth(uv);
  109. }
  110. // Normal vector comparer (for geometry-aware weighting)
  111. half CompareNormal(half3 d1, half3 d2)
  112. {
  113. return smoothstep(kGeometryCoeff, 1.0, dot(d1, d2));
  114. }
  115. // Trigonometric function utility
  116. float2 CosSin(float theta)
  117. {
  118. float sn, cs;
  119. sincos(theta, sn, cs);
  120. return float2(cs, sn);
  121. }
  122. // Pseudo random number generator with 2D coordinates
  123. float UVRandom(float u, float v)
  124. {
  125. float f = dot(float2(12.9898, 78.233), float2(u, v));
  126. return frac(43758.5453 * sin(f));
  127. }
  128. // Check if the camera is perspective.
  129. // (returns 1.0 when orthographic)
  130. float CheckPerspective(float x)
  131. {
  132. return lerp(x, 1.0, unity_OrthoParams.w);
  133. }
  134. // Reconstruct view-space position from UV and depth.
  135. // p11_22 = (unity_CameraProjection._11, unity_CameraProjection._22)
  136. // p13_31 = (unity_CameraProjection._13, unity_CameraProjection._23)
  137. float3 ReconstructViewPos(float2 uv, float depth, float2 p11_22, float2 p13_31)
  138. {
  139. return float3((uv * 2.0 - 1.0 - p13_31) / p11_22 * CheckPerspective(depth), depth);
  140. }
  141. // Sample point picker
  142. float3 PickSamplePoint(float2 uv, float index)
  143. {
  144. // Uniformaly distributed points on a unit sphere
  145. // http://mathworld.wolfram.com/SpherePointPicking.html
  146. #if defined(FIX_SAMPLING_PATTERN)
  147. float gn = GradientNoise(uv * DOWNSAMPLE);
  148. // FIXEME: This was added to avoid a NVIDIA driver issue.
  149. // vvvvvvvvvvvv
  150. float u = frac(UVRandom(0.0, index + uv.x * 1e-10) + gn) * 2.0 - 1.0;
  151. float theta = (UVRandom(1.0, index + uv.x * 1e-10) + gn) * TWO_PI;
  152. #else
  153. float u = UVRandom(uv.x + _Time.x, uv.y + index) * 2.0 - 1.0;
  154. float theta = UVRandom(-uv.x - _Time.x, uv.y + index) * TWO_PI;
  155. #endif
  156. float3 v = float3(CosSin(theta) * sqrt(1.0 - u * u), u);
  157. // Make them distributed between [0, _Radius]
  158. float l = sqrt((index + 1.0) / SAMPLE_COUNT) * RADIUS;
  159. return v * l;
  160. }
  161. //
  162. // Distance-based AO estimator based on Morgan 2011
  163. // "Alchemy screen-space ambient obscurance algorithm"
  164. // http://graphics.cs.williams.edu/papers/AlchemyHPG11/
  165. //
  166. float4 FragAO(VaryingsDefault i) : SV_Target
  167. {
  168. float2 uv = i.texcoord;
  169. // Parameters used in coordinate conversion
  170. float3x3 proj = (float3x3)unity_CameraProjection;
  171. float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22);
  172. float2 p13_31 = float2(unity_CameraProjection._13, unity_CameraProjection._23);
  173. // View space normal and depth
  174. float3 norm_o;
  175. float depth_o = SampleDepthNormal(uv, norm_o);
  176. // Reconstruct the view-space position.
  177. float3 vpos_o = ReconstructViewPos(uv, depth_o, p11_22, p13_31);
  178. float ao = 0.0;
  179. for (int s = 0; s < int(SAMPLE_COUNT); s++)
  180. {
  181. // Sample point
  182. #if defined(SHADER_API_D3D11)
  183. // This 'floor(1.0001 * s)' operation is needed to avoid a NVidia shader issue. This issue
  184. // is only observed on DX11.
  185. float3 v_s1 = PickSamplePoint(uv, floor(1.0001 * s));
  186. #else
  187. float3 v_s1 = PickSamplePoint(uv, s);
  188. #endif
  189. v_s1 = faceforward(v_s1, -norm_o, v_s1);
  190. float3 vpos_s1 = vpos_o + v_s1;
  191. // Reproject the sample point
  192. float3 spos_s1 = mul(proj, vpos_s1);
  193. float2 uv_s1_01 = (spos_s1.xy / CheckPerspective(vpos_s1.z) + 1.0) * 0.5;
  194. // Depth at the sample point
  195. float depth_s1 = SampleDepth(uv_s1_01);
  196. // Relative position of the sample point
  197. float3 vpos_s2 = ReconstructViewPos(uv_s1_01, depth_s1, p11_22, p13_31);
  198. float3 v_s2 = vpos_s2 - vpos_o;
  199. // Estimate the obscurance value
  200. float a1 = max(dot(v_s2, norm_o) - kBeta * depth_o, 0.0);
  201. float a2 = dot(v_s2, v_s2) + EPSILON;
  202. ao += a1 / a2;
  203. }
  204. ao *= RADIUS; // Intensity normalization
  205. // Apply other parameters.
  206. ao = PositivePow(ao * INTENSITY / SAMPLE_COUNT, kContrast);
  207. // Apply fog when enabled (forward-only)
  208. #if (APPLY_FORWARD_FOG)
  209. float d = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, i.texcoordStereo));
  210. d = ComputeFogDistance(d);
  211. ao *= ComputeFog(d);
  212. #endif
  213. return PackAONormal(ao, norm_o);
  214. }
  215. // Geometry-aware separable bilateral filter
  216. float4 FragBlur(VaryingsDefault i) : SV_Target
  217. {
  218. #if defined(BLUR_HORIZONTAL)
  219. // Horizontal pass: Always use 2 texels interval to match to
  220. // the dither pattern.
  221. float2 delta = float2(_MainTex_TexelSize.x * 2.0, 0.0);
  222. #else
  223. // Vertical pass: Apply _Downsample to match to the dither
  224. // pattern in the original occlusion buffer.
  225. float2 delta = float2(0.0, _MainTex_TexelSize.y / DOWNSAMPLE * 2.0);
  226. #endif
  227. #if defined(BLUR_HIGH_QUALITY)
  228. // High quality 7-tap Gaussian with adaptive sampling
  229. half4 p0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
  230. half4 p1a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord - delta));
  231. half4 p1b = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord + delta));
  232. half4 p2a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord - delta * 2.0));
  233. half4 p2b = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord + delta * 2.0));
  234. half4 p3a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord - delta * 3.2307692308));
  235. half4 p3b = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord + delta * 3.2307692308));
  236. #if defined(BLUR_SAMPLE_CENTER_NORMAL)
  237. half3 n0 = SampleNormal(i.texcoordStereo);
  238. #else
  239. half3 n0 = GetPackedNormal(p0);
  240. #endif
  241. half w0 = 0.37004405286;
  242. half w1a = CompareNormal(n0, GetPackedNormal(p1a)) * 0.31718061674;
  243. half w1b = CompareNormal(n0, GetPackedNormal(p1b)) * 0.31718061674;
  244. half w2a = CompareNormal(n0, GetPackedNormal(p2a)) * 0.19823788546;
  245. half w2b = CompareNormal(n0, GetPackedNormal(p2b)) * 0.19823788546;
  246. half w3a = CompareNormal(n0, GetPackedNormal(p3a)) * 0.11453744493;
  247. half w3b = CompareNormal(n0, GetPackedNormal(p3b)) * 0.11453744493;
  248. half s;
  249. s = GetPackedAO(p0) * w0;
  250. s += GetPackedAO(p1a) * w1a;
  251. s += GetPackedAO(p1b) * w1b;
  252. s += GetPackedAO(p2a) * w2a;
  253. s += GetPackedAO(p2b) * w2b;
  254. s += GetPackedAO(p3a) * w3a;
  255. s += GetPackedAO(p3b) * w3b;
  256. s /= w0 + w1a + w1b + w2a + w2b + w3a + w3b;
  257. #else
  258. // Fater 5-tap Gaussian with linear sampling
  259. half4 p0 = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
  260. half4 p1a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord - delta * 1.3846153846));
  261. half4 p1b = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord + delta * 1.3846153846));
  262. half4 p2a = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord - delta * 3.2307692308));
  263. half4 p2b = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, UnityStereoTransformScreenSpaceTex(i.texcoord + delta * 3.2307692308));
  264. #if defined(BLUR_SAMPLE_CENTER_NORMAL)
  265. half3 n0 = SampleNormal(i.texcoordStereo);
  266. #else
  267. half3 n0 = GetPackedNormal(p0);
  268. #endif
  269. half w0 = 0.2270270270;
  270. half w1a = CompareNormal(n0, GetPackedNormal(p1a)) * 0.3162162162;
  271. half w1b = CompareNormal(n0, GetPackedNormal(p1b)) * 0.3162162162;
  272. half w2a = CompareNormal(n0, GetPackedNormal(p2a)) * 0.0702702703;
  273. half w2b = CompareNormal(n0, GetPackedNormal(p2b)) * 0.0702702703;
  274. half s;
  275. s = GetPackedAO(p0) * w0;
  276. s += GetPackedAO(p1a) * w1a;
  277. s += GetPackedAO(p1b) * w1b;
  278. s += GetPackedAO(p2a) * w2a;
  279. s += GetPackedAO(p2b) * w2b;
  280. s /= w0 + w1a + w1b + w2a + w2b;
  281. #endif
  282. return PackAONormal(s, n0);
  283. }
  284. // Gamma encoding (only needed in gamma lighting mode)
  285. half EncodeAO(half x)
  286. {
  287. #if UNITY_COLORSPACE_GAMMA
  288. return 1.0 - max(LinearToSRGB(1.0 - saturate(x)), 0.0);
  289. #else
  290. return x;
  291. #endif
  292. }
  293. // Geometry-aware bilateral filter (single pass/small kernel)
  294. half BlurSmall(TEXTURE2D_ARGS(tex, samp), float2 uv, float2 delta)
  295. {
  296. half4 p0 = SAMPLE_TEXTURE2D(tex, samp, UnityStereoTransformScreenSpaceTex(uv));
  297. half4 p1 = SAMPLE_TEXTURE2D(tex, samp, UnityStereoTransformScreenSpaceTex(uv + float2(-delta.x, -delta.y)));
  298. half4 p2 = SAMPLE_TEXTURE2D(tex, samp, UnityStereoTransformScreenSpaceTex(uv + float2( delta.x, -delta.y)));
  299. half4 p3 = SAMPLE_TEXTURE2D(tex, samp, UnityStereoTransformScreenSpaceTex(uv + float2(-delta.x, delta.y)));
  300. half4 p4 = SAMPLE_TEXTURE2D(tex, samp, UnityStereoTransformScreenSpaceTex(uv + float2( delta.x, delta.y)));
  301. half3 n0 = GetPackedNormal(p0);
  302. half w0 = 1.0;
  303. half w1 = CompareNormal(n0, GetPackedNormal(p1));
  304. half w2 = CompareNormal(n0, GetPackedNormal(p2));
  305. half w3 = CompareNormal(n0, GetPackedNormal(p3));
  306. half w4 = CompareNormal(n0, GetPackedNormal(p4));
  307. half s;
  308. s = GetPackedAO(p0) * w0;
  309. s += GetPackedAO(p1) * w1;
  310. s += GetPackedAO(p2) * w2;
  311. s += GetPackedAO(p3) * w3;
  312. s += GetPackedAO(p4) * w4;
  313. return s / (w0 + w1 + w2 + w3 + w4);
  314. }
  315. // Final composition shader
  316. float4 FragComposition(VaryingsDefault i) : SV_Target
  317. {
  318. float2 delta = _SAOcclusionTexture_TexelSize.xy / DOWNSAMPLE;
  319. half ao = BlurSmall(TEXTURE2D_PARAM(_SAOcclusionTexture, sampler_SAOcclusionTexture), i.texcoord, delta);
  320. ao = EncodeAO(ao);
  321. return float4(ao * _AOColor, ao);
  322. }
  323. #if !SHADER_API_GLES // Excluding the MRT pass under GLES2
  324. struct CompositionOutput
  325. {
  326. half4 gbuffer0 : SV_Target0;
  327. half4 gbuffer3 : SV_Target1;
  328. };
  329. CompositionOutput FragCompositionGBuffer(VaryingsDefault i)
  330. {
  331. // Workaround: _SAOcclusionTexture_Texelsize hasn't been set properly
  332. // for some reasons. Use _ScreenParams instead.
  333. float2 delta = (_ScreenParams.zw - 1.0) / DOWNSAMPLE;
  334. half ao = BlurSmall(TEXTURE2D_PARAM(_SAOcclusionTexture, sampler_SAOcclusionTexture), i.texcoord, delta);
  335. CompositionOutput o;
  336. o.gbuffer0 = half4(0.0, 0.0, 0.0, ao);
  337. o.gbuffer3 = half4((half3)EncodeAO(ao) * _AOColor, 0.0);
  338. return o;
  339. }
  340. #else
  341. float4 FragCompositionGBuffer(VaryingsDefault i) : SV_Target
  342. {
  343. return (0.0).xxxx;
  344. }
  345. #endif
  346. float4 FragDebugOverlay(VaryingsDefault i) : SV_Target
  347. {
  348. float2 delta = _SAOcclusionTexture_TexelSize.xy / DOWNSAMPLE;
  349. half ao = BlurSmall(TEXTURE2D_PARAM(_SAOcclusionTexture, sampler_SAOcclusionTexture), i.texcoord, delta);
  350. ao = EncodeAO(ao);
  351. return float4(1.0 - ao.xxx, 1.0);
  352. }
  353. #endif // UNITY_POSTFX_AMBIENT_OCCLUSION