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

Revision 3316, 14.4 KB checked in by mattausch, 15 years ago (diff)

surpressed blur, worked on object placing in sibenik (why performance worse than vienna??)

Line 
1#include "../shaderenv.h"
2#include "common.h"
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// this function is inspired from the paper of shamulgaan in order
25// to get a physical expression for the occlusion culling
26inline float occlusionPower(float radius, float dist)
27{
28        return 6.283185307179586476925286766559f * (1.0f - cos(asin(radius / dist)));
29}
30
31
32
33// reconstruct world space position
34inline float3 ReconstructSamplePos(float eyeSpaceDepth,
35                                                                   float2 texcoord,
36                                                                   float3 bl, float3 br, float3 tl, float3 tr)
37{
38        float3 viewVec = Interpol(texcoord, bl, br, tl, tr);
39        float3 samplePos = -viewVec * eyeSpaceDepth;
40
41        return samplePos;
42}
43
44
45
46/** This shader computes the reprojection and stores
47        the ssao value of the old pixel as well as the
48        weight of the pixel in the new frame.
49*/
50inline float2 Reproject(float4 worldPos,
51                                                                float eyeSpaceDepth,
52                                                                float2 texcoord0,
53                                                                float3 oldEyePos,
54                                                                sampler2D oldTex,
55                                                                float4x4 oldModelViewProj,
56                                                                sampler2D colors,
57                                                                float3 projPos,
58                                                                float invW,
59                                                                float3 oldbl,
60                                                                float3 oldbr,
61                                                                float3 oldtl,
62                                                                float3 oldtr,
63                                                                float3 diffVec
64                                                                )
65{
66        // compute position from old frame for dynamic objects + translational portion
67        const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
68
69
70        /////////////////
71        //-- reproject into old frame and calculate texture position of sample in old frame
72
73        // note: the old model view matrix only holds the view orientation part
74        float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
75        backProjPos /= backProjPos.w;
76       
77        // fit from unit cube into 0 .. 1
78        const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
79        // retrieve the sample from the last frame
80        const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
81
82        // the ssao value in the old frame
83        const float ssao = oldPixel.x;
84
85        // calculate eye space position of sample in old frame
86        const float oldEyeSpaceDepth = oldPixel.w;
87
88        // vector from eye pos to old sample
89        const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
90        const float invLen = 1.0f / length(viewVec);
91        const float projectedEyeSpaceDepth = invLen * length(translatedPos);
92        //const float projectedEyeSpaceDepth = length(translatedPos);
93       
94        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
95
96        // the weight of the accumulated samples from the previous frames
97        float w;
98
99        //////////////
100        //-- reuse old value only if it was still valid in the old frame
101
102        if (1
103                && (oldTexCoords.x > 0) && (oldTexCoords.x < 1.0f)
104                && (oldTexCoords.y > 0) && (oldTexCoords.y < 1.0f)
105                && (depthDif <= MIN_DEPTH_DIFF)
106                )
107        {
108                // pixel valid => retrieve the convergence weight
109                float w1 = tex2Dlod(oldTex, float4(oldTexCoords + float2(0.5f / 1024.0f, 0), .0f, .0f)).y;
110                float w2 = tex2Dlod(oldTex, float4(oldTexCoords - float2(0.5f / 1024.0f, 0), .0f, .0f)).y;
111                float w3 = tex2Dlod(oldTex, float4(oldTexCoords + float2(0, 0.5f / 768.0f), .0f, .0f)).y;
112                float w4 = tex2Dlod(oldTex, float4(oldTexCoords - float2(0, 0.5f / 768.0f), .0f, .0f)).y;
113
114                w = min(min(w1, w2), min(w3, w4));
115                //w = oldPixel.y;
116        }
117        else
118        {       
119                w = 0.0f;
120        }
121
122        return float2(ssao, w);
123}
124
125
126/** The ssao shader returning the an intensity value between 0 and 1
127        This version of the ssao shader uses the dotproduct between pixel and
128        sample normal as weight.
129*/
130float3 ssao2(fragment IN,
131                         sampler2D colors,
132                         sampler2D noiseTex,
133                         float2 samples[NUM_SAMPLES],
134                         float3 normal,
135                         float3 centerPosition,
136                         float scaleFactor,
137                         float3 bl,
138                         float3 br,
139                         float3 tl,
140                         float3 tr,
141                         float3 viewDir,
142                         sampler2D normalTex,
143                         float sampleIntensity
144                         )
145{
146        float total_ao = .0f;
147        float numSamples = .0f;
148        float validSamples = .0f;
149
150        for (int i = 0; i < NUM_SAMPLES; ++ i)
151        {
152                const float2 offset = samples[i];
153
154#if 1
155                ////////////////////
156                //-- add random noise: reflect around random normal vector (rather slow!)
157
158                const float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
159                const float2 offsetTransformed = myreflect(offset, mynoise);
160#else
161                const float2 offsetTransformed = offset;
162#endif
163                // weight with projected coordinate to reach similar kernel size for near and far
164                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
165                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
166
167                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
168                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
169
170                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
171                // the normal of the current sample
172                const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
173
174
175                ////////////////
176                //-- compute contribution of sample using the direction and angle
177
178                float3 dirSample = samplePos - centerPosition;
179
180                const float sqrLen = max(SqrLen(dirSample), 1e-2f);
181                const float lengthToSample = sqrt(sqrLen);
182                //const float lengthToSample = max(length(dirSample), 1e-6f);
183
184                dirSample /= lengthToSample; // normalize
185
186                // angle between current normal and direction to sample controls AO intensity.
187                float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
188                // use binary decision to cull samples that are behind current shading point
189                cosAngle *= step(0.0f, dot(dirSample, normal));
190       
191                const float aoContrib = sampleIntensity / sqrLen;
192                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
193
194                total_ao += cosAngle * aoContrib;
195
196                // check if the samples have been valid in the last frame
197                validSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
198
199                ++ numSamples;
200        }
201
202        total_ao /= numSamples;
203
204#if 1
205        // if surface normal perpenticular to view dir, approx. half of the samples will not count
206        // => compensate for this (on the other hand, projected sampling area could be larger!)
207        const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
208        total_ao += cosAngle * aoContrib * viewCorrection;
209
210#endif
211
212        return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
213}
214
215
216/** The ssao shader returning the an intensity value between 0 and 1.
217        This version of the ssao shader uses the dotproduct between
218        pixel-to-sample direction and sample normal as weight.
219
220    The algorithm works like the following:
221        1) Check in a circular area around the current position.
222        2) Shoot vectors to the positions there, and check the angle to these positions.
223        3) Summing up these angles gives an estimation of the occlusion at the current position.
224*/
225float3 ssao(fragment IN,
226                        sampler2D colors,
227                        sampler2D noiseTex,
228                        float2 samples[NUM_SAMPLES],
229                        float3 normal,
230                        float3 centerPosition,
231                        float scaleFactor,
232                        float3 bl,
233                        float3 br,
234                        float3 tl,
235                        float3 tr,
236                        float3 viewDir,
237                        float convergence,
238                        float sampleIntensity,
239                        bool isMovingObject
240                        )
241{
242        float total_ao = .0f;
243        float validSamples = .0f;
244        float numSamples = .0f;
245
246        for (int i = 0; i < NUM_SAMPLES; ++ i)
247        {
248                float2 offset;
249
250                ////////////////////
251                //-- add random noise: reflect around random normal vector
252                //-- (affects performance for some reason!)
253
254                if (convergence < SSAO_CONVERGENCE_THRESHOLD)
255                {
256                        float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
257                        //offset = myreflect(samples[i], mynoise);
258                        offset = myrotate(samples[i], mynoise.x);
259                }
260                else
261                {
262                        offset = samples[i];
263                }
264               
265                // weight with projected coordinate to reach similar kernel size for near and far
266                const float2 texcoord = IN.texCoord.xy + offset * scaleFactor;
267
268                const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
269                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
270               
271
272                ////////////////
273                //-- compute contribution of sample using the direction and angle
274
275                float3 dirSample = samplePos - centerPosition;
276
277                //const float sqrLen = max(SqrLen(dirSample), 1e-2f);
278                //const float lengthToSample = sqrt(sqrLen);
279                const float lengthToSample =  max(length(dirSample), 1e-2f);
280
281                dirSample /= lengthToSample; // normalize
282
283                // angle between current normal and direction to sample controls AO intensity.
284                float cosAngle = dot(dirSample, normal);
285
286                //const float aoContrib = sampleIntensity / sqrLen;
287                const float aoContrib = sampleIntensity / lengthToSample;
288                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
289
290                total_ao += max(cosAngle, 0) * aoContrib;
291
292                ++ numSamples;
293
294                // check if the samples have been valid in the last frame
295                // only mark sample as invalid if in the last / current frame
296                // they possibly have any influence on the ao
297                const float changeFactor = sampleColor.y;
298                const float pixelValid = sampleColor.x;
299
300                // we check if the sample could have been near enough to the current pixel
301                // to have any influence in the current or last frame
302#if 0
303                const float tooFarAway = step(0.5f, lengthToSample - changeFactor);
304                validSamples = max(validSamples, (1.0f - tooFarAway) * pixelValid * step(-0.1f, cosAngle));
305#else
306                validSamples = max(validSamples, pixelValid);
307#endif
308
309#ifdef USE_GTX
310                // we can bail out early and use a minimal #samples)
311                // if some conditions are met as long as the hardware supports it
312                if (numSamples >= MIN_SAMPLES)
313                {
314                        //break;
315                        // if the pixel belongs to a static object and all the samples stay valid in the current frame
316                        if (!isMovingObject && (validSamples < 1.0f)) break;
317                        // if the pixel belongs to a dynamic object but the #accumulated samples for this pixel is sufficiently high
318                        // (=> there was no discontinuity recently)
319                        else if (isMovingObject && (convergence > NUM_SAMPLES * 5)) break;
320                }
321#endif
322        }
323
324        // "normalize" ao contribution
325        total_ao /= numSamples;
326
327#if 1
328        // if surface normal perpenticular to view dir, approx. half of the samples will not count
329        // => compensate for this (on the other hand, projected sampling area could be larger!)
330        const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
331        total_ao *= viewCorrection;
332#endif
333
334        //return float3(total_ao, validSamples, numSamples);
335        return float3(min(1.0f, total_ao), validSamples, numSamples);
336}
337
338
339
340/** The mrt shader for screen space ambient occlusion
341*/
342pixel main(fragment IN,
343                   uniform sampler2D colors,
344                   uniform sampler2D normals,
345                   uniform sampler2D noiseTex,
346                   uniform float2 samples[NUM_SAMPLES],
347                   uniform sampler2D oldTex,
348                   uniform float4x4 modelViewProj,
349                   uniform float4x4 oldModelViewProj,
350                   uniform float temporalCoherence,
351                   uniform float3 bl,
352                   uniform float3 br,
353                   uniform float3 tl,
354                   uniform float3 tr,
355                   uniform float3 oldEyePos,
356                   uniform float3 oldbl,
357                   uniform float3 oldbr,
358                   uniform float3 oldtl,
359                   uniform float3 oldtr,
360                   uniform sampler2D attribsTex,
361                   uniform float kernelRadius,
362                   uniform float sampleIntensity
363                   )
364{
365        pixel OUT;
366
367        //const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
368        const float3 normal = tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz;
369
370        // reconstruct position from the eye space depth
371        const float3 viewDir = IN.view;
372        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
373        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
374
375        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
376       
377
378        ////////////////
379        //-- calculcate the current projected posiion (also used for next frame)
380       
381        float4 projPos = mul(modelViewProj, eyeSpacePos);
382        const float invw = 1.0f / projPos.w;
383        projPos *= invw;
384        float scaleFactor = kernelRadius * invw;
385
386        const float sqrMoveSpeed = SqrLen(diffVec);
387        const bool isMovingObject = (sqrMoveSpeed > DYNAMIC_OBJECTS_THRESHOLD);
388
389       
390        /////////////////
391        //-- compute temporal reprojection
392
393        float2 temporalVals = Reproject(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
394                                        oldTex, oldModelViewProj,
395                                                                        colors,
396                                                                        projPos.xyz,
397                                                                        invw,
398                                                                        oldbl, oldbr, oldtl, oldtr,
399                                                                        diffVec
400                                                                        );
401
402        const float oldSsao = temporalVals.x;
403        float oldWeight = temporalVals.y;
404       
405        float3 ao;
406
407        // cull background note: this should be done with the stencil buffer
408        if (eyeSpaceDepth < DEPTH_THRESHOLD)
409        {
410                ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), oldWeight, sampleIntensity, isMovingObject);
411                //ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), normals, sampleIntensity);
412        }
413        else
414        {
415                 ao = float3(1.0f, 1.0f, 1.0f);
416        }
417
418       
419        ///////////
420        //-- check if we have to reset pixel because one of the sample points was invalid
421        //-- only do this if the current pixel does not belong to a moving object
422
423        // the weight equals the number of sampled shot in this pass
424        const float newWeight = ao.z;
425
426        // completely reset the ao in this pixel
427        const float completelyResetThres = 20.0f;
428        // don't fully reset the ao in this pixel, but give low weight to old solution
429        const float partlyResetThres = 1.0f;
430       
431        if (!isMovingObject)
432        {
433                if (ao.y > completelyResetThres)
434                {
435                        oldWeight = .0f;
436                }
437                else if (ao.y > partlyResetThres)
438                {
439                        oldWeight = min(oldWeight, 4.0f * newWeight);
440                        //oldWeight = .0f;
441                }
442        }
443
444        //////////
445        //-- blend ao between old and new samples (and avoid division by zero)
446
447        OUT.illum_col.x = (ao.x * newWeight + oldSsao * oldWeight);// / (newWeight + oldWeight);//max(1e-6f, newWeight + oldWeight);
448
449        OUT.illum_col.x /= (newWeight + oldWeight);
450
451        // the new weight for the next frame
452        const float combinedWeight = clamp(newWeight + oldWeight, .0f, temporalCoherence);
453
454        OUT.illum_col.y = combinedWeight;
455        // can be used to check if this pixel belongs to a moving object
456        OUT.illum_col.z = SqrLen(diffVec);
457        OUT.illum_col.w = eyeSpaceDepth;
458
459        //OUT.illum_col.yzw = diffVec;
460
461        return OUT;
462}
Note: See TracBrowser for help on using the repository browser.