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

Revision 3203, 13.0 KB checked in by mattausch, 16 years ago (diff)

only adapting filter size left ...

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                                                                sampler2D colors,
82                                                                float3 projPos,
83                                                                float invW,
84                                                                float3 oldbl,
85                                                                float3 oldbr,
86                                                                float3 oldtl,
87                                                                float3 oldtr,
88                                                                float3 diffVec
89                                                                )
90{
91        // compute position from old frame for dynamic objects + translational portion
92        const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
93
94
95        /////////////////
96        //-- reproject into old frame and calculate texture position of sample in old frame
97
98        // note: the old model view matrix only holds the view orientation part
99        float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
100        backProjPos /= backProjPos.w;
101       
102        // fit from unit cube into 0 .. 1
103        const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
104        // retrieve the sample from the last frame
105        const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
106
107#if USE_EYESPACE_DEPTH
108
109        // calculate eye space position of sample in old frame
110        const float oldEyeSpaceDepth = oldPixel.w;
111
112        // vector from eye pos to old sample
113        const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
114        const float invLen = 1.0f / length(viewVec);
115        const float projectedEyeSpaceDepth = invLen * length(translatedPos);
116        //const float projectedEyeSpaceDepth = length(translatedPos);
117       
118        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
119
120#else
121
122        // calculate eye space position of sample in old frame
123        const float oldDepth = oldPixel.w;
124        // the depth projected into the old frame
125        const float projectedDepth = projPos.z;
126        // calculate depth difference
127        const float depthDif = abs(projectedDepth - oldDepth);
128
129#endif
130
131        float newWeight;
132
133        if (1
134                && (oldTexCoords.x >= 0.0f) && (oldTexCoords.x < 1.0f)
135                && (oldTexCoords.y >= 0.0f) && (oldTexCoords.y < 1.0f)
136                && (depthDif <= MIN_DEPTH_DIFF)
137                )
138        {
139                // increase the weight for convergence
140                newWeight =  oldPixel.y;
141        }
142        else
143        {       
144                newWeight = 0.0f;
145        }
146
147        return float2(oldPixel.x, newWeight);
148}
149
150
151/** The ssao shader returning the an intensity value between 0 and 1
152        This version of the ssao shader uses the dotproduct between pixel and
153        sample normal as weight.
154*/
155float3 ssao2(fragment IN,
156                         sampler2D colors,
157                         sampler2D noiseTex,
158                         float2 samples[NUM_SAMPLES],
159                         float3 normal,
160                         float3 centerPosition,
161                         float scaleFactor,
162                         float3 bl,
163                         float3 br,
164                         float3 tl,
165                         float3 tr,
166                         float3 viewDir,
167                         sampler2D normalTex
168                         )
169{
170        // Check in a circular area around the current position.
171        // Shoot vectors to the positions there, and check the angle to these positions.
172        // Summing up these angles gives an estimation of the occlusion at the current position.
173
174        float total_ao = .0f;
175        float numSamples = .0f;
176        float validSamples = .0f;
177
178        for (int i = 0; i < NUM_SAMPLES; ++ i)
179        {
180                const float2 offset = samples[i];
181
182#if 1
183                ////////////////////
184                //-- add random noise: reflect around random normal vector (rather slow!)
185
186                const float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
187                const float2 offsetTransformed = myreflect(offset, mynoise);
188#else
189                const float2 offsetTransformed = offset;
190#endif
191                // weight with projected coordinate to reach similar kernel size for near and far
192                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
193                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
194
195                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
196                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
197
198                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
199                // the normal of the current sample
200                //const float3 sampleNormal = normalize(tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz);
201                const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
202
203
204                ////////////////
205                //-- compute contribution of sample using the direction and angle
206
207                float3 dirSample = samplePos - centerPosition;
208
209                const float sqrLen = max(SqrLen(dirSample), 1e-2f);
210                const float lengthToSample = sqrt(sqrLen);
211                //const float lengthToSample = max(length(dirSample), 1e-6f);
212
213                dirSample /= lengthToSample; // normalize
214
215                // angle between current normal and direction to sample controls AO intensity.
216                float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
217                // use binary decision to cull samples that are behind current shading point
218                cosAngle *= step(0.0f, dot(dirSample, normal));
219       
220                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
221                // the distance to a sample approaches zero
222                //const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
223                const float aoContrib = SAMPLE_INTENSITY / sqrLen;
224                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
225
226#if 1
227                // if surface normal perpenticular to view dir, approx. half of the samples will not count
228                // => compensate for this (on the other hand, projected sampling area could be larger!)
229
230                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
231                total_ao += cosAngle * aoContrib * viewCorrection;
232#else
233                total_ao += cosAngle * aoContrib;
234#endif
235                // check if the samples have been valid in the last frame
236                validSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
237
238                ++ numSamples;
239        }
240
241        total_ao /= numSamples;
242
243        return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
244}
245
246
247/** The ssao shader returning the an intensity value between 0 and 1.
248        This version of the ssao shader uses the dotproduct between
249        pixel-to-sample direction and sample normal as weight.
250*/
251float3 ssao(fragment IN,
252                        sampler2D colors,
253                        sampler2D noiseTex,
254                        float2 samples[NUM_SAMPLES],
255                        float3 normal,
256                        float3 centerPosition,
257                        float scaleFactor,
258                        float3 bl,
259                        float3 br,
260                        float3 tl,
261                        float3 tr,
262                        float3 viewDir,
263                        float newWeight
264                        )
265{
266        // Check in a circular area around the current position.
267        // Shoot vectors to the positions there, and check the angle to these positions.
268        // Summing up these angles gives an estimation of the occlusion at the current position.
269
270        float total_ao = .0f;
271        float validSamples = .0f;
272        float numSamples = .0f;
273
274        for (int i = 0; i < NUM_SAMPLES; ++ i)
275        {
276                const float2 offset = samples[i];
277
278#if 1
279                ////////////////////
280                //-- add random noise: reflect around random normal vector (rather slow!)
281
282                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
283                const float2 offsetTransformed = myreflect(offset, mynoise);
284#else
285                const float2 offsetTransformed = offset;
286#endif
287                // weight with projected coordinate to reach similar kernel size for near and far
288                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
289                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
290
291                const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
292                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
293               
294
295                ////////////////
296                //-- compute contribution of sample using the direction and angle
297
298                float3 dirSample = samplePos - centerPosition;
299
300                const float sqrLen = max(SqrLen(dirSample), 1e-2f);
301                const float lengthToSample = sqrt(sqrLen);
302
303                dirSample /= lengthToSample; // normalize
304
305                // angle between current normal and direction to sample controls AO intensity.
306                const float cosAngle = max(dot(dirSample, normal), .0f);
307                const float aoContrib = SAMPLE_INTENSITY / sqrLen;
308                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
309
310#if 1
311                // if surface normal perpenticular to view dir, approx. half of the samples will not count
312                // => compensate for this (on the other hand, projected sampling area could be larger!)
313
314                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
315                total_ao += cosAngle * aoContrib * viewCorrection;
316#else
317                total_ao += cosAngle * aoContrib;
318#endif
319
320                // check if the samples have been valid in the last frame
321                // hack: the distance measure can fail in some cases => choose something different
322                const float tooFarAway = step(1.0f, lengthToSample);
323                validSamples += (1.0f - tooFarAway) * sampleColor.x;
324                //validSamples += sampleColor.x;
325
326                ++ numSamples;
327
328                //if ((validSamples < 1.0f) && (newWeight > 200) && (numSamples >= 8)) break;
329                if ((validSamples < 1.0f) && (numSamples >= 8)) break;
330        }
331
332        total_ao /= numSamples;
333
334        return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
335}
336
337
338
339/** The mrt shader for screen space ambient occlusion
340*/
341pixel main(fragment IN,
342                   uniform sampler2D colors,
343                   uniform sampler2D normals,
344                   uniform sampler2D noiseTex,
345                   uniform float2 samples[NUM_SAMPLES],
346                   uniform sampler2D oldTex,
347                   uniform float4x4 modelViewProj,
348                   uniform float4x4 oldModelViewProj,
349                   uniform float temporalCoherence,
350                   uniform float3 bl,
351                   uniform float3 br,
352                   uniform float3 tl,
353                   uniform float3 tr,
354                   uniform float3 oldEyePos,
355                   uniform float3 oldbl,
356                   uniform float3 oldbr,
357                   uniform float3 oldtl,
358                   uniform float3 oldtr,
359                   uniform sampler2D attribsTex
360                   )
361{
362        pixel OUT;
363
364        //const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
365        const float3 normal = tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz;
366
367        // reconstruct position from the eye space depth
368        const float3 viewDir = IN.view;
369        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
370        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
371
372        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
373       
374
375        ////////////////
376        //-- calculcate the current projected posiion (also used for next frame)
377       
378        float4 projPos = mul(modelViewProj, eyeSpacePos);
379        const float invw = 1.0f / projPos.w;
380        projPos *= invw;
381        float scaleFactor = SAMPLE_RADIUS * invw;
382
383       
384        /////////////////
385        //-- compute temporal reprojection
386
387        float2 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
388                                                oldTex, oldModelViewProj,
389                                                                                        colors,
390                                                                                        projPos.xyz,
391                                                                                        invw,
392                                                                                        oldbl, oldbr, oldtl, oldtr,
393                                                                                        diffVec
394                                                                                        );
395
396        const float oldSsao = temporalVals.x;
397        float oldWeight = temporalVals.y;
398       
399        float3 ao;
400
401        // cull background note: this should be done with the stencil buffer
402        //if (SqrLen(diffVec < 1e6f) && (eyeSpaceDepth < 1e10f))
403        if (eyeSpaceDepth < 1e10f)
404        {
405                ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), oldWeight);
406                //ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), normals);
407        }
408        else
409        {
410                 ao = float3(1.0f, 1.0f, 1.0f);
411        }
412
413        const float squaredLen = SqrLen(diffVec);
414       
415        if ((ao.y > 1.0f) && (squaredLen < DYNAMIC_OBJECTS_THRESHOLD))
416        {               
417                oldWeight = min(oldWeight, 4.0f * NUM_SAMPLES);
418        }
419
420        const float newWeight = ao.z;
421
422        // blend between old and new samples (and avoid division by zero)
423        OUT.illum_col.x = (ao.x * newWeight + oldSsao * oldWeight) / max(1e-6f, (newWeight + oldWeight));
424        OUT.illum_col.y = clamp(newWeight + oldWeight, .0f, temporalCoherence);
425        OUT.illum_col.z = SqrLen(diffVec);
426        OUT.illum_col.w = eyeSpaceDepth;
427
428        return OUT;
429}
Note: See TracBrowser for help on using the repository browser.