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

Revision 3133, 12.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 = 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       
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 0
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, .0f, .0f)).xy;
283                //float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
284                const float2 offsetTransformed = myreflect(offset, mynoise);
285#else
286                const float2 offsetTransformed = offset;
287#endif
288                // weight with projected coordinate to reach similar kernel size for near and far
289                //const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor + jitter;
290                const float2 texcoord = IN.texCoord.xy + offsetTransformed * scaleFactor;
291
292                //if ((texcoord.x <= 1.0f) && (texcoord.x >= 0.0f) && (texcoord.y <= 1.0f) && (texcoord.y >= 0.0f)) ++ numSamples;
293                const float3 samplePos = ReconstructSamplePos(colors, texcoord, bl, br, tl, tr);
294
295
296                ////////////////
297                //-- compute contribution of sample using the direction and angle
298
299                float3 dirSample = samplePos - centerPosition;
300                const float lengthToSample = max(length(dirSample), 1e-6f);
301
302                dirSample /= lengthToSample; // normalize
303
304                // angle between current normal and direction to sample controls AO intensity.
305                float cosAngle = max(dot(dirSample, normal), .0f);
306       
307                // the distance_scale offset is used to avoid singularity that occurs at global illumination when
308                // the distance to a sample approaches zero
309                const float aoContrib = SAMPLE_INTENSITY / (DISTANCE_SCALE + lengthToSample * lengthToSample);
310                //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
311
312#if 1
313                // if surface normal perpenticular to view dir, approx. half of the samples will not count
314                // => compensate for this (on the other hand, projected sampling area could be larger!)
315
316                const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
317                total_ao += cosAngle * aoContrib * viewCorrection;
318#else
319                total_ao += cosAngle * aoContrib;
320#endif
321        }
322
323        return float2(max(0.0f, 1.0f - total_ao), numSamples);
324}
325
326
327/** The mrt shader for screen space ambient occlusion
328*/
329pixel main(fragment IN,
330                   uniform sampler2D colors,
331                   uniform sampler2D normals,
332                   uniform sampler2D noiseTex,
333                   uniform float2 samples[NUM_SAMPLES],
334                   uniform sampler2D oldTex,
335                   uniform float4x4 modelViewProj,
336                   uniform float4x4 oldModelViewProj,
337                   uniform float temporalCoherence,
338                   uniform float3 bl,
339                   uniform float3 br,
340                   uniform float3 tl,
341                   uniform float3 tr,
342                   uniform float3 oldEyePos,
343                   uniform float3 oldbl,
344                   uniform float3 oldbr,
345                   uniform float3 oldtl,
346                   uniform float3 oldtr,
347                   uniform sampler2D attribsTex,
348                   uniform sampler2D noiseTex1D
349                   )
350{
351        pixel OUT;
352
353        const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
354
355        // reconstruct position from the eye space depth
356        const float3 viewDir = IN.view;
357        const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
358        const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
359
360        /*const float xoffs = 2.0f / 1024.0f;
361        const float yoffs = 2.0f / 768.0f;
362
363        //float3 id = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
364        float3 x1 = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
365        float3 x2 = tex2Dlod(attribsTex, float4(IN.texCoord + float2(xoffs, 0), 0, 0)).xyz;
366        float3 x3 = tex2Dlod(attribsTex, float4(IN.texCoord + float2(0, yoffs), 0, 0)).xyz;
367        float3 x4 = tex2Dlod(attribsTex, float4(IN.texCoord + float2(-xoffs, 0), 0, 0)).xyz;
368        float3 x5 = tex2Dlod(attribsTex, float4(IN.texCoord + float2(0, -yoffs), 0, 0)).xyz;
369
370        float3 diffVec = (x1+x2+x3+x4+x5) * .25f;
371*/
372        float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, 0, 0)).xyz;
373       
374
375        ////////////////
376        //-- calculcate the current projected posiion (also used for next frame)
377       
378        float4 projPos = mul(modelViewProj, eyeSpacePos);
379        const float invw = 1.0f / projPos.w;
380        projPos *= invw;
381        float scaleFactor = SAMPLE_RADIUS * invw;
382
383       
384        /////////////////
385        //-- compute temporal reprojection
386
387        float3 temporalVals = temporalSmoothing(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
388                                                oldTex, oldModelViewProj, temporalCoherence,
389                                                                                        colors,
390                                                                                        bl, br, tl, tr,
391                                                                                        projPos.xyz,
392                                                                                        invw,
393                                                                                        noiseTex,
394                                                                                        samples,
395                                                                                        scaleFactor,
396                                                                                        oldbl, oldbr, oldtl, oldtr,
397                                                                                        diffVec
398                                                                                        );
399
400        const float oldSsao = temporalVals.x;
401        //const float newWeight = clamp(temporalVals.y, 1.0f, temporalCoherence);
402        const float newWeight = temporalVals.y;
403        //float2 noiseOffs = float2((temporalVals.y - 1)/ 139.0f, .0f);
404        float2 noiseOffs = float2(.0f);
405
406        float2 ao;
407
408        // note: this should be done with the stencil buffer
409        if (eyeSpaceDepth < 1e10f)
410        {
411                ao = ssao(IN, colors, noiseTex, samples, normal,
412                          eyeSpacePos.xyz, scaleFactor, bl, br, tl, tr, normalize(viewDir), noiseOffs, noiseTex1D);
413        }
414        else
415        {
416                 ao = float2(1.0f, 0);
417        }
418
419        OUT.illum_col.x = (ao.x + oldSsao * (newWeight - 1.0f)) / newWeight;
420        OUT.illum_col.y = temporalVals.y;
421        OUT.illum_col.z = invw;
422        OUT.illum_col.w = temporalVals.z;
423
424        return OUT;
425}
Note: See TracBrowser for help on using the repository browser.