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 ...

RevLine 
[2884]1#include "../shaderenv.h"
2
[3144]3
[2881]4////////////////////
5// Screen Spaced Ambient Occlusion shader
6// based on shader of Alexander Kusternig
7
[3144]8
[3106]9#define USE_EYESPACE_DEPTH 1
[3105]10
11
[2881]12struct fragment
13{
[2889]14        float2 texCoord: TEXCOORD0;
15        float3 view: TEXCOORD1;
[2881]16};
17
18
19struct pixel
20{
21        float4 illum_col: COLOR0;
22};
23
24
[3081]25inline float occlusionPower(float radius, float dist)
26{
27        return 6.283185307179586476925286766559f * (1.0f - cos(asin(radius / dist)));
28}
29
30
[3159]31inline float SqrLen(float3 v)
32{
33        return v.x * v.x + v.y * v.y + v.z * v.z;
34}
35
36
[2990]37inline float2 myreflect(float2 pt, float2 n)
[2881]38{
39        // distance to plane
40        float d = dot(n, pt);
41        // reflect around plane
42        float2 rpt = pt - d * 2.0f * n;
[2886]43
[2881]44        return rpt;
45}
46
47
[2990]48inline float3 Interpol(float2 w, float3 bl, float3 br, float3 tl, float3 tr)
[2986]49{
[2991]50        float3 x1 = lerp(bl, tl, w.y);
51        float3 x2 = lerp(br, tr, w.y);
52        float3 v = lerp(x1, x2, w.x);
[2987]53
54        return v;
55}
56
[2988]57
[2992]58// reconstruct world space position
[3155]59inline float3 ReconstructSamplePos(float eyeSpaceDepth,
[3017]60                                                                   float2 texcoord,
61                                                                   float3 bl, float3 br, float3 tl, float3 tr)
[2988]62{
[3097]63        float3 viewVec = Interpol(texcoord, bl, br, tl, tr);
[3017]64        float3 samplePos = -viewVec * eyeSpaceDepth;
65
[2999]66        return samplePos;
[2988]67}
68
69
[3087]70
[3115]71/** This shader computes the reprojection and stores
[3155]72        the ssao value of the old pixel as well as the
73        weight of the pixel in the new frame.
[3082]74*/
[3137]75inline float2 temporalSmoothing(float4 worldPos,
[3095]76                                                                float eyeSpaceDepth,
77                                                                float2 texcoord0,
78                                                                float3 oldEyePos,
[3113]79                                                                sampler2D oldTex,
80                                                                float4x4 oldModelViewProj,
81                                                                sampler2D colors,
[3112]82                                                                float3 projPos,
[3109]83                                                                float invW,
[3113]84                                                                float3 oldbl,
85                                                                float3 oldbr,
86                                                                float3 oldtl,
87                                                                float3 oldtr,
[3192]88                                                                float3 diffVec
[3109]89                                                                )
[3082]90{
[3113]91        // compute position from old frame for dynamic objects + translational portion
[3133]92        const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
[3111]93
[3082]94
[3109]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
[3115]99        float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
[3083]100        backProjPos /= backProjPos.w;
[3109]101       
[3082]102        // fit from unit cube into 0 .. 1
[3085]103        const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
[3082]104        // retrieve the sample from the last frame
[3095]105        const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
[3105]106
107#if USE_EYESPACE_DEPTH
[3111]108
[3095]109        // calculate eye space position of sample in old frame
110        const float oldEyeSpaceDepth = oldPixel.w;
[3082]111
[3095]112        // vector from eye pos to old sample
[3097]113        const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
[3109]114        const float invLen = 1.0f / length(viewVec);
[3115]115        const float projectedEyeSpaceDepth = invLen * length(translatedPos);
[3137]116        //const float projectedEyeSpaceDepth = length(translatedPos);
[3099]117       
[3109]118        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
[3106]119
[3105]120#else
[3117]121
[3105]122        // calculate eye space position of sample in old frame
123        const float oldDepth = oldPixel.w;
[3125]124        // the depth projected into the old frame
[3106]125        const float projectedDepth = projPos.z;
[3125]126        // calculate depth difference
[3105]127        const float depthDif = abs(projectedDepth - oldDepth);
[3117]128
[3105]129#endif
130
[3089]131        float newWeight;
132
[3192]133        if (1
[3085]134                && (oldTexCoords.x >= 0.0f) && (oldTexCoords.x < 1.0f)
135                && (oldTexCoords.y >= 0.0f) && (oldTexCoords.y < 1.0f)
[3103]136                && (depthDif <= MIN_DEPTH_DIFF)
[3082]137                )
138        {
139                // increase the weight for convergence
[3192]140                newWeight =  oldPixel.y;
[3082]141        }
142        else
143        {       
[3192]144                newWeight = 0.0f;
[3082]145        }
[3087]146
[3137]147        return float2(oldPixel.x, newWeight);
[3082]148}
149
150
[2881]151/** The ssao shader returning the an intensity value between 0 and 1
[3151]152        This version of the ssao shader uses the dotproduct between pixel and
153        sample normal as weight.
[2881]154*/
[3193]155float3 ssao2(fragment IN,
[3150]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;
[3203]176        float validSamples = .0f;
[3150]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;
[3155]196                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
197
198                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
[3159]199                // the normal of the current sample
[3167]200                //const float3 sampleNormal = normalize(tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz);
201                const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
[3150]202
203
204                ////////////////
205                //-- compute contribution of sample using the direction and angle
206
207                float3 dirSample = samplePos - centerPosition;
208
[3199]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
[3150]213                dirSample /= lengthToSample; // normalize
214
215                // angle between current normal and direction to sample controls AO intensity.
[3151]216                float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
[3155]217                // use binary decision to cull samples that are behind current shading point
218                cosAngle *= step(0.0f, dot(dirSample, normal));
[3150]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
[3199]222                //const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
223                const float aoContrib = SAMPLE_INTENSITY / sqrLen;
[3150]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
[3157]235                // check if the samples have been valid in the last frame
[3203]236                validSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
237
[3193]238                ++ numSamples;
[3150]239        }
240
[3193]241        total_ao /= numSamples;
242
[3203]243        return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
[3150]244}
245
246
[3151]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.
[3150]250*/
[3192]251float3 ssao(fragment IN,
[3117]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,
[3192]262                        float3 viewDir,
263                        float newWeight
[3083]264                        )
[2881]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
[3084]270        float total_ao = .0f;
[3192]271        float validSamples = .0f;
[3084]272        float numSamples = .0f;
[2881]273
274        for (int i = 0; i < NUM_SAMPLES; ++ i)
275        {
[2892]276                const float2 offset = samples[i];
[2881]277
[3175]278#if 1
[2881]279                ////////////////////
[3084]280                //-- add random noise: reflect around random normal vector (rather slow!)
[2985]281
[3150]282                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
[2892]283                const float2 offsetTransformed = myreflect(offset, mynoise);
[2903]284#else
285                const float2 offsetTransformed = offset;
286#endif
[2881]287                // weight with projected coordinate to reach similar kernel size for near and far
[3129]288                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
[3019]289                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
[2881]290
[3203]291                const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
[3155]292                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
[3150]293               
[2989]294
[3017]295                ////////////////
296                //-- compute contribution of sample using the direction and angle
[2881]297
[3017]298                float3 dirSample = samplePos - centerPosition;
[2999]299
[3198]300                const float sqrLen = max(SqrLen(dirSample), 1e-2f);
[3197]301                const float lengthToSample = sqrt(sqrLen);
302
[3095]303                dirSample /= lengthToSample; // normalize
304
[2885]305                // angle between current normal and direction to sample controls AO intensity.
[3151]306                const float cosAngle = max(dot(dirSample, normal), .0f);
[3197]307                const float aoContrib = SAMPLE_INTENSITY / sqrLen;
[3089]308                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
[2881]309
[3017]310#if 1
[2885]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!)
[3095]313
[3103]314                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
[3081]315                total_ao += cosAngle * aoContrib * viewCorrection;
[3017]316#else
[3098]317                total_ao += cosAngle * aoContrib;
[2911]318#endif
[3157]319
320                // check if the samples have been valid in the last frame
[3203]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;
[3192]324                //validSamples += sampleColor.x;
325
326                ++ numSamples;
[3203]327
328                //if ((validSamples < 1.0f) && (newWeight > 200) && (numSamples >= 8)) break;
329                if ((validSamples < 1.0f) && (numSamples >= 8)) break;
[2881]330        }
331
[3192]332        total_ao /= numSamples;
333
334        return float3(max(0.0f, 1.0f - total_ao), validSamples, numSamples);
[2881]335}
336
[3121]337
[3150]338
[2881]339/** The mrt shader for screen space ambient occlusion
340*/
341pixel main(fragment IN,
342                   uniform sampler2D colors,
343                   uniform sampler2D normals,
[3084]344                   uniform sampler2D noiseTex,
[2881]345                   uniform float2 samples[NUM_SAMPLES],
346                   uniform sampler2D oldTex,
[3085]347                   uniform float4x4 modelViewProj,
348                   uniform float4x4 oldModelViewProj,
[2985]349                   uniform float temporalCoherence,
[2986]350                   uniform float3 bl,
351                   uniform float3 br,
352                   uniform float3 tl,
[3085]353                   uniform float3 tr,
354                   uniform float3 oldEyePos,
355                   uniform float3 oldbl,
356                   uniform float3 oldbr,
357                   uniform float3 oldtl,
[3109]358                   uniform float3 oldtr,
[3150]359                   uniform sampler2D attribsTex
[2881]360                   )
361{
362        pixel OUT;
363
[3167]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;
[2975]366
[3082]367        // reconstruct position from the eye space depth
[3097]368        const float3 viewDir = IN.view;
[3089]369        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
[3097]370        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
[3014]371
[3121]372        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
373       
[3001]374
[3017]375        ////////////////
[3080]376        //-- calculcate the current projected posiion (also used for next frame)
[3017]377       
[3094]378        float4 projPos = mul(modelViewProj, eyeSpacePos);
[3112]379        const float invw = 1.0f / projPos.w;
380        projPos *= invw;
381        float scaleFactor = SAMPLE_RADIUS * invw;
[3121]382
[3017]383       
[3121]384        /////////////////
385        //-- compute temporal reprojection
386
[3137]387        float2 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
[3192]388                                                oldTex, oldModelViewProj,
[3121]389                                                                                        colors,
390                                                                                        projPos.xyz,
391                                                                                        invw,
392                                                                                        oldbl, oldbr, oldtl, oldtr,
[3192]393                                                                                        diffVec
[3129]394                                                                                        );
[3121]395
396        const float oldSsao = temporalVals.x;
[3192]397        float oldWeight = temporalVals.y;
398       
399        float3 ao;
[3137]400
[3192]401        // cull background note: this should be done with the stencil buffer
[3198]402        //if (SqrLen(diffVec < 1e6f) && (eyeSpaceDepth < 1e10f))
[3192]403        if (eyeSpaceDepth < 1e10f)
404        {
[3203]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);
[3192]407        }
408        else
409        {
[3198]410                 ao = float3(1.0f, 1.0f, 1.0f);
[3192]411        }
[3122]412
[3192]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);
[3137]426        OUT.illum_col.w = eyeSpaceDepth;
[3120]427
[2881]428        return OUT;
[3104]429}
Note: See TracBrowser for help on using the repository browser.