source: GTP/trunk/App/Demos/Vis/FriendlyCulling/src/shaders/ssao.cg @ 3167

Revision 3167, 12.7 KB checked in by mattausch, 16 years ago (diff)
Line 
1#include "../shaderenv.h"
2
3
4////////////////////
5// Screen Spaced Ambient Occlusion shader
6// based on shader of Alexander Kusternig
7
8
9#define USE_EYESPACE_DEPTH 1
10
11
12struct fragment
13{
14        float2 texCoord: TEXCOORD0;
15        float3 view: TEXCOORD1;
16};
17
18
19struct pixel
20{
21        float4 illum_col: COLOR0;
22};
23
24
25inline float occlusionPower(float radius, float dist)
26{
27        return 6.283185307179586476925286766559f * (1.0f - cos(asin(radius / dist)));
28}
29
30
31inline float SqrLen(float3 v)
32{
33        return v.x * v.x + v.y * v.y + v.z * v.z;
34}
35
36
37inline float2 myreflect(float2 pt, float2 n)
38{
39        // distance to plane
40        float d = dot(n, pt);
41        // reflect around plane
42        float2 rpt = pt - d * 2.0f * n;
43
44        return rpt;
45}
46
47
48inline float3 Interpol(float2 w, float3 bl, float3 br, float3 tl, float3 tr)
49{
50        float3 x1 = lerp(bl, tl, w.y);
51        float3 x2 = lerp(br, tr, w.y);
52        float3 v = lerp(x1, x2, w.x);
53
54        return v;
55}
56
57
58// reconstruct world space position
59inline float3 ReconstructSamplePos(float eyeSpaceDepth,
60                                                                   float2 texcoord,
61                                                                   float3 bl, float3 br, float3 tl, float3 tr)
62{
63        float3 viewVec = Interpol(texcoord, bl, br, tl, tr);
64        float3 samplePos = -viewVec * eyeSpaceDepth;
65
66        return samplePos;
67}
68
69
70
71/** This shader computes the reprojection and stores
72        the ssao value of the old pixel as well as the
73        weight of the pixel in the new frame.
74*/
75inline float2 temporalSmoothing(float4 worldPos,
76                                                                float eyeSpaceDepth,
77                                                                float2 texcoord0,
78                                                                float3 oldEyePos,
79                                                                sampler2D oldTex,
80                                                                float4x4 oldModelViewProj,
81                                                                float temporalCoherence,
82                                                                sampler2D colors,
83                                                                float3 projPos,
84                                                                float invW,
85                                                                float3 oldbl,
86                                                                float3 oldbr,
87                                                                float3 oldtl,
88                                                                float3 oldtr,
89                                                                float3 diffVec,
90                                                                float pixelValid
91                                                                )
92{
93        // compute position from old frame for dynamic objects + translational portion
94        const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
95
96
97        /////////////////
98        //-- reproject into old frame and calculate texture position of sample in old frame
99
100        // note: the old model view matrix only holds the view orientation part
101        float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
102        backProjPos /= backProjPos.w;
103       
104        // fit from unit cube into 0 .. 1
105        const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
106        // retrieve the sample from the last frame
107        const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
108
109#if USE_EYESPACE_DEPTH
110
111        // calculate eye space position of sample in old frame
112        const float oldEyeSpaceDepth = oldPixel.w;
113
114        // vector from eye pos to old sample
115        const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
116        const float invLen = 1.0f / length(viewVec);
117        const float projectedEyeSpaceDepth = invLen * length(translatedPos);
118        //const float projectedEyeSpaceDepth = length(translatedPos);
119       
120        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
121
122#else
123
124        // calculate eye space position of sample in old frame
125        const float oldDepth = oldPixel.w;
126        // the depth projected into the old frame
127        const float projectedDepth = projPos.z;
128        // calculate depth difference
129        const float depthDif = abs(projectedDepth - oldDepth);
130
131#endif
132
133        const float squaredLen = SqrLen(diffVec);
134
135        const float oldWeight = clamp(oldPixel.y, .0f, temporalCoherence);
136        //const float oldWeight = oldPixel.y;
137
138        float newWeight;
139
140        if ((temporalCoherence > 1e-6f)
141                && (oldTexCoords.x >= 0.0f) && (oldTexCoords.x < 1.0f)
142                && (oldTexCoords.y >= 0.0f) && (oldTexCoords.y < 1.0f)
143                && (depthDif <= MIN_DEPTH_DIFF)
144                // if visibility changed in the surrounding area we have to recompute
145                //&& (oldNumSamples > 0.8f * newNumSamples)
146                //&& (pixelValid < 1.0f)
147                )
148        {
149                // increase the weight for convergence
150                newWeight = oldWeight + 1.0f;
151               
152                if ((pixelValid > 1.0f) && (squaredLen < DYNAMIC_OBJECTS_THRESHOLD))
153                {               
154                        newWeight = 4.0f;
155                        //newWeight = 1.0f;
156                }
157        }
158        else
159        {       
160                newWeight = 1.0f;
161        }
162
163        return float2(oldPixel.x, newWeight);
164}
165
166
167/** The ssao shader returning the an intensity value between 0 and 1
168        This version of the ssao shader uses the dotproduct between pixel and
169        sample normal as weight.
170*/
171float2 ssao2(fragment IN,
172                         sampler2D colors,
173                         sampler2D noiseTex,
174                         float2 samples[NUM_SAMPLES],
175                         float3 normal,
176                         float3 centerPosition,
177                         float scaleFactor,
178                         float3 bl,
179                         float3 br,
180                         float3 tl,
181                         float3 tr,
182                         float3 viewDir,
183                         sampler2D normalTex
184                         )
185{
186        // Check in a circular area around the current position.
187        // Shoot vectors to the positions there, and check the angle to these positions.
188        // Summing up these angles gives an estimation of the occlusion at the current position.
189
190        float total_ao = .0f;
191        float numSamples = .0f;
192
193        for (int i = 0; i < NUM_SAMPLES; ++ i)
194        {
195                const float2 offset = samples[i];
196
197#if 1
198                ////////////////////
199                //-- add random noise: reflect around random normal vector (rather slow!)
200
201                const float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
202                const float2 offsetTransformed = myreflect(offset, mynoise);
203#else
204                const float2 offsetTransformed = offset;
205#endif
206                // weight with projected coordinate to reach similar kernel size for near and far
207                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
208                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
209
210                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
211                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
212
213                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
214                // the normal of the current sample
215                //const float3 sampleNormal = normalize(tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz);
216                const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
217
218
219                ////////////////
220                //-- compute contribution of sample using the direction and angle
221
222                float3 dirSample = samplePos - centerPosition;
223                const float lengthToSample = max(length(dirSample), 1e-6f);
224
225                dirSample /= lengthToSample; // normalize
226
227                // angle between current normal and direction to sample controls AO intensity.
228                float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
229                // use binary decision to cull samples that are behind current shading point
230                cosAngle *= step(0.0f, dot(dirSample, normal));
231       
232                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
233                // the distance to a sample approaches zero
234                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
235                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
236
237#if 1
238                // if surface normal perpenticular to view dir, approx. half of the samples will not count
239                // => compensate for this (on the other hand, projected sampling area could be larger!)
240
241                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
242                total_ao += cosAngle * aoContrib * viewCorrection;
243#else
244                total_ao += cosAngle * aoContrib;
245#endif
246                // check if the samples have been valid in the last frame
247                numSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
248        }
249
250        return float2(max(0.0f, 1.0f - total_ao), numSamples);
251}
252
253
254/** The ssao shader returning the an intensity value between 0 and 1.
255        This version of the ssao shader uses the dotproduct between
256        pixel-to-sample direction and sample normal as weight.
257*/
258float2 ssao(fragment IN,
259                        sampler2D colors,
260                        sampler2D noiseTex,
261                        float2 samples[NUM_SAMPLES],
262                        float3 normal,
263                        float3 centerPosition,
264                        float scaleFactor,
265                        float3 bl,
266                        float3 br,
267                        float3 tl,
268                        float3 tr,
269                        float3 viewDir
270                        )
271{
272        // Check in a circular area around the current position.
273        // Shoot vectors to the positions there, and check the angle to these positions.
274        // Summing up these angles gives an estimation of the occlusion at the current position.
275
276        float total_ao = .0f;
277        float numSamples = .0f;
278
279        for (int i = 0; i < NUM_SAMPLES; ++ i)
280        {
281                const float2 offset = samples[i];
282
283#if 1
284                ////////////////////
285                //-- add random noise: reflect around random normal vector (rather slow!)
286
287                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
288                const float2 offsetTransformed = myreflect(offset, mynoise);
289#else
290                const float2 offsetTransformed = offset;
291#endif
292                // weight with projected coordinate to reach similar kernel size for near and far
293                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
294                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
295
296                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
297                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
298               
299
300                ////////////////
301                //-- compute contribution of sample using the direction and angle
302
303                float3 dirSample = samplePos - centerPosition;
304                const float lengthToSample = max(length(dirSample), 1e-6f);
305
306                dirSample /= lengthToSample; // normalize
307
308                // angle between current normal and direction to sample controls AO intensity.
309                const float cosAngle = max(dot(dirSample, normal), .0f);
310       
311                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
312                // the distance to a sample approaches zero
313                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
314                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
315
316#if 1
317                // if surface normal perpenticular to view dir, approx. half of the samples will not count
318                // => compensate for this (on the other hand, projected sampling area could be larger!)
319
320                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
321                total_ao += cosAngle * aoContrib * viewCorrection;
322#else
323                total_ao += cosAngle * aoContrib;
324#endif
325
326                // check if the samples have been valid in the last frame
327                numSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
328        }
329
330        return float2(max(0.0f, 1.0f - total_ao), numSamples);
331}
332
333
334
335/** The mrt shader for screen space ambient occlusion
336*/
337pixel main(fragment IN,
338                   uniform sampler2D colors,
339                   uniform sampler2D normals,
340                   uniform sampler2D noiseTex,
341                   uniform float2 samples[NUM_SAMPLES],
342                   uniform sampler2D oldTex,
343                   uniform float4x4 modelViewProj,
344                   uniform float4x4 oldModelViewProj,
345                   uniform float temporalCoherence,
346                   uniform float3 bl,
347                   uniform float3 br,
348                   uniform float3 tl,
349                   uniform float3 tr,
350                   uniform float3 oldEyePos,
351                   uniform float3 oldbl,
352                   uniform float3 oldbr,
353                   uniform float3 oldtl,
354                   uniform float3 oldtr,
355                   uniform sampler2D attribsTex
356                   )
357{
358        pixel OUT;
359
360        //const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
361        const float3 normal = tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz;
362
363        // reconstruct position from the eye space depth
364        const float3 viewDir = IN.view;
365        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
366        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
367
368        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
369       
370
371        ////////////////
372        //-- calculcate the current projected posiion (also used for next frame)
373       
374        float4 projPos = mul(modelViewProj, eyeSpacePos);
375        const float invw = 1.0f / projPos.w;
376        projPos *= invw;
377        float scaleFactor = SAMPLE_RADIUS * invw;
378
379       
380        float2 ao;
381
382        // note: this should be done with the stencil buffer
383        if (eyeSpaceDepth < 1e10f)
384        {
385                ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir));
386                //ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), normals);
387        }
388        else
389        {
390                 ao = float2(1.0f, 0);
391        }
392
393
394        /////////////////
395        //-- compute temporal reprojection
396
397        float2 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
398                                                oldTex, oldModelViewProj, temporalCoherence,
399                                                                                        colors,
400                                                                                        projPos.xyz,
401                                                                                        invw,
402                                                                                        oldbl, oldbr, oldtl, oldtr,
403                                                                                        diffVec,
404                                                                                        ao.y
405                                                                                        );
406
407        const float oldSsao = temporalVals.x;
408        const float newWeight = temporalVals.y;
409
410
411        OUT.illum_col.x = (ao.x + oldSsao * (newWeight - 1.0f)) / newWeight;
412        OUT.illum_col.y = newWeight;
413        OUT.illum_col.z = SqrLen(diffVec);//invw;
414        OUT.illum_col.w = eyeSpaceDepth;
415
416        return OUT;
417}
Note: See TracBrowser for help on using the repository browser.