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

Revision 3129, 12.2 KB checked in by mattausch, 16 years ago (diff)

tried different techniques for smoothing the flickering for moving objects
fixed kernel + rotation + jittering not working yet
smoothssao function working better now (also test different filter sizes!)

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 float3 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 = worldPos.xyz - oldEyePos + diffVec;
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       
170        const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
171
172#else
173
174        // calculate eye space position of sample in old frame
175        const float oldDepth = oldPixel.w;
176        // the depth projected into the old frame
177        const float projectedDepth = projPos.z;
178        // calculate depth difference
179        const float depthDif = abs(projectedDepth - oldDepth);
180
181#endif
182
183        float notValid = 0.5f;
184        float overallDepth = 0;
185
186        const float squaredLen = diffVec.x * diffVec.x + diffVec.y * diffVec.y + diffVec.z * diffVec.z;
187
188#if 1
189        if (squaredLen < 1e-8f) // object not dynamic
190        {
191                for (int i = 0; i < NUM_SAMPLES; ++ i)
192                {
193                        float sampleDif = ComputeDifference(samples[i],
194                                                                                                oldTex,
195                                                                                                oldModelViewProj,
196                                                                                                colors,
197                                                                                                noiseTex,
198                                                                                                scaleFactor,
199                                                                                                bl, br, tl, tr,
200                                                                                                texcoord0,
201                                                                                                oldEyePos,
202                                                                                                oldbl, oldbr, oldtl, oldtr,
203                                                                                                eyeSpaceDepth
204                                                                                                );
205                        //overallDepth += sampleDif;
206                        if (sampleDif >= MIN_DEPTH_DIFF) ++ notValid;
207                }
208        }
209#endif
210
211        //const float oldWeight = clamp(oldPixel.y, .0f, temporalCoherence);
212        const float oldWeight = oldPixel.y;
213
214        float newWeight;
215
216        if ((temporalCoherence > 1e-6f)
217                && (oldTexCoords.x >= 0.0f) && (oldTexCoords.x < 1.0f)
218                && (oldTexCoords.y >= 0.0f) && (oldTexCoords.y < 1.0f)
219                && (depthDif <= MIN_DEPTH_DIFF)
220                // if visibility changed in the surrounding area we have to recompute
221                //&& (oldNumSamples > 0.8f * newNumSamples)
222                //&& (notValid < 1.0f)
223                )
224        {
225                // increase the weight for convergence
226                newWeight = oldWeight + 1.0f;
227                if (notValid > 1.0f) newWeight = 10.0f;
228                //if (notValid > 1.0f) newWeight = max(15.0f - notValid * 2.0f, 1.0f);
229        }
230        else
231        {       
232                newWeight = 1.0f;
233        }
234
235        //if (oldPixel.y >= 2000)
236        //      newWeight = min(temporalCoherence + 1, max(oldPixel.y - 70, 50));
237        //if (newWeight >= 2000) newWeight = 1000;
238        newWeight -= step(512.0f, newWeight) * 256.0f;
239
240        return float3(oldPixel.x, newWeight, eyeSpaceDepth);
241}
242
243
244/** The ssao shader returning the an intensity value between 0 and 1
245*/
246float2 ssao(fragment IN,
247                        sampler2D colors,
248                        sampler2D noiseTex,
249                        float2 samples[NUM_SAMPLES],
250                        float3 normal,
251                        float3 centerPosition,
252                        float scaleFactor,
253                        float3 bl,
254                        float3 br,
255                        float3 tl,
256                        float3 tr,
257                        float3 viewDir
258                        , float2 noiseOffs
259                        , sampler2D noiseTex1D
260                        )
261{
262        // Check in a circular area around the current position.
263        // Shoot vectors to the positions there, and check the angle to these positions.
264        // Summing up these angles gives an estimation of the occlusion at the current position.
265
266        float total_ao = .0f;
267        float numSamples = .0f;
268
269
270        //float2 jitter = tex2Dlod(noiseTex1D, float4(IN.texCoord.x * 4.0f + noiseOffs.x, 0.5f, 0, 0)).xy;
271        //float2 jitter = tex2Dlod(noiseTex1D, float4(noiseOffs.x, 0.5f, 0, 0)).xy;
272
273        for (int i = 0; i < NUM_SAMPLES; ++ i)
274        {
275                const float2 offset = samples[i];
276
277#if 1
278                ////////////////////
279                //-- add random noise: reflect around random normal vector (rather slow!)
280
281                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f + noiseOffs, 0, 0)).xy;
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                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
292                const float3 samplePos = ReconstructSamplePos(colors, 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                const float lengthToSample = max(length(dirSample), 1e-6f);
300
301                dirSample /= lengthToSample; // normalize
302
303                // angle between current normal and direction to sample controls AO intensity.
304                float cosAngle = max(dot(dirSample, normal), .0f);
305       
306                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
307                // the distance to a sample approaches zero
308                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
309                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
310
311#if 1
312                // if surface normal perpenticular to view dir, approx. half of the samples will not count
313                // => compensate for this (on the other hand, projected sampling area could be larger!)
314
315                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
316                total_ao += cosAngle * aoContrib * viewCorrection;
317#else
318                total_ao += cosAngle * aoContrib;
319#endif
320        }
321
322        return float2(max(0.0f, 1.0f - total_ao), numSamples);
323}
324
325
326/** The mrt shader for screen space ambient occlusion
327*/
328pixel main(fragment IN,
329                   uniform sampler2D colors,
330                   uniform sampler2D normals,
331                   uniform sampler2D noiseTex,
332                   uniform float2 samples[NUM_SAMPLES],
333                   uniform sampler2D oldTex,
334                   uniform float4x4 modelViewProj,
335                   uniform float4x4 oldModelViewProj,
336                   uniform float temporalCoherence,
337                   uniform float3 bl,
338                   uniform float3 br,
339                   uniform float3 tl,
340                   uniform float3 tr,
341                   uniform float3 oldEyePos,
342                   uniform float3 oldbl,
343                   uniform float3 oldbr,
344                   uniform float3 oldtl,
345                   uniform float3 oldtr,
346                   uniform sampler2D attribsTex,
347                   uniform sampler2D noiseTex1D
348                   )
349{
350        pixel OUT;
351
352        const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
353
354        // reconstruct position from the eye space depth
355        const float3 viewDir = IN.view;
356        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
357        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
358
359        //float3 id = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
360        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
361       
362
363        ////////////////
364        //-- calculcate the current projected posiion (also used for next frame)
365       
366        float4 projPos = mul(modelViewProj, eyeSpacePos);
367        const float invw = 1.0f / projPos.w;
368        projPos *= invw;
369        float scaleFactor = SAMPLE_RADIUS * invw;
370
371       
372        /////////////////
373        //-- compute temporal reprojection
374
375        float3 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
376                                                oldTex, oldModelViewProj, temporalCoherence,
377                                                                                        colors,
378                                                                                        bl, br, tl, tr,
379                                                                                        projPos.xyz,
380                                                                                        invw,
381                                                                                        noiseTex,
382                                                                                        samples,
383                                                                                        scaleFactor,
384                                                                                        oldbl, oldbr, oldtl, oldtr,
385                                                                                        diffVec
386                                                                                        );
387
388        const float oldSsao = temporalVals.x;
389        const float newWeight = clamp(temporalVals.y, 1.0f, temporalCoherence);
390        float2 noiseOffs = float2((temporalVals.y - 1)/ 139.0f, .0f);
391        //float2 noiseOffs = float2(.0f);
392
393        float2 ao;
394
395        // note: this should be done with the stencil buffer
396        if (eyeSpaceDepth < 1e10f)
397        {
398                ao = ssao(IN, colors, noiseTex, samples, normal,
399                          eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), noiseOffs, noiseTex1D);
400        }
401        else
402        {
403                 ao = float2(1.0f, 0);
404        }
405
406        OUT.illum_col.x = (ao.x + oldSsao * (newWeight - 1.0f)) / newWeight;
407        OUT.illum_col.y = temporalVals.y;
408        OUT.illum_col.z = invw;
409        OUT.illum_col.w = temporalVals.z;
410
411        return OUT;
412}
Note: See TracBrowser for help on using the repository browser.