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

Revision 3128, 11.9 KB checked in by mattausch, 16 years ago (diff)
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(2000.0f, newWeight) * 1000.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                        )
260{
261        // Check in a circular area around the current position.
262        // Shoot vectors to the positions there, and check the angle to these positions.
263        // Summing up these angles gives an estimation of the occlusion at the current position.
264
265        float total_ao = .0f;
266        float numSamples = .0f;
267
268
269        for (int i = 0; i < NUM_SAMPLES; ++ i)
270        {
271                const float2 offset = samples[i];
272
273#if 1
274                ////////////////////
275                //-- add random noise: reflect around random normal vector (rather slow!)
276
277                float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f + noiseOffs, 0, 0)).xy;
278                //float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
279                const float2 offsetTransformed = myreflect(offset, mynoise);
280#else
281                const float2 offsetTransformed = offset;
282#endif
283                // weight with projected coordinate to reach similar kernel size for near and far
284                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
285
286                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
287
288                const float3 samplePos = ReconstructSamplePos(colors, texcoord, bl, br, tl, tr);
289
290
291                ////////////////
292                //-- compute contribution of sample using the direction and angle
293
294                float3 dirSample = samplePos - centerPosition;
295                const float lengthToSample = max(length(dirSample), 1e-6f);
296
297                dirSample /= lengthToSample; // normalize
298
299                // angle between current normal and direction to sample controls AO intensity.
300                float cosAngle = max(dot(dirSample, normal), .0f);
301       
302                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
303                // the distance to a sample approaches zero
304                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
305                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
306
307#if 1
308                // if surface normal perpenticular to view dir, approx. half of the samples will not count
309                // => compensate for this (on the other hand, projected sampling area could be larger!)
310
311                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
312                total_ao += cosAngle * aoContrib * viewCorrection;
313#else
314                total_ao += cosAngle * aoContrib;
315#endif
316        }
317
318        return float2(max(0.0f, 1.0f - total_ao), numSamples);
319}
320
321
322/** The mrt shader for screen space ambient occlusion
323*/
324pixel main(fragment IN,
325                   uniform sampler2D colors,
326                   uniform sampler2D normals,
327                   uniform sampler2D noiseTex,
328                   uniform float2 samples[NUM_SAMPLES],
329                   uniform sampler2D oldTex,
330                   uniform float4x4 modelViewProj,
331                   uniform float4x4 oldModelViewProj,
332                   uniform float temporalCoherence,
333                   uniform float3 bl,
334                   uniform float3 br,
335                   uniform float3 tl,
336                   uniform float3 tr,
337                   uniform float3 oldEyePos,
338                   uniform float3 oldbl,
339                   uniform float3 oldbr,
340                   uniform float3 oldtl,
341                   uniform float3 oldtr,
342                   uniform sampler2D attribsTex
343                   )
344{
345        pixel OUT;
346
347        const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
348
349        // reconstruct position from the eye space depth
350        const float3 viewDir = IN.view;
351        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
352        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
353
354        //float3 id = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
355        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
356       
357
358        ////////////////
359        //-- calculcate the current projected posiion (also used for next frame)
360       
361        float4 projPos = mul(modelViewProj, eyeSpacePos);
362        const float invw = 1.0f / projPos.w;
363        projPos *= invw;
364        float scaleFactor = SAMPLE_RADIUS * invw;
365
366       
367        /////////////////
368        //-- compute temporal reprojection
369
370        float3 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
371                                                oldTex, oldModelViewProj, temporalCoherence,
372                                                                                        colors,
373                                                                                        bl, br, tl, tr,
374                                                                                        projPos.xyz,
375                                                                                        invw,
376                                                                                        noiseTex,
377                                                                                        samples,
378                                                                                        scaleFactor,
379                                                                                        oldbl, oldbr, oldtl, oldtr,
380                                                                                        diffVec);
381
382        const float oldSsao = temporalVals.x;
383        const float newWeight = clamp(temporalVals.y, .0f, temporalCoherence);
384
385        //float2 noiseOffs = float2(temporalVals.y / 139.0f, temporalVals.y / 141.0f);
386        float2 noiseOffs = float2(.0f);
387
388        float2 ao;
389
390        // note: this should be done with the stencil buffer
391        if (eyeSpaceDepth < 1e10f)
392        {
393                ao = ssao(IN, colors, noiseTex, samples, normal,
394                          eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), noiseOffs);
395        }
396        else
397        {
398                 ao = float2(1.0f, 0);
399        }
400
401        OUT.illum_col.x = (ao.x + oldSsao * (newWeight - 1.0f)) / newWeight;
402        OUT.illum_col.y = temporalVals.y;
403        OUT.illum_col.z = invw;
404        OUT.illum_col.w = temporalVals.z;
405
406        return OUT;
407}
Note: See TracBrowser for help on using the repository browser.