<@include gpu/Config.slh@> <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // // Created by Sam Gateau on 1/1/16. // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // <@include ssao.slh@> <$declareAmbientOcclusion()$> <$declarePackOcclusionDepth()$> const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; // the depth pyramid texture uniform sampler2D pyramidMap; float getZEye(ivec2 pixel) { return -texelFetch(pyramidMap, pixel, getResolutionLevel()).x; } vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { // compute the view space position using the depth // basically manually pick the proj matrix components to do the inverse float Xe = (-Zeye * (texcoord.x * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][0] - frameTransform._projection[side][3][0]) / frameTransform._projection[side][0][0]; float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * frameTransform._projection[side][2][1] - frameTransform._projection[side][3][1]) / frameTransform._projection[side][1][1]; return vec3(Xe, Ye, Zeye); } out vec4 outFragColor; uniform sampler2D normalMap; float getAngleDithering(in ivec2 pixelPos) { // Hash function used in the AlchemyAO paper return isDitheringEnabled() * (3 * pixelPos.x ^ pixelPos.y + pixelPos.x * pixelPos.y) * 10 + getFrameDithering(); } const float TWO_PI = 6.28; vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ // Radius relative to ssR float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; ssR = alpha; return vec2(cos(angle), sin(angle)); } vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { // Derivation: // mipLevel = floor(log(ssR / MAX_OFFSET)); int mipLevel = clamp(findMSB(int(ssR)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); ivec2 ssOffset = ivec2(ssR * unitOffset); ivec2 ssP = ssOffset + ssC; if (bool(isBorderingEnabled())) { ssP.x = ((ssP.x < 0 || ssP.x >= side.z) ? ssC.x - ssOffset.x : ssP.x); ssP.y = ((ssP.y < 0 || ssP.y >= int(getWidthHeight().y)) ? ssC.y - ssOffset.y : ssP.y); } ivec2 ssPFull = ivec2(ssP.x + side.y, ssP.y); vec3 P; // We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map. // Manually clamp to the texture size because texelFetch bypasses the texture unit ivec2 mipP = clamp(ssPFull >> mipLevel, ivec2(0), textureSize(pyramidMap, getResolutionLevel() + mipLevel) - ivec2(1)); P.z = -texelFetch(pyramidMap, mipP, getResolutionLevel() + mipLevel).r; // Offset to pixel center vec2 tapUV = (vec2(ssP) + vec2(0.5)) / float(side.z); P = evalEyePositionFromZeye(side.x, P.z, tapUV); return P; } float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in int tapIndex, in float randomPatternRotationAngle) { // Offset on the unit disk, spun for this pixel float ssR; vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR); ssR *= ssDiskRadius; // The occluding point in camera space vec3 Q = getOffsetPosition(side, ssC, unitOffset, ssR); vec3 v = Q - C; float vv = dot(v, v); float vn = dot(v, n_C); // Fall off function as recommended in SAO paper const float epsilon = 0.01; float f = max(getRadius2() - vv, 0.0); return f * f * f * max((vn - getFalloffBias()) / (epsilon + vv), 0.0); } void main(void) { // Pixel being shaded ivec2 ssC = ivec2(gl_FragCoord.xy); // Fetch the z under the pixel (stereo or not) float Zeye = getZEye(ssC); // Stereo side info ivec3 side = getStereoSideInfo(ssC.x); // From now on, ssC is the pixel pos in the side ssC.x -= side.y; vec2 fragPos = (vec2(ssC) + 0.5) / getStereoSideWidth(); // The position and normal of the pixel fragment in Eye space vec3 Cp = evalEyePositionFromZeye(side.x, Zeye, fragPos); vec3 Cn = evalEyeNormal(Cp); // Choose the screen-space sample radius // proportional to the projected area of the sphere float ssDiskRadius = -getProjScale() * getRadius() / Cp.z; // Let's make noise float randomPatternRotationAngle = getAngleDithering(ssC); // Accumulate the Obscurance for each samples float sum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { sum += sampleAO(side, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); outFragColor = vec4(packOcclusionDepth(A, CSZToDephtKey(Cp.z)), 1.0); }