ScreenSpaceAmbientObscurance.shader 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
  2. // This Ambient Occlusion image effect is based on "Scalable Ambient Obscurance":
  3. /**
  4. \author Morgan McGuire and Michael Mara, NVIDIA and Williams College, http://research.nvidia.com, http://graphics.cs.williams.edu
  5. Open Source under the "BSD" license: http://www.opensource.org/licenses/bsd-license.php
  6. Copyright (c) 2011-2012, NVIDIA
  7. All rights reserved.
  8. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  9. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  10. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  11. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  12. */
  13. Shader "Hidden/ScreenSpaceAmbientObscurance"
  14. {
  15. Properties {
  16. _MainTex ("Base (RGB)", 2D) = "white" {}
  17. }
  18. CGINCLUDE
  19. #include "UnityCG.cginc"
  20. #ifdef SHADER_API_D3D11
  21. #define NUM_SAMPLES (15)
  22. #else
  23. #define NUM_SAMPLES (11)
  24. #endif
  25. #define FAR_PLANE_Z (300.0)
  26. #define NUM_SPIRAL_TURNS (7)
  27. #define bias (0.01)
  28. float _Radius;
  29. float _Radius2; // _Radius * _Radius;
  30. float _Intensity;
  31. float4 _ProjInfo;
  32. float4x4 _ProjectionInv; // ref only
  33. sampler2D_float _CameraDepthTexture;
  34. sampler2D _Rand;
  35. sampler2D _AOTex;
  36. sampler2D _MainTex;
  37. float4 _MainTex_TexelSize;
  38. static const float gaussian[5] = { 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 }; // stddev = 2.0
  39. float2 _Axis;
  40. /** Increase to make edges crisper. Decrease to reduce temporal flicker. */
  41. #define EDGE_SHARPNESS (1.0)
  42. float _BlurFilterDistance;
  43. #define SCALE _BlurFilterDistance
  44. /** Filter _Radius in pixels. This will be multiplied by SCALE. */
  45. #define R (4)
  46. struct v2f
  47. {
  48. float4 pos : SV_POSITION;
  49. float2 uv : TEXCOORD0;
  50. float2 uv2 : TEXCOORD1;
  51. };
  52. v2f vert( appdata_img v )
  53. {
  54. v2f o;
  55. o.pos = UnityObjectToClipPos(v.vertex);
  56. o.uv = v.texcoord.xy;
  57. o.uv2 = v.texcoord.xy;
  58. #if UNITY_UV_STARTS_AT_TOP
  59. if (_MainTex_TexelSize.y < 0)
  60. o.uv2.y = 1-o.uv2.y;
  61. #endif
  62. return o;
  63. }
  64. float3 ReconstructCSPosition(float2 S, float z)
  65. {
  66. float linEyeZ = LinearEyeDepth(z);
  67. return float3(( ( S.xy * _MainTex_TexelSize.zw) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ);
  68. /*
  69. // for reference
  70. float4 clipPos = float4(S*2.0-1.0, (z*2-1), 1);
  71. float4 viewPos;
  72. viewPos.x = dot((float4)_ProjectionInv[0], clipPos);
  73. viewPos.y = dot((float4)_ProjectionInv[1], clipPos);
  74. viewPos.w = dot((float4)_ProjectionInv[3], clipPos);
  75. viewPos.z = z;
  76. viewPos = viewPos/viewPos.w;
  77. return viewPos.xyz;
  78. */
  79. }
  80. float3 ReconstructCSFaceNormal(float3 C) {
  81. return normalize(cross(ddy(C), ddx(C)));
  82. }
  83. /** Returns a unit vector and a screen-space _Radius for the tap on a unit disk (the caller should scale by the actual disk _Radius) */
  84. float2 TapLocation(int sampleNumber, float spinAngle, out float ssR){
  85. // Radius relative to ssR
  86. float alpha = float(sampleNumber + 0.5) * (1.0 / NUM_SAMPLES);
  87. float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle;
  88. ssR = alpha;
  89. return float2(cos(angle), sin(angle));
  90. }
  91. /** Used for packing Z into the GB channels */
  92. float CSZToKey(float z) {
  93. return saturate(z * (1.0 / FAR_PLANE_Z));
  94. }
  95. /** Used for packing Z into the GB channels */
  96. void packKey(float key, out float2 p) {
  97. // Round to the nearest 1/256.0
  98. float temp = floor(key * 256.0);
  99. // Integer part
  100. p.x = temp * (1.0 / 256.0);
  101. // Fractional part
  102. p.y = key * 256.0 - temp;
  103. }
  104. /** Returns a number on (0, 1) */
  105. float UnpackKey(float2 p)
  106. {
  107. return p.x * (256.0 / 257.0) + p.y * (1.0 / 257.0);
  108. }
  109. /** Read the camera-space position of the point at screen-space pixel ssP */
  110. float3 GetPosition(float2 ssP) {
  111. float3 P;
  112. P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy);
  113. // Offset to pixel center
  114. P = ReconstructCSPosition(float2(ssP) /*+ float2(0.5, 0.5)*/, P.z);
  115. return P;
  116. }
  117. /** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */
  118. float3 GetOffsetPosition(float2 ssC, float2 unitOffset, float ssR)
  119. {
  120. float2 ssP = saturate(float2(ssR*unitOffset) + ssC);
  121. float3 P;
  122. P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy);
  123. // Offset to pixel center
  124. P = ReconstructCSPosition(float2(ssP)/* + float2(0.5, 0.5)*/, P.z);
  125. return P;
  126. }
  127. /** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
  128. to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling _Radius \a ssDiskRadius */
  129. float SampleAO(in float2 ssC, in float3 C, in float3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle)
  130. {
  131. // Offset on the unit disk, spun for this pixel
  132. float ssR;
  133. float2 unitOffset = TapLocation(tapIndex, randomPatternRotationAngle, ssR);
  134. ssR *= ssDiskRadius;
  135. // The occluding point in camera space
  136. float3 Q = GetOffsetPosition(ssC, unitOffset, ssR);
  137. float3 v = Q - C;
  138. float vv = dot(v, v);
  139. float vn = dot(v, n_C);
  140. const float epsilon = 0.01;
  141. float f = max(_Radius2 - vv, 0.0);
  142. return f * f * f * max((vn - bias) / (epsilon + vv), 0.0);
  143. }
  144. float4 fragAO(v2f i) : SV_Target
  145. {
  146. float4 fragment = fixed4(1,1,1,1);
  147. // Pixel being shaded
  148. float2 ssC = i.uv2.xy;// * _MainTex_TexelSize.zw;
  149. // View space point being shaded
  150. float3 C = GetPosition(ssC);
  151. //return abs(float4(C.xyz,0));
  152. //if(abs(C.z)<0.31)
  153. // return 1;
  154. //return abs(C.z);
  155. packKey(CSZToKey(C.z), fragment.gb);
  156. //packKey(CSZToKey(C.z), bilateralKey);
  157. float randomPatternRotationAngle = 1.0;
  158. #ifdef SHADER_API_D3D11
  159. int2 ssCInt = ssC.xy * _MainTex_TexelSize.zw;
  160. randomPatternRotationAngle = (3 * ssCInt.x ^ ssCInt.y + ssCInt.x * ssCInt.y) * 10;
  161. #else
  162. // TODO: make dx9 rand better
  163. randomPatternRotationAngle = tex2D(_Rand, i.uv*12.0).x * 1000.0;
  164. #endif
  165. // Reconstruct normals from positions. These will lead to 1-pixel black lines
  166. // at depth discontinuities, however the blur will wipe those out so they are not visible
  167. // in the final image.
  168. float3 n_C = ReconstructCSFaceNormal(C);
  169. //return float4((n_C),0);
  170. // Choose the screen-space sample _Radius
  171. // proportional to the projected area of the sphere
  172. float ssDiskRadius = -_Radius / C.z; // -projScale * _Radius / C.z; // <:::::
  173. float sum = 0.0;
  174. for (int l = 0; l < NUM_SAMPLES; ++l) {
  175. sum += SampleAO(ssC, C, n_C, (ssDiskRadius), l, randomPatternRotationAngle);
  176. }
  177. float temp = _Radius2 * _Radius;
  178. sum /= temp * temp;
  179. float A = max(0.0, 1.0 - sum * _Intensity * (5.0 / NUM_SAMPLES));
  180. fragment.ra = float2(A,A);
  181. return fragment;
  182. }
  183. float4 fragUpsample (v2f i) : SV_Target
  184. {
  185. float4 fragment = fixed4(1,1,1,1);
  186. // View space point being shaded
  187. float3 C = GetPosition(i.uv.xy);
  188. packKey(CSZToKey(C.z), fragment.gb);
  189. fragment.ra = tex2D(_MainTex, i.uv.xy).ra;
  190. return fragment;
  191. }
  192. float4 fragApply (v2f i) : SV_Target
  193. {
  194. float4 ao = tex2D(_AOTex, i.uv2.xy);
  195. return tex2D(_MainTex, i.uv.xy) * ao.rrrr;
  196. }
  197. float4 fragApplySoft (v2f i) : SV_Target
  198. {
  199. float4 color = tex2D(_MainTex, i.uv.xy);
  200. float ao = tex2D(_AOTex, i.uv2.xy).r;
  201. ao += tex2D(_AOTex, i.uv2.xy + _MainTex_TexelSize.xy * 0.75).r;
  202. ao += tex2D(_AOTex, i.uv2.xy - _MainTex_TexelSize.xy * 0.75).r;
  203. ao += tex2D(_AOTex, i.uv2.xy + _MainTex_TexelSize.xy * float2(-0.75,0.75)).r;
  204. ao += tex2D(_AOTex, i.uv2.xy - _MainTex_TexelSize.xy * float2(-0.75,0.75)).r;
  205. return color * float4(ao,ao,ao,5)/5;
  206. }
  207. float4 fragBlurBL (v2f i) : SV_Target
  208. {
  209. float4 fragment = float4(1,1,1,1);
  210. float2 ssC = i.uv.xy;
  211. float4 temp = tex2Dlod(_MainTex, float4(i.uv.xy,0,0));
  212. float2 passthrough2 = temp.gb;
  213. float key = UnpackKey(passthrough2);
  214. float sum = temp.r;
  215. /*
  216. if (key >= 0.999) {
  217. // Sky pixel (if you aren't using depth keying, disable this test)
  218. fragment.gb = passthrough2;
  219. return fragment;
  220. }
  221. */
  222. // Base weight for depth falloff. Increase this for more blurriness, decrease it for better edge discrimination
  223. float BASE = gaussian[0] * 0.5; // ole: i decreased
  224. float totalWeight = BASE;
  225. sum *= totalWeight;
  226. for (int r = -R; r <= R; ++r) {
  227. // We already handled the zero case above. This loop should be unrolled and the branch discarded
  228. if (r != 0) {
  229. temp = tex2Dlod(_MainTex, float4(ssC + _Axis * _MainTex_TexelSize.xy * (r * SCALE),0,0) );
  230. float tapKey = UnpackKey(temp.gb);
  231. float value = temp.r;
  232. // spatial domain: offset gaussian tap
  233. float weight = 0.3 + gaussian[abs(r)];
  234. // range domain (the "bilateral" weight). As depth difference increases, decrease weight.
  235. weight *= max(0.0, 1.0 - (2000.0 * EDGE_SHARPNESS) * abs(tapKey - key));
  236. sum += value * weight;
  237. totalWeight += weight;
  238. }
  239. }
  240. const float epsilon = 0.0001;
  241. fragment = sum / (totalWeight + epsilon);
  242. fragment.gb = passthrough2;
  243. return fragment;
  244. }
  245. ENDCG
  246. SubShader {
  247. // 0: get ao
  248. Pass {
  249. ZTest Always Cull Off ZWrite Off
  250. CGPROGRAM
  251. #pragma vertex vert
  252. #pragma fragment fragAO
  253. #pragma target 3.0
  254. ENDCG
  255. }
  256. // 1: bilateral blur
  257. Pass {
  258. ZTest Always Cull Off ZWrite Off
  259. CGPROGRAM
  260. #pragma vertex vert
  261. #pragma fragment fragBlurBL
  262. #pragma target 3.0
  263. ENDCG
  264. }
  265. // 2: apply ao
  266. Pass {
  267. ZTest Always Cull Off ZWrite Off
  268. CGPROGRAM
  269. #pragma vertex vert
  270. #pragma fragment fragApply
  271. #pragma target 3.0
  272. ENDCG
  273. }
  274. // 3: apply with a slight box filter
  275. Pass {
  276. ZTest Always Cull Off ZWrite Off
  277. CGPROGRAM
  278. #pragma vertex vert
  279. #pragma fragment fragApplySoft
  280. #pragma target 3.0
  281. ENDCG
  282. }
  283. // 4: in case you want to blur in high rez for nicer z borders
  284. Pass {
  285. ZTest Always Cull Off ZWrite Off
  286. CGPROGRAM
  287. #pragma vertex vert
  288. #pragma fragment fragUpsample
  289. #pragma target 3.0
  290. ENDCG
  291. }
  292. }
  293. Fallback off
  294. }