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

Revision 3175, 12.7 KB checked in by mattausch, 16 years ago (diff)
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                                                                float temporalCoherence,
82                                                                sampler2D colors,
[3112]83                                                                float3 projPos,
[3109]84                                                                float invW,
[3113]85                                                                float3 oldbl,
86                                                                float3 oldbr,
87                                                                float3 oldtl,
88                                                                float3 oldtr,
[3155]89                                                                float3 diffVec,
90                                                                float pixelValid
[3109]91                                                                )
[3082]92{
[3113]93        // compute position from old frame for dynamic objects + translational portion
[3133]94        const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
[3111]95
[3082]96
[3109]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
[3115]101        float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
[3083]102        backProjPos /= backProjPos.w;
[3109]103       
[3082]104        // fit from unit cube into 0 .. 1
[3085]105        const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
[3082]106        // retrieve the sample from the last frame
[3095]107        const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
[3105]108
109#if USE_EYESPACE_DEPTH
[3111]110
[3095]111        // calculate eye space position of sample in old frame
112        const float oldEyeSpaceDepth = oldPixel.w;
[3082]113
[3095]114        // vector from eye pos to old sample
[3097]115        const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
[3109]116        const float invLen = 1.0f / length(viewVec);
[3115]117        const float projectedEyeSpaceDepth = invLen * length(translatedPos);
[3137]118        //const float projectedEyeSpaceDepth = length(translatedPos);
[3099]119       
[3109]120        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
[3106]121
[3105]122#else
[3117]123
[3105]124        // calculate eye space position of sample in old frame
125        const float oldDepth = oldPixel.w;
[3125]126        // the depth projected into the old frame
[3106]127        const float projectedDepth = projPos.z;
[3125]128        // calculate depth difference
[3105]129        const float depthDif = abs(projectedDepth - oldDepth);
[3117]130
[3105]131#endif
132
[3159]133        const float squaredLen = SqrLen(diffVec);
134
[3132]135        const float oldWeight = clamp(oldPixel.y, .0f, temporalCoherence);
136        //const float oldWeight = oldPixel.y;
[3121]137
[3089]138        float newWeight;
139
[3084]140        if ((temporalCoherence > 1e-6f)
[3085]141                && (oldTexCoords.x >= 0.0f) && (oldTexCoords.x < 1.0f)
142                && (oldTexCoords.y >= 0.0f) && (oldTexCoords.y < 1.0f)
[3103]143                && (depthDif <= MIN_DEPTH_DIFF)
[3082]144                // if visibility changed in the surrounding area we have to recompute
145                //&& (oldNumSamples > 0.8f * newNumSamples)
[3155]146                //&& (pixelValid < 1.0f)
[3082]147                )
148        {
149                // increase the weight for convergence
150                newWeight = oldWeight + 1.0f;
[3159]151               
152                if ((pixelValid > 1.0f) && (squaredLen < DYNAMIC_OBJECTS_THRESHOLD))
153                {               
154                        newWeight = 4.0f;
[3161]155                        //newWeight = 1.0f;
[3159]156                }
[3082]157        }
158        else
159        {       
[3121]160                newWeight = 1.0f;
[3082]161        }
[3087]162
[3137]163        return float2(oldPixel.x, newWeight);
[3082]164}
165
166
[2881]167/** The ssao shader returning the an intensity value between 0 and 1
[3151]168        This version of the ssao shader uses the dotproduct between pixel and
169        sample normal as weight.
[2881]170*/
[3150]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;
[3155]211                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
212
213                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
[3159]214                // the normal of the current sample
[3167]215                //const float3 sampleNormal = normalize(tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz);
216                const float3 sampleNormal = tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz;
[3150]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.
[3151]228                float cosAngle = .5f + dot(sampleNormal, -normal) * 0.5f;
[3155]229                // use binary decision to cull samples that are behind current shading point
230                cosAngle *= step(0.0f, dot(dirSample, normal));
[3150]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
[3157]246                // check if the samples have been valid in the last frame
247                numSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
[3150]248        }
249
250        return float2(max(0.0f, 1.0f - total_ao), numSamples);
251}
252
253
[3151]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.
[3150]257*/
[2904]258float2 ssao(fragment IN,
[3117]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
[3083]270                        )
[2881]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
[3084]276        float total_ao = .0f;
277        float numSamples = .0f;
[2881]278
279        for (int i = 0; i < NUM_SAMPLES; ++ i)
280        {
[2892]281                const float2 offset = samples[i];
[2881]282
[3175]283#if 1
[2881]284                ////////////////////
[3084]285                //-- add random noise: reflect around random normal vector (rather slow!)
[2985]286
[3150]287                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
[2892]288                const float2 offsetTransformed = myreflect(offset, mynoise);
[2903]289#else
290                const float2 offsetTransformed = offset;
291#endif
[2881]292                // weight with projected coordinate to reach similar kernel size for near and far
[3129]293                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
[3019]294                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
[2881]295
[3155]296                float4 sampleColor = tex2Dlod(colors, float4(texcoord, 0, 0));
297                const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
[3150]298               
[2989]299
[3017]300                ////////////////
301                //-- compute contribution of sample using the direction and angle
[2881]302
[3017]303                float3 dirSample = samplePos - centerPosition;
[3103]304                const float lengthToSample = max(length(dirSample), 1e-6f);
[2999]305
[3095]306                dirSample /= lengthToSample; // normalize
307
[2885]308                // angle between current normal and direction to sample controls AO intensity.
[3151]309                const float cosAngle = max(dot(dirSample, normal), .0f);
[3103]310       
[2979]311                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
312                // the distance to a sample approaches zero
[3081]313                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
[3089]314                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
[2881]315
[3017]316#if 1
[2885]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!)
[3095]319
[3103]320                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
[3081]321                total_ao += cosAngle * aoContrib * viewCorrection;
[3017]322#else
[3098]323                total_ao += cosAngle * aoContrib;
[2911]324#endif
[3157]325
326                // check if the samples have been valid in the last frame
327                numSamples += (1.0f - step(1.0f, lengthToSample)) * sampleColor.x;
[2881]328        }
329
[2904]330        return float2(max(0.0f, 1.0f - total_ao), numSamples);
[2881]331}
332
[3121]333
[3150]334
[2881]335/** The mrt shader for screen space ambient occlusion
336*/
337pixel main(fragment IN,
338                   uniform sampler2D colors,
339                   uniform sampler2D normals,
[3084]340                   uniform sampler2D noiseTex,
[2881]341                   uniform float2 samples[NUM_SAMPLES],
342                   uniform sampler2D oldTex,
[3085]343                   uniform float4x4 modelViewProj,
344                   uniform float4x4 oldModelViewProj,
[2985]345                   uniform float temporalCoherence,
[2986]346                   uniform float3 bl,
347                   uniform float3 br,
348                   uniform float3 tl,
[3085]349                   uniform float3 tr,
350                   uniform float3 oldEyePos,
351                   uniform float3 oldbl,
352                   uniform float3 oldbr,
353                   uniform float3 oldtl,
[3109]354                   uniform float3 oldtr,
[3150]355                   uniform sampler2D attribsTex
[2881]356                   )
357{
358        pixel OUT;
359
[3167]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;
[2975]362
[3082]363        // reconstruct position from the eye space depth
[3097]364        const float3 viewDir = IN.view;
[3089]365        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
[3097]366        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
[3014]367
[3121]368        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
369       
[3001]370
[3017]371        ////////////////
[3080]372        //-- calculcate the current projected posiion (also used for next frame)
[3017]373       
[3094]374        float4 projPos = mul(modelViewProj, eyeSpacePos);
[3112]375        const float invw = 1.0f / projPos.w;
376        projPos *= invw;
377        float scaleFactor = SAMPLE_RADIUS * invw;
[3121]378
[3017]379       
[3155]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
[3156]393
[3121]394        /////////////////
395        //-- compute temporal reprojection
396
[3137]397        float2 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
[3121]398                                                oldTex, oldModelViewProj, temporalCoherence,
399                                                                                        colors,
400                                                                                        projPos.xyz,
401                                                                                        invw,
402                                                                                        oldbl, oldbr, oldtl, oldtr,
[3155]403                                                                                        diffVec,
404                                                                                        ao.y
[3129]405                                                                                        );
[3121]406
407        const float oldSsao = temporalVals.x;
[3132]408        const float newWeight = temporalVals.y;
[3137]409
[3122]410
[3156]411        OUT.illum_col.x = (ao.x + oldSsao * (newWeight - 1.0f)) / newWeight;
[3137]412        OUT.illum_col.y = newWeight;
[3159]413        OUT.illum_col.z = SqrLen(diffVec);//invw;
[3137]414        OUT.illum_col.w = eyeSpaceDepth;
[3120]415
[2881]416        return OUT;
[3104]417}
Note: See TracBrowser for help on using the repository browser.