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

Revision 3137, 12.9 KB checked in by mattausch, 16 years ago (diff)

working ok

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