[2884] | 1 | #include "../shaderenv.h"
|
---|
[3227] | 2 | #include "common.h"
|
---|
[2884] | 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] | 12 | struct fragment
|
---|
| 13 | {
|
---|
[2889] | 14 | float2 texCoord: TEXCOORD0;
|
---|
| 15 | float3 view: TEXCOORD1;
|
---|
[2881] | 16 | };
|
---|
| 17 |
|
---|
| 18 |
|
---|
[3325] | 19 | struct pixel2
|
---|
| 20 | {
|
---|
| 21 | float4 illum_col: COLOR0;
|
---|
[3345] | 22 | //float4 col: COLOR1;
|
---|
[3325] | 23 | };
|
---|
| 24 |
|
---|
| 25 |
|
---|
[3247] | 26 | // this function is inspired from the paper of shamulgaan in order
|
---|
| 27 | // to get a physical expression for the occlusion culling
|
---|
[3081] | 28 | inline float occlusionPower(float radius, float dist)
|
---|
| 29 | {
|
---|
| 30 | return 6.283185307179586476925286766559f * (1.0f - cos(asin(radius / dist)));
|
---|
| 31 | }
|
---|
| 32 |
|
---|
| 33 |
|
---|
[3159] | 34 |
|
---|
[2992] | 35 | // reconstruct world space position
|
---|
[3155] | 36 | inline float3 ReconstructSamplePos(float eyeSpaceDepth,
|
---|
[3017] | 37 | float2 texcoord,
|
---|
| 38 | float3 bl, float3 br, float3 tl, float3 tr)
|
---|
[2988] | 39 | {
|
---|
[3097] | 40 | float3 viewVec = Interpol(texcoord, bl, br, tl, tr);
|
---|
[3017] | 41 | float3 samplePos = -viewVec * eyeSpaceDepth;
|
---|
| 42 |
|
---|
[2999] | 43 | return samplePos;
|
---|
[2988] | 44 | }
|
---|
| 45 |
|
---|
| 46 |
|
---|
[3318] | 47 | float ComputeConvergence(uniform sampler2D tex, float2 texCoord, float2 res)
|
---|
| 48 | {
|
---|
| 49 | // get the minimum convergence by exactly sampling the 4 surrounding
|
---|
| 50 | // texels in the old texture, otherwise flickering because convergence
|
---|
| 51 | // will be interpolated when upsampling and filter size does not match!
|
---|
[3087] | 52 |
|
---|
[3319] | 53 | const float2 invRes = float2(1.0f / res.x, 1.0f / res.y);
|
---|
[3318] | 54 |
|
---|
| 55 | // get position exactly between texel centers
|
---|
[3321] | 56 | float2 center = (floor(texCoord * res) + float2(.5f)) * texCoord;
|
---|
[3318] | 57 | //center.x = (floor(texCoord.x * res.x - .5f) + 1.0f) / res.x;
|
---|
| 58 | //center.y = (floor(texCoord.y * res.y - .5f) + 1.0f) / res.y;
|
---|
| 59 | //center.y = (floor(texCoord.y * res.y) + .5f) * yOffs;
|
---|
| 60 |
|
---|
| 61 | /*texelCenterConv.x = tex2Dlod(tex, float4(center + float2( xoffs, yoffs), 0, 0)).y;
|
---|
| 62 | texelCenterConv.y = tex2Dlod(tex, float4(center + float2( xoffs, -yoffs), 0, 0)).y;
|
---|
| 63 | texelCenterConv.z = tex2Dlod(tex, float4(center + float2(-xoffs, -yoffs), 0, 0)).y;
|
---|
| 64 | texelCenterConv.w = tex2Dlod(tex, float4(center + float2(-xoffs, yoffs), 0, 0)).y;
|
---|
| 65 |
|
---|
| 66 | const float m1 = min(texelCenterConv.x, texelCenterConv.y);
|
---|
| 67 | const float m2 = min(texelCenterConv.z, texelCenterConv.w);
|
---|
| 68 |
|
---|
| 69 | const float convergence = min(m1, m2);*/
|
---|
| 70 |
|
---|
[3319] | 71 | //const float convergence = tex2Dlod(tex, float4(center, 0, 0)).y;
|
---|
| 72 | const float convergence = tex2Dlod(tex, float4(texCoord, 0, 0)).y;
|
---|
[3318] | 73 |
|
---|
| 74 | return convergence;
|
---|
| 75 | }
|
---|
| 76 |
|
---|
[3115] | 77 | /** This shader computes the reprojection and stores
|
---|
[3155] | 78 | the ssao value of the old pixel as well as the
|
---|
| 79 | weight of the pixel in the new frame.
|
---|
[3082] | 80 | */
|
---|
[3324] | 81 | inline float3 Reproject(float4 worldPos,
|
---|
| 82 | float eyeSpaceDepth,
|
---|
| 83 | float2 texcoord0,
|
---|
| 84 | float3 oldEyePos,
|
---|
| 85 | sampler2D oldTex,
|
---|
| 86 | float4x4 oldModelViewProj,
|
---|
| 87 | sampler2D colors,
|
---|
| 88 | float3 projPos,
|
---|
| 89 | float invW,
|
---|
| 90 | float3 oldbl,
|
---|
| 91 | float3 oldbr,
|
---|
| 92 | float3 oldtl,
|
---|
| 93 | float3 oldtr,
|
---|
| 94 | float3 diffVec
|
---|
| 95 | )
|
---|
[3082] | 96 | {
|
---|
[3113] | 97 | // compute position from old frame for dynamic objects + translational portion
|
---|
[3133] | 98 | const float3 translatedPos = diffVec - oldEyePos + worldPos.xyz;
|
---|
[3111] | 99 |
|
---|
[3082] | 100 |
|
---|
[3109] | 101 | /////////////////
|
---|
| 102 | //-- reproject into old frame and calculate texture position of sample in old frame
|
---|
| 103 |
|
---|
| 104 | // note: the old model view matrix only holds the view orientation part
|
---|
[3115] | 105 | float4 backProjPos = mul(oldModelViewProj, float4(translatedPos, 1.0f));
|
---|
[3083] | 106 | backProjPos /= backProjPos.w;
|
---|
[3109] | 107 |
|
---|
[3082] | 108 | // fit from unit cube into 0 .. 1
|
---|
[3085] | 109 | const float2 oldTexCoords = backProjPos.xy * 0.5f + 0.5f;
|
---|
[3082] | 110 | // retrieve the sample from the last frame
|
---|
[3095] | 111 | const float4 oldPixel = tex2Dlod(oldTex, float4(oldTexCoords, .0f, .0f));
|
---|
[3105] | 112 |
|
---|
[3204] | 113 | // the ssao value in the old frame
|
---|
| 114 | const float ssao = oldPixel.x;
|
---|
| 115 |
|
---|
[3095] | 116 | // calculate eye space position of sample in old frame
|
---|
| 117 | const float oldEyeSpaceDepth = oldPixel.w;
|
---|
[3082] | 118 |
|
---|
[3345] | 119 | // vector from eye pos to old sample
|
---|
[3097] | 120 | const float3 viewVec = Interpol(oldTexCoords, oldbl, oldbr, oldtl, oldtr);
|
---|
[3109] | 121 | const float invLen = 1.0f / length(viewVec);
|
---|
[3115] | 122 | const float projectedEyeSpaceDepth = invLen * length(translatedPos);
|
---|
[3137] | 123 | //const float projectedEyeSpaceDepth = length(translatedPos);
|
---|
[3099] | 124 |
|
---|
[3109] | 125 | const float depthDif = abs(1.0f - oldEyeSpaceDepth / projectedEyeSpaceDepth);
|
---|
[3106] | 126 |
|
---|
[3225] | 127 | // the weight of the accumulated samples from the previous frames
|
---|
[3204] | 128 | float w;
|
---|
[3325] | 129 | float idx;
|
---|
[3204] | 130 |
|
---|
[3341] | 131 |
|
---|
[3204] | 132 | //////////////
|
---|
| 133 | //-- reuse old value only if it was still valid in the old frame
|
---|
| 134 |
|
---|
[3192] | 135 | if (1
|
---|
[3225] | 136 | && (oldTexCoords.x > 0) && (oldTexCoords.x < 1.0f)
|
---|
| 137 | && (oldTexCoords.y > 0) && (oldTexCoords.y < 1.0f)
|
---|
[3103] | 138 | && (depthDif <= MIN_DEPTH_DIFF)
|
---|
[3082] | 139 | )
|
---|
| 140 | {
|
---|
[3204] | 141 | // pixel valid => retrieve the convergence weight
|
---|
[3318] | 142 | /*float w1 = tex2Dlod(oldTex, float4(oldTexCoords + float2(0.5f / 1024.0f, 0), .0f, .0f)).y;
|
---|
[3316] | 143 | float w2 = tex2Dlod(oldTex, float4(oldTexCoords - float2(0.5f / 1024.0f, 0), .0f, .0f)).y;
|
---|
| 144 | float w3 = tex2Dlod(oldTex, float4(oldTexCoords + float2(0, 0.5f / 768.0f), .0f, .0f)).y;
|
---|
| 145 | float w4 = tex2Dlod(oldTex, float4(oldTexCoords - float2(0, 0.5f / 768.0f), .0f, .0f)).y;
|
---|
| 146 |
|
---|
[3318] | 147 | w = min(min(w1, w2), min(w3, w4));*/
|
---|
| 148 |
|
---|
[3319] | 149 | //w = ComputeConvergence(oldTex, oldTexCoords, float2(1024.0f, 768.0f));
|
---|
[3345] | 150 | w = floor(oldPixel.y);
|
---|
| 151 | //w = oldPixel.y;
|
---|
[3325] | 152 | idx = floor(oldPixel.z);
|
---|
[3082] | 153 | }
|
---|
| 154 | else
|
---|
| 155 | {
|
---|
[3345] | 156 | w = .0f;
|
---|
[3325] | 157 | idx = .0f;
|
---|
[3082] | 158 | }
|
---|
[3087] | 159 |
|
---|
[3324] | 160 | return float3(ssao, w, idx);
|
---|
[3082] | 161 | }
|
---|
| 162 |
|
---|
| 163 |
|
---|
[3320] | 164 | /** The ssao shader returning the an intensity value between 0 and 1.
|
---|
| 165 | This version of the ssao shader uses the dotproduct between
|
---|
| 166 | pixel-to-sample direction and sample normal as weight.
|
---|
| 167 |
|
---|
| 168 | The algorithm works like the following:
|
---|
| 169 | 1) Check in a circular area around the current position.
|
---|
| 170 | 2) Shoot vectors to the positions there, and check the angle to these positions.
|
---|
| 171 | 3) Summing up these angles gives an estimation of the occlusion at the current position.
|
---|
[2881] | 172 | */
|
---|
[3193] | 173 | float3 ssao2(fragment IN,
|
---|
[3150] | 174 | sampler2D colors,
|
---|
| 175 | sampler2D noiseTex,
|
---|
[3324] | 176 | sampler2D samples,
|
---|
[3150] | 177 | float3 normal,
|
---|
| 178 | float3 centerPosition,
|
---|
[3331] | 179 | float radius,
|
---|
[3150] | 180 | float3 bl,
|
---|
| 181 | float3 br,
|
---|
| 182 | float3 tl,
|
---|
| 183 | float3 tr,
|
---|
| 184 | float3 viewDir,
|
---|
[3320] | 185 | float convergence,
|
---|
| 186 | float sampleIntensity,
|
---|
| 187 | bool isMovingObject,
|
---|
[3324] | 188 | sampler2D normalTex,
|
---|
[3325] | 189 | float idx
|
---|
[3150] | 190 | )
|
---|
| 191 | {
|
---|
| 192 | float total_ao = .0f;
|
---|
[3320] | 193 | float validSamples = .0f;
|
---|
[3150] | 194 | float numSamples = .0f;
|
---|
| 195 |
|
---|
[3313] | 196 | for (int i = 0; i < NUM_SAMPLES; ++ i)
|
---|
[3150] | 197 | {
|
---|
[3320] | 198 | float2 offset;
|
---|
[3150] | 199 |
|
---|
[3329] | 200 | const float2 ssaoOffset =
|
---|
| 201 | tex2Dlod(samples, float4((0.5f + i + idx) / NUM_PRECOMPUTED_SAMPLES, 0.5f, .0f, .0f)).xy;
|
---|
[3324] | 202 |
|
---|
[3150] | 203 | ////////////////////
|
---|
[3320] | 204 | //-- add random noise: reflect around random normal vector
|
---|
| 205 | //-- (affects performance for some reason!)
|
---|
[3150] | 206 |
|
---|
[3328] | 207 | if (convergence < SSAO_CONVERGENCE_THRESHOLD)
|
---|
[3320] | 208 | {
|
---|
| 209 | float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
|
---|
| 210 | //offset = myreflect(samples[i], mynoise);
|
---|
[3324] | 211 | //offset = myrotate(samples[i], mynoise.x);
|
---|
| 212 | offset = myrotate(ssaoOffset, mynoise.x);
|
---|
[3320] | 213 | }
|
---|
| 214 | else
|
---|
| 215 | {
|
---|
[3324] | 216 | offset = ssaoOffset;
|
---|
[3320] | 217 | }
|
---|
| 218 |
|
---|
[3150] | 219 | // weight with projected coordinate to reach similar kernel size for near and far
|
---|
[3331] | 220 | const float2 texcoord = IN.texCoord.xy + offset * radius;
|
---|
[3150] | 221 |
|
---|
[3320] | 222 | const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
|
---|
[3155] | 223 | const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
|
---|
[3320] | 224 |
|
---|
[3150] | 225 |
|
---|
| 226 | ////////////////
|
---|
| 227 | //-- compute contribution of sample using the direction and angle
|
---|
| 228 |
|
---|
| 229 | float3 dirSample = samplePos - centerPosition;
|
---|
| 230 |
|
---|
[3329] | 231 | const float minDist = 1e-6f;
|
---|
[3331] | 232 | const float delta = 1e-3f;
|
---|
[3199] | 233 |
|
---|
[3329] | 234 | const float lengthToSample = length(dirSample);
|
---|
[3331] | 235 | const float sampleWeight = 1.0f / (lengthToSample + delta);
|
---|
[3320] | 236 |
|
---|
[3329] | 237 | dirSample /= max(lengthToSample, minDist); // normalize
|
---|
[3150] | 238 |
|
---|
[3329] | 239 |
|
---|
[3150] | 240 | // angle between current normal and direction to sample controls AO intensity.
|
---|
[3329] | 241 | const float cosAngle = dot(dirSample, normal);
|
---|
[3320] | 242 |
|
---|
| 243 | // the normal of the current sample
|
---|
[3329] | 244 | const float3 sampleNormal = normalize(tex2Dlod(normalTex, float4(texcoord, 0, 0)).xyz);
|
---|
| 245 |
|
---|
[3320] | 246 | // angle between current normal and direction to sample controls AO intensity.
|
---|
[3330] | 247 | //const float cosAngle2 = dot(-dirSample, sampleNormal);
|
---|
[3339] | 248 | const float cosAngle2 = .5f + dot(sampleNormal, -normal) * .5f;
|
---|
[3330] | 249 |
|
---|
[3329] | 250 | dirSample *= minDist;
|
---|
| 251 | const float aoContrib = sampleIntensity * sampleWeight;
|
---|
[3320] | 252 |
|
---|
[3150] | 253 | //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
|
---|
[3339] | 254 | //total_ao += max(cosAngle, .0f) * max(cosAngle2, .0f) * aoContrib;
|
---|
| 255 | total_ao += max(cosAngle, .0f) * cosAngle2 * aoContrib;
|
---|
[3150] | 256 |
|
---|
[3320] | 257 | ++ numSamples;
|
---|
[3150] | 258 |
|
---|
[3157] | 259 | // check if the samples have been valid in the last frame
|
---|
[3320] | 260 | // only mark sample as invalid if in the last / current frame
|
---|
| 261 | // they possibly have any influence on the ao
|
---|
[3203] | 262 |
|
---|
[3320] | 263 | const float changeFactor = sampleColor.y;
|
---|
| 264 | const float pixelValid = sampleColor.x;
|
---|
| 265 |
|
---|
| 266 | // hack:
|
---|
| 267 | // we check if the sample could have been near enough
|
---|
| 268 | // to the current pixel or if the angle is small enough
|
---|
| 269 | // to have any influence in the current or last frame
|
---|
[3328] | 270 | #if 1
|
---|
[3320] | 271 | const float tooFarAway = step(0.5f, lengthToSample - changeFactor);
|
---|
[3321] | 272 | const float partlyResetThres = 1.0f;
|
---|
[3329] | 273 |
|
---|
[3328] | 274 | if (pixelValid <= partlyResetThres)
|
---|
[3320] | 275 | validSamples = max(validSamples, pixelValid * (1.0f - tooFarAway) * step(-0.1f, cosAngle));
|
---|
| 276 | else
|
---|
| 277 | validSamples = max(validSamples, pixelValid);
|
---|
| 278 | #endif
|
---|
| 279 |
|
---|
| 280 | #ifdef USE_GTX
|
---|
| 281 | // we can bail out early and use a minimal #samples)
|
---|
| 282 | // if some conditions are met as long as the hardware supports it
|
---|
| 283 | if (numSamples >= MIN_SAMPLES)
|
---|
| 284 | {
|
---|
| 285 | //break;
|
---|
| 286 | // if the pixel belongs to a static object and all the samples stay valid in the current frame
|
---|
| 287 | if (!isMovingObject && (validSamples < 1.0f) && (convergence > NUM_SAMPLES)) break;
|
---|
| 288 | // if the pixel belongs to a dynamic object but the #accumulated samples for this pixel is sufficiently high
|
---|
| 289 | // (=> there was no discontinuity recently)
|
---|
| 290 | //else if (isMovingObject && (convergence > SSAO_CONVERGENCE_THRESHOLD)) break;
|
---|
[3328] | 291 | else if (isMovingObject && (convergence > NUM_SAMPLES * 5)) break;
|
---|
[3320] | 292 | }
|
---|
| 293 | #endif
|
---|
[3150] | 294 | }
|
---|
| 295 |
|
---|
[3320] | 296 | // "normalize" ao contribution
|
---|
[3193] | 297 | total_ao /= numSamples;
|
---|
| 298 |
|
---|
[3227] | 299 | #if 1
|
---|
[3225] | 300 | // if surface normal perpenticular to view dir, approx. half of the samples will not count
|
---|
| 301 | // => compensate for this (on the other hand, projected sampling area could be larger!)
|
---|
| 302 | const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
|
---|
[3320] | 303 | total_ao *= viewCorrection;
|
---|
[3225] | 304 | #endif
|
---|
| 305 |
|
---|
[3320] | 306 | //return float3(total_ao, validSamples, numSamples);
|
---|
| 307 | return float3(min(1.0f, total_ao), validSamples, numSamples);
|
---|
[3150] | 308 | }
|
---|
| 309 |
|
---|
| 310 |
|
---|
[3151] | 311 | /** The ssao shader returning the an intensity value between 0 and 1.
|
---|
| 312 | This version of the ssao shader uses the dotproduct between
|
---|
| 313 | pixel-to-sample direction and sample normal as weight.
|
---|
[3204] | 314 |
|
---|
| 315 | The algorithm works like the following:
|
---|
| 316 | 1) Check in a circular area around the current position.
|
---|
| 317 | 2) Shoot vectors to the positions there, and check the angle to these positions.
|
---|
| 318 | 3) Summing up these angles gives an estimation of the occlusion at the current position.
|
---|
[3150] | 319 | */
|
---|
[3192] | 320 | float3 ssao(fragment IN,
|
---|
[3117] | 321 | sampler2D colors,
|
---|
| 322 | sampler2D noiseTex,
|
---|
[3329] | 323 | sampler2D samples,
|
---|
[3117] | 324 | float3 normal,
|
---|
| 325 | float3 centerPosition,
|
---|
[3331] | 326 | float radius,
|
---|
[3117] | 327 | float3 bl,
|
---|
| 328 | float3 br,
|
---|
| 329 | float3 tl,
|
---|
| 330 | float3 tr,
|
---|
[3192] | 331 | float3 viewDir,
|
---|
[3230] | 332 | float convergence,
|
---|
[3213] | 333 | float sampleIntensity,
|
---|
[3329] | 334 | bool isMovingObject,
|
---|
| 335 | float oldIdx
|
---|
[3083] | 336 | )
|
---|
[2881] | 337 | {
|
---|
[3084] | 338 | float total_ao = .0f;
|
---|
[3192] | 339 | float validSamples = .0f;
|
---|
[3084] | 340 | float numSamples = .0f;
|
---|
[3313] | 341 |
|
---|
[2881] | 342 | for (int i = 0; i < NUM_SAMPLES; ++ i)
|
---|
| 343 | {
|
---|
[3230] | 344 | float2 offset;
|
---|
[2881] | 345 |
|
---|
[3329] | 346 | const float2 ssaoOffset =
|
---|
| 347 | tex2Dlod(samples, float4((0.5f + i + oldIdx) / NUM_PRECOMPUTED_SAMPLES, 0.5f, .0f, .0f)).xy;
|
---|
| 348 |
|
---|
[2881] | 349 | ////////////////////
|
---|
[3204] | 350 | //-- add random noise: reflect around random normal vector
|
---|
[3227] | 351 | //-- (affects performance for some reason!)
|
---|
[2985] | 352 |
|
---|
[3311] | 353 | if (convergence < SSAO_CONVERGENCE_THRESHOLD)
|
---|
[3230] | 354 | {
|
---|
[3313] | 355 | float2 mynoise = tex2Dlod(noiseTex, float4(IN.texCoord * 4.0f, 0, 0)).xy;
|
---|
[3284] | 356 | //offset = myreflect(samples[i], mynoise);
|
---|
[3329] | 357 | //offset = myrotate(samples[i], mynoise.x);
|
---|
| 358 | offset = myrotate(ssaoOffset, mynoise.x);
|
---|
[3230] | 359 | }
|
---|
| 360 | else
|
---|
| 361 | {
|
---|
[3329] | 362 | offset = ssaoOffset;
|
---|
[3230] | 363 | }
|
---|
[3329] | 364 |
|
---|
| 365 |
|
---|
[2881] | 366 | // weight with projected coordinate to reach similar kernel size for near and far
|
---|
[3331] | 367 | const float2 texcoord = IN.texCoord.xy + offset * radius;
|
---|
[2881] | 368 |
|
---|
[3203] | 369 | const float4 sampleColor = tex2Dlod(colors, float4(texcoord, .0f, .0f));
|
---|
[3155] | 370 | const float3 samplePos = ReconstructSamplePos(sampleColor.w, texcoord, bl, br, tl, tr);
|
---|
[3150] | 371 |
|
---|
[2989] | 372 |
|
---|
[3017] | 373 | ////////////////
|
---|
| 374 | //-- compute contribution of sample using the direction and angle
|
---|
[2881] | 375 |
|
---|
[3017] | 376 | float3 dirSample = samplePos - centerPosition;
|
---|
[2999] | 377 |
|
---|
[3329] | 378 | const float minDist = 1e-6f;
|
---|
[3344] | 379 | const float eps = 1e-3f;
|
---|
[3319] | 380 |
|
---|
[3329] | 381 | const float lengthToSample = length(dirSample);
|
---|
[3344] | 382 | const float sampleWeight = 1.0f / max(lengthToSample, eps);
|
---|
[3197] | 383 |
|
---|
[3329] | 384 | dirSample /= max(length(dirSample), minDist); // normalize
|
---|
[3095] | 385 |
|
---|
[2885] | 386 | // angle between current normal and direction to sample controls AO intensity.
|
---|
[3329] | 387 | const float cosAngle = dot(dirSample, normal);
|
---|
[3227] | 388 |
|
---|
| 389 | //const float aoContrib = sampleIntensity / sqrLen;
|
---|
[3329] | 390 | const float aoContrib = sampleIntensity * sampleWeight;
|
---|
[3089] | 391 | //const float aoContrib = (1.0f > lengthToSample) ? occlusionPower(9e-2f, DISTANCE_SCALE + lengthToSample): .0f;
|
---|
[2881] | 392 |
|
---|
[3329] | 393 | total_ao += max(cosAngle, .0f) * aoContrib;
|
---|
[3157] | 394 |
|
---|
[3206] | 395 | ++ numSamples;
|
---|
[3340] | 396 |
|
---|
[3331] | 397 | #ifdef PERFORMANCE_TEST
|
---|
[3157] | 398 | // check if the samples have been valid in the last frame
|
---|
[3213] | 399 | // only mark sample as invalid if in the last / current frame
|
---|
| 400 | // they possibly have any influence on the ao
|
---|
[3319] | 401 |
|
---|
[3206] | 402 | const float changeFactor = sampleColor.y;
|
---|
| 403 | const float pixelValid = sampleColor.x;
|
---|
[3204] | 404 |
|
---|
[3319] | 405 | // hack:
|
---|
[3213] | 406 | // we check if the sample could have been near enough to the current pixel
|
---|
[3319] | 407 | // or if the angle is small enough
|
---|
[3213] | 408 | // to have any influence in the current or last frame
|
---|
[3331] | 409 |
|
---|
[3328] | 410 | #if 1
|
---|
[3321] | 411 | const float partlyResetThres = 1.0f;
|
---|
| 412 |
|
---|
[3313] | 413 | const float tooFarAway = step(0.5f, lengthToSample - changeFactor);
|
---|
[3321] | 414 | if (0)//pixelValid <= partlyResetThres)
|
---|
[3319] | 415 | validSamples = max(validSamples, pixelValid * (1.0f - tooFarAway) * step(-0.1f, cosAngle));
|
---|
| 416 | else
|
---|
| 417 | validSamples = max(validSamples, pixelValid);
|
---|
[3314] | 418 | #endif
|
---|
[3192] | 419 |
|
---|
[3227] | 420 | #ifdef USE_GTX
|
---|
[3213] | 421 | // we can bail out early and use a minimal #samples)
|
---|
| 422 | // if some conditions are met as long as the hardware supports it
|
---|
[3230] | 423 | if (numSamples >= MIN_SAMPLES)
|
---|
[3213] | 424 | {
|
---|
[3313] | 425 | //break;
|
---|
[3213] | 426 | // if the pixel belongs to a static object and all the samples stay valid in the current frame
|
---|
[3319] | 427 | if (!isMovingObject && (validSamples < 1.0f) && (convergence > NUM_SAMPLES)) break;
|
---|
[3313] | 428 | // if the pixel belongs to a dynamic object but the #accumulated samples for this pixel is sufficiently high
|
---|
| 429 | // (=> there was no discontinuity recently)
|
---|
[3319] | 430 | //else if (isMovingObject && (convergence > SSAO_CONVERGENCE_THRESHOLD)) break;
|
---|
[3329] | 431 | else if (isMovingObject && (convergence > NUM_SAMPLES * 5)) break;
|
---|
[3213] | 432 | }
|
---|
| 433 | #endif
|
---|
[3331] | 434 |
|
---|
| 435 | #endif // PERFORMANCE_TEST
|
---|
[2881] | 436 | }
|
---|
| 437 |
|
---|
[3225] | 438 | // "normalize" ao contribution
|
---|
[3192] | 439 | total_ao /= numSamples;
|
---|
| 440 |
|
---|
[3225] | 441 | #if 1
|
---|
| 442 | // if surface normal perpenticular to view dir, approx. half of the samples will not count
|
---|
| 443 | // => compensate for this (on the other hand, projected sampling area could be larger!)
|
---|
| 444 | const float viewCorrection = 1.0f + VIEW_CORRECTION_SCALE * max(dot(viewDir, normal), 0.0f);
|
---|
| 445 | total_ao *= viewCorrection;
|
---|
| 446 | #endif
|
---|
| 447 |
|
---|
[3271] | 448 | //return float3(total_ao, validSamples, numSamples);
|
---|
| 449 | return float3(min(1.0f, total_ao), validSamples, numSamples);
|
---|
[2881] | 450 | }
|
---|
| 451 |
|
---|
[3121] | 452 |
|
---|
[3150] | 453 |
|
---|
[2881] | 454 | /** The mrt shader for screen space ambient occlusion
|
---|
| 455 | */
|
---|
[3325] | 456 | pixel2 main(fragment IN,
|
---|
| 457 | uniform sampler2D colors,
|
---|
| 458 | uniform sampler2D normals,
|
---|
| 459 | uniform sampler2D noiseTex,
|
---|
| 460 | uniform sampler2D samples,
|
---|
| 461 | uniform sampler2D oldTex,
|
---|
| 462 | uniform float4x4 modelViewProj,
|
---|
| 463 | uniform float4x4 oldModelViewProj,
|
---|
| 464 | uniform float temporalCoherence,
|
---|
| 465 | uniform float3 bl,
|
---|
| 466 | uniform float3 br,
|
---|
| 467 | uniform float3 tl,
|
---|
| 468 | uniform float3 tr,
|
---|
| 469 | uniform float3 oldEyePos,
|
---|
| 470 | uniform float3 oldbl,
|
---|
| 471 | uniform float3 oldbr,
|
---|
| 472 | uniform float3 oldtl,
|
---|
| 473 | uniform float3 oldtr,
|
---|
| 474 | uniform sampler2D attribsTex,
|
---|
| 475 | uniform float kernelRadius,
|
---|
| 476 | uniform float sampleIntensity
|
---|
| 477 | )
|
---|
[2881] | 478 | {
|
---|
[3325] | 479 | pixel2 OUT;
|
---|
[2881] | 480 |
|
---|
[3167] | 481 | //const float3 normal = normalize(tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz);
|
---|
| 482 | const float3 normal = tex2Dlod(normals, float4(IN.texCoord, 0 ,0)).xyz;
|
---|
[2975] | 483 |
|
---|
[3082] | 484 | // reconstruct position from the eye space depth
|
---|
[3097] | 485 | const float3 viewDir = IN.view;
|
---|
[3089] | 486 | const float eyeSpaceDepth = tex2Dlod(colors, float4(IN.texCoord, 0, 0)).w;
|
---|
[3097] | 487 | const float4 eyeSpacePos = float4(-viewDir * eyeSpaceDepth, 1.0f);
|
---|
[3014] | 488 |
|
---|
[3341] | 489 |
|
---|
[3017] | 490 | ////////////////
|
---|
[3080] | 491 | //-- calculcate the current projected posiion (also used for next frame)
|
---|
[3017] | 492 |
|
---|
[3094] | 493 | float4 projPos = mul(modelViewProj, eyeSpacePos);
|
---|
[3112] | 494 | const float invw = 1.0f / projPos.w;
|
---|
| 495 | projPos *= invw;
|
---|
[3331] | 496 |
|
---|
| 497 | //const float radiusMult = kernelRadius;
|
---|
[3342] | 498 | //const float radiusMult = 3e-2;
|
---|
| 499 | const float radiusMult = kernelRadius * invw;
|
---|
[3331] | 500 |
|
---|
[3342] | 501 | #ifdef PERFORMANCE_TEST
|
---|
[3121] | 502 |
|
---|
[3345] | 503 | float3 diffVec = tex2Dlod(attribsTex, float4(IN.texCoord, .0f, .0f)).xyz;
|
---|
[3331] | 504 |
|
---|
[3213] | 505 | const float sqrMoveSpeed = SqrLen(diffVec);
|
---|
| 506 | const bool isMovingObject = (sqrMoveSpeed > DYNAMIC_OBJECTS_THRESHOLD);
|
---|
| 507 |
|
---|
[3017] | 508 |
|
---|
[3121] | 509 | /////////////////
|
---|
| 510 | //-- compute temporal reprojection
|
---|
| 511 |
|
---|
[3324] | 512 | float3 temporalVals = Reproject(eyeSpacePos, eyeSpaceDepth, IN.texCoord, oldEyePos,
|
---|
[3316] | 513 | oldTex, oldModelViewProj,
|
---|
| 514 | colors,
|
---|
| 515 | projPos.xyz,
|
---|
| 516 | invw,
|
---|
| 517 | oldbl, oldbr, oldtl, oldtr,
|
---|
| 518 | diffVec
|
---|
| 519 | );
|
---|
[3331] | 520 |
|
---|
[3121] | 521 | const float oldSsao = temporalVals.x;
|
---|
[3328] | 522 |
|
---|
[3345] | 523 | float oldIdx = temporalCoherence > 1 ? temporalVals.y : .0f;
|
---|
| 524 | //float oldWeight = temporalVals.y;
|
---|
| 525 | float oldWeight = clamp(oldIdx, 0, temporalCoherence);
|
---|
| 526 |
|
---|
[3331] | 527 | #else
|
---|
| 528 |
|
---|
| 529 | const bool isMovingObject = false;
|
---|
[3345] | 530 | const float oldSsao = .0f;
|
---|
[3331] | 531 |
|
---|
[3345] | 532 | float oldWeight = .0f;
|
---|
| 533 | float oldIdx = .0f;
|
---|
[3331] | 534 |
|
---|
| 535 | #endif
|
---|
| 536 |
|
---|
[3192] | 537 | float3 ao;
|
---|
[3137] | 538 |
|
---|
[3192] | 539 | // cull background note: this should be done with the stencil buffer
|
---|
[3304] | 540 | if (eyeSpaceDepth < DEPTH_THRESHOLD)
|
---|
[3192] | 541 | {
|
---|
[3340] | 542 | if (1)
|
---|
[3327] | 543 | {
|
---|
[3329] | 544 | ao = ssao(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz,
|
---|
[3331] | 545 | radiusMult, bl, br, tl, tr, normalize(viewDir),
|
---|
[3329] | 546 | oldWeight, sampleIntensity, isMovingObject, oldIdx);
|
---|
[3327] | 547 | }
|
---|
[3326] | 548 | else
|
---|
[3327] | 549 | {
|
---|
[3331] | 550 | ao = ssao2(IN, colors, noiseTex, samples, normal, eyeSpacePos.xyz, radiusMult,
|
---|
[3330] | 551 | bl, br, tl, tr, normalize(viewDir), oldWeight, sampleIntensity,
|
---|
| 552 | isMovingObject, normals, oldIdx);
|
---|
[3327] | 553 | }
|
---|
[3192] | 554 | }
|
---|
| 555 | else
|
---|
| 556 | {
|
---|
[3198] | 557 | ao = float3(1.0f, 1.0f, 1.0f);
|
---|
[3192] | 558 | }
|
---|
[3122] | 559 |
|
---|
[3331] | 560 |
|
---|
[3342] | 561 | #ifdef PERFORMANCE_TEST
|
---|
| 562 |
|
---|
[3213] | 563 | ///////////
|
---|
| 564 | //-- check if we have to reset pixel because one of the sample points was invalid
|
---|
| 565 | //-- only do this if the current pixel does not belong to a moving object
|
---|
[3205] | 566 |
|
---|
[3213] | 567 | // the weight equals the number of sampled shot in this pass
|
---|
| 568 | const float newWeight = ao.z;
|
---|
| 569 |
|
---|
[3225] | 570 | // completely reset the ao in this pixel
|
---|
[3306] | 571 | const float completelyResetThres = 20.0f;
|
---|
[3225] | 572 | // don't fully reset the ao in this pixel, but give low weight to old solution
|
---|
[3213] | 573 | const float partlyResetThres = 1.0f;
|
---|
| 574 |
|
---|
[3325] | 575 | // don't check for moving objects, otherwise almost no coherence
|
---|
[3328] | 576 | if (!isMovingObject)
|
---|
[3206] | 577 | {
|
---|
[3213] | 578 | if (ao.y > completelyResetThres)
|
---|
[3225] | 579 | {
|
---|
[3325] | 580 | oldWeight = .0f;
|
---|
| 581 | oldIdx = .0f;
|
---|
[3225] | 582 | }
|
---|
[3213] | 583 | else if (ao.y > partlyResetThres)
|
---|
[3225] | 584 | {
|
---|
[3345] | 585 | oldWeight = min(oldWeight, 4.0f * NUM_SAMPLES); oldIdx = oldWeight;
|
---|
| 586 | //oldWeight = .0f; oldIdx = .0f;
|
---|
[3225] | 587 | }
|
---|
[3328] | 588 | }
|
---|
[3206] | 589 |
|
---|
[3326] | 590 |
|
---|
[3213] | 591 | //////////
|
---|
| 592 | //-- blend ao between old and new samples (and avoid division by zero)
|
---|
[3225] | 593 |
|
---|
[3325] | 594 | OUT.illum_col.x = (ao.x * newWeight + oldSsao * oldWeight);
|
---|
[3311] | 595 | OUT.illum_col.x /= (newWeight + oldWeight);
|
---|
[3213] | 596 |
|
---|
[3227] | 597 | // the new weight for the next frame
|
---|
[3345] | 598 | const float newIdx = newWeight + oldIdx;
|
---|
| 599 | //const float combinedWeight = clamp(newIdx, .0f, temporalCoherence);
|
---|
| 600 | const float combinedWeight = clamp(newIdx, .0f, min(newWeight + oldWeight, temporalCoherence));
|
---|
[3313] | 601 |
|
---|
[3345] | 602 | //OUT.illum_col.y = combinedWeight;
|
---|
| 603 | OUT.illum_col.y = newIdx; // the new index
|
---|
[3325] | 604 | OUT.illum_col.w = eyeSpaceDepth;
|
---|
| 605 |
|
---|
[3327] | 606 | //if (OUT.illum_col.z > 1000) OUT.illum_col.z = 0;
|
---|
| 607 |
|
---|
[3326] | 608 | // this value can be used to check if this pixel belongs to a moving object
|
---|
[3345] | 609 | //OUT.col.x = SqrLen(diffVec);
|
---|
| 610 | OUT.illum_col.z = SqrLen(diffVec);
|
---|
[3326] | 611 |
|
---|
[3331] | 612 | #else
|
---|
| 613 |
|
---|
| 614 | OUT.illum_col.x = ao.x;
|
---|
| 615 | OUT.illum_col.w = eyeSpaceDepth;
|
---|
| 616 |
|
---|
| 617 | #endif
|
---|
| 618 |
|
---|
[2881] | 619 | return OUT;
|
---|
[3326] | 620 | } |
---|