1
2 // This Ambient Occlusion image effect
is based on "Scalable Ambient Obscurance":
3
4 /**
5
6 \author Morgan McGuire and Michael Mara, NVIDIA and Williams College, http://research.nvidia.com, http://graphics.cs.williams.edu
7
8 Open Source under the
"BSD" license: http://www.opensource.org/licenses/bsd-license.php
9
10 Copyright (c)
2011-2012, NVIDIA
11 All rights reserved.
12
13 Redistribution and use
in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
14
15 Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
16 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.
17 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.
18
19 */

20
21 Shader
"Hidden/ScreenSpaceAmbientObscurance"
22 {
23     Properties {
24         _MainTex (
"Base (RGB)", 2D) = "white" {}
25     }
26
27     CGINCLUDE
28
29     #include
"UnityCG.cginc"
30
31     #ifdef SHADER_API_D3D11
32         #define NUM_SAMPLES (
15)
33     #
else
34         #define NUM_SAMPLES (
11)
35     #endif
36
37     #define FAR_PLANE_Z (
300.0)
38     #define NUM_SPIRAL_TURNS (
7)
39     #define bias (
0.01)
40
41     
float _Radius;
42     
float _Radius2; // _Radius * _Radius;
43     
float _Intensity;
44     float4 _ProjInfo;
45     float4x4 _ProjectionInv;
// ref only
46
47     sampler2D_float _CameraDepthTexture;
48     sampler2D _Rand;
49     sampler2D _AOTex;
50     sampler2D _MainTex;
51
52     float4 _MainTex_TexelSize;
53
54     
static const float gaussian[5] = { 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 }; // stddev = 2.0
55
56     float2 _Axis;
57
58     
/** Increase to make edges crisper. Decrease to reduce temporal flicker. */
59     #define EDGE_SHARPNESS (
1.0)
60
61     
float _BlurFilterDistance;
62     #define SCALE _BlurFilterDistance
63
64     
/** Filter _Radius in pixels. This will be multiplied by SCALE. */
65     #define R (
4)
66
67     
struct v2f
68     {
69         float4 pos : SV_POSITION;
70         float2 uv : TEXCOORD0;
71         float2 uv2 : TEXCOORD1;
72     };
73
74     v2f vert( appdata_img v )
75     {
76         v2f o;
77         o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
78         o.uv = v.texcoord.xy;
79         o.uv2 = v.texcoord.xy;
80         #
if UNITY_UV_STARTS_AT_TOP
81         
if (_MainTex_TexelSize.y < 0)
82             o.uv2.y =
1-o.uv2.y;
83         #endif
84         
return o;
85     }
86
87     float3 ReconstructCSPosition(float2 S,
float z)
88     {
89         
float linEyeZ = LinearEyeDepth(z);
90         
return float3(( ( S.xy * _MainTex_TexelSize.zw) * _ProjInfo.xy + _ProjInfo.zw) * linEyeZ, linEyeZ);
91         
92         
/*
93         //
for reference
94         float4 clipPos = float4(S*
2.0-1.0, (z*2-1), 1);
95         float4 viewPos;
96         viewPos.x = dot((float4)_ProjectionInv[
0], clipPos);
97         viewPos.y = dot((float4)_ProjectionInv[
1], clipPos);
98         viewPos.w = dot((float4)_ProjectionInv[
3], clipPos);
99         viewPos.z = z;
100         viewPos = viewPos/viewPos.w;
101         
return viewPos.xyz;
102         */

103     }
104
105     float3 ReconstructCSFaceNormal(float3 C) {
106         
return normalize(cross(ddy(C), ddx(C)));
107     }
108
109
110     
/** 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) */
111
112     float2 TapLocation(
int sampleNumber, float spinAngle, out float ssR){
113         
// Radius relative to ssR
114         
float alpha = float(sampleNumber + 0.5) * (1.0 / NUM_SAMPLES);
115         
float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle;
116
117         ssR = alpha;
118         
return float2(cos(angle), sin(angle));
119     }
120
121     
/** Used for packing Z into the GB channels */
122     
float CSZToKey(float z) {
123         
return saturate(z * (1.0 / FAR_PLANE_Z));
124     }
125
126     
/** Used for packing Z into the GB channels */
127     
void packKey(float key, out float2 p) {
128         
// Round to the nearest 1/256.0
129         
float temp = floor(key * 256.0);
130
131         
// Integer part
132         p.x = temp * (
1.0 / 256.0);
133
134         
// Fractional part
135         p.y = key *
256.0 - temp;
136     }
137
138     
/** Returns a number on (0, 1) */
139     
float UnpackKey(float2 p)
140     {
141         
return p.x * (256.0 / 257.0) + p.y * (1.0 / 257.0);
142     }
143
144
145     
/** Read the camera-space position of the point at screen-space pixel ssP */
146     float3 GetPosition(float2 ssP) {
147         float3 P;
148
149         P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy);
150
151         
// Offset to pixel center
152         P = ReconstructCSPosition(float2(ssP)
/*+ float2(0.5, 0.5)*/, P.z);
153         
return P;
154     }
155
156     
/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR. Assumes length(unitOffset) == 1 */
157     float3 GetOffsetPosition(float2 ssC, float2 unitOffset,
float ssR)
158     {
159         float2 ssP = saturate(float2(ssR*unitOffset) + ssC);
160
161         float3 P;
162         P.z = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, ssP.xy);
163
164         
// Offset to pixel center
165         P = ReconstructCSPosition(float2(ssP)
/* + float2(0.5, 0.5)*/, P.z);
166
167         
return P;
168     }
169
170     
/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
171     to camera-space point \a C with unit normal \a n_C,
using maximum screen-space sampling _Radius \a ssDiskRadius */
172     
173     
float SampleAO(in float2 ssC, in float3 C, in float3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle)
174     {
175         
// Offset on the unit disk, spun for this pixel
176         
float ssR;
177         float2 unitOffset = TapLocation(tapIndex, randomPatternRotationAngle, ssR);
178         ssR *= ssDiskRadius;
179
180         
// The occluding point in camera space
181         float3 Q = GetOffsetPosition(ssC, unitOffset, ssR);
182
183         float3 v = Q - C;
184
185         
float vv = dot(v, v);
186         
float vn = dot(v, n_C);
187
188         
const float epsilon = 0.01;
189         
float f = max(_Radius2 - vv, 0.0);
190         
return f * f * f * max((vn - bias) / (epsilon + vv), 0.0);
191     }
192
193     float4 fragAO(v2f i) : SV_Target
194     {
195         float4 fragment = fixed4(
1,1,1,1);
196
197         
// Pixel being shaded
198         float2 ssC = i.uv2.xy;
// * _MainTex_TexelSize.zw;
199
200         
// View space point being shaded
201         float3 C = GetPosition(ssC);
202
203         
//return abs(float4(C.xyz,0));
204         
//if(abs(C.z)<0.31)
205         
// return 1;
206         
//return abs(C.z);
207
208         packKey(CSZToKey(C.z), fragment.gb);
209         
//packKey(CSZToKey(C.z), bilateralKey);
210
211         
float randomPatternRotationAngle = 1.0;
212         #ifdef SHADER_API_D3D11
213             int2 ssCInt = ssC.xy * _MainTex_TexelSize.zw;
214             randomPatternRotationAngle = (
3 * ssCInt.x ^ ssCInt.y + ssCInt.x * ssCInt.y) * 10;
215         #
else
216             
// TODO: make dx9 rand better
217             randomPatternRotationAngle = tex2D(_Rand, i.uv*
12.0).x * 1000.0;
218         #endif
219
220         
// Reconstruct normals from positions. These will lead to 1-pixel black lines
221         
// at depth discontinuities, however the blur will wipe those out so they are not visible
222         
// in the final image.
223         float3 n_C = ReconstructCSFaceNormal(C);
224
225         
//return float4((n_C),0);
226
227         
// Choose the screen-space sample _Radius
228         
// proportional to the projected area of the sphere
229         
float ssDiskRadius = -_Radius / C.z; // -projScale * _Radius / C.z; // <:::::
230
231         
float sum = 0.0;
232         
for (int l = 0; l < NUM_SAMPLES; ++l) {
233              sum += SampleAO(ssC, C, n_C, (ssDiskRadius), l, randomPatternRotationAngle);
234         }
235
236         
float temp = _Radius2 * _Radius;
237         sum /= temp * temp;
238
239         
float A = max(0.0, 1.0 - sum * _Intensity * (5.0 / NUM_SAMPLES));
240         fragment.ra = float2(A,A);
241
242         
return fragment;
243     }
244
245     float4 fragUpsample (v2f i) : SV_Target
246     {
247         float4 fragment = fixed4(
1,1,1,1);
248
249         
// View space point being shaded
250         float3 C = GetPosition(i.uv.xy);
251
252         packKey(CSZToKey(C.z), fragment.gb);
253         fragment.ra = tex2D(_MainTex, i.uv.xy).ra;
254
255         
return fragment;
256     }
257
258     float4 fragApply (v2f i) : SV_Target
259     {
260         float4 ao = tex2D(_AOTex, i.uv2.xy);
261         
return tex2D(_MainTex, i.uv.xy) * ao.rrrr;
262     }
263
264     float4 fragApplySoft (v2f i) : SV_Target
265     {
266         float4 color = tex2D(_MainTex, i.uv.xy);
267
268         
float ao = tex2D(_AOTex, i.uv2.xy).r;
269         ao += tex2D(_AOTex, i.uv2.xy + _MainTex_TexelSize.xy *
0.75).r;
270         ao += tex2D(_AOTex, i.uv2.xy - _MainTex_TexelSize.xy *
0.75).r;
271         ao += tex2D(_AOTex, i.uv2.xy + _MainTex_TexelSize.xy * float2(-
0.75,0.75)).r;
272         ao += tex2D(_AOTex, i.uv2.xy - _MainTex_TexelSize.xy * float2(-
0.75,0.75)).r;
273
274         
return color * float4(ao,ao,ao,5)/5;
275     }
276
277     float4 fragBlurBL (v2f i) : SV_Target
278     {
279         float4 fragment = float4(
1,1,1,1);
280
281         float2 ssC = i.uv.xy;
282
283         float4 temp = tex2Dlod(_MainTex, float4(i.uv.xy,
0,0));
284
285         float2 passthrough2 = temp.gb;
286         
float key = UnpackKey(passthrough2);
287
288         
float sum = temp.r;
289
290         
/*
291         
if (key >= 0.999) {
292             // Sky pixel (
if you aren't using depth keying, disable this test)
293             fragment.gb = passthrough2;
294             
return fragment;
295         }
296         */

297
298         
// Base weight for depth falloff. Increase this for more blurriness, decrease it for better edge discrimination
299
300         
float BASE = gaussian[0] * 0.5; // ole: i decreased
301         
float totalWeight = BASE;
302         sum *= totalWeight;
303
304         
for (int r = -R; r <= R; ++r) {
305             
// We already handled the zero case above. This loop should be unrolled and the branch discarded
306             
if (r != 0) {
307                 temp = tex2Dlod(_MainTex, float4(ssC + _Axis * _MainTex_TexelSize.xy * (r * SCALE),
0,0) );
308                 
float tapKey = UnpackKey(temp.gb);
309                 
float value = temp.r;
310
311                 
// spatial domain: offset gaussian tap
312                 
float weight = 0.3 + gaussian[abs(r)];
313
314                 
// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
315                 weight *= max(
0.0, 1.0 - (2000.0 * EDGE_SHARPNESS) * abs(tapKey - key));
316
317                 sum +=
value * weight;
318                 totalWeight += weight;
319             }
320         }
321
322         
const float epsilon = 0.0001;
323         fragment = sum / (totalWeight + epsilon);
324         
325         fragment.gb = passthrough2;
326
327         
return fragment;
328     }
329
330     ENDCG
331
332 SubShader {
333
334     
// 0: get ao
335     Pass {
336         ZTest Always Cull Off ZWrite Off
337
338         CGPROGRAM
339
340         #pragma vertex vert
341         #pragma fragment fragAO
342         #pragma target
3.0
343         
344         ENDCG
345     }
346
347     
// 1: bilateral blur
348     Pass {
349         ZTest Always Cull Off ZWrite Off
350
351         CGPROGRAM
352
353         #pragma vertex vert
354         #pragma fragment fragBlurBL
355         #pragma target
3.0
356         
357         ENDCG
358     }
359
360     
// 2: apply ao
361     Pass {
362         ZTest Always Cull Off ZWrite Off
363
364         CGPROGRAM
365
366         #pragma vertex vert
367         #pragma fragment fragApply
368         #pragma target
3.0
369         
370         ENDCG
371     }
372
373     
// 3: apply with a slight box filter
374     Pass {
375         ZTest Always Cull Off ZWrite Off
376
377         CGPROGRAM
378
379         #pragma vertex vert
380         #pragma fragment fragApplySoft
381         #pragma target
3.0
382         
383         ENDCG
384     }
385
386     
// 4: in case you want to blur in high rez for nicer z borders
387     Pass {
388         ZTest Always Cull Off ZWrite Off
389
390         CGPROGRAM
391
392         #pragma vertex vert
393         #pragma fragment fragUpsample
394         #pragma target
3.0
395         
396         ENDCG
397     }
398 }
399
400 Fallback off
401
402 }


Gõ tìm kiếm nhanh...