From b4e5b606566c8e2847540b8d88e4e38a4d63adb1 Mon Sep 17 00:00:00 2001 From: samcake Date: Wed, 3 Aug 2016 15:45:22 -0700 Subject: [PATCH] fixing ao --- .../src/AmbientOcclusionEffect.cpp | 2 +- .../render-utils/src/AmbientOcclusionEffect.h | 2 +- .../render-utils/src/DeferredTransform.slh | 4 + .../render-utils/src/RenderDeferredTask.cpp | 8 +- libraries/render-utils/src/ssao.slh | 109 ++++++++++++++++++ .../render-utils/src/ssao_debugOcclusion.slf | 100 ++++------------ .../render-utils/src/ssao_makeOcclusion.slf | 59 +++------- .../utilities/render/ambientOcclusionPass.qml | 4 +- 8 files changed, 159 insertions(+), 129 deletions(-) diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 71222ee671..97132a0fc7 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -487,7 +487,7 @@ const gpu::PipelinePointer& DebugAmbientOcclusion::getDebugPipeline() { gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setColorWriteMask(true, true, true, false); - + state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA); // Good to go add the brand new pipeline _debugPipeline = gpu::Pipeline::create(program, state); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index ffc2c4d3e3..5a0e7da587 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -100,7 +100,7 @@ public: float numSpiralTurns{ 7.0f }; // defining an angle span to distribute the samples ray directions int numSamples{ 11 }; int resolutionLevel{ 0 }; - int blurRadius{ 0 }; // 0 means no blurring + int blurRadius{ 3 }; // 0 means no blurring bool ditheringEnabled{ true }; // randomize the distribution of rays per pixel, should always be true bool borderingEnabled{ true }; // avoid evaluating information from non existing pixels out of the frame, should always be true double gpuTime{ 0.0 }; diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index ec8cdac8c1..44f1073702 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -85,6 +85,10 @@ float getStereoSideHeight(int resolutionLevel) { return float(int(frameTransform._pixelInfo.w) >> resolutionLevel); } +vec2 getSideImageSize(int resolutionLevel) { + return vec2(float(int(frameTransform._stereoInfo.y) >> resolutionLevel), float(int(frameTransform._pixelInfo.w) >> resolutionLevel)); +} + ivec4 getStereoSideInfo(int xPos, int resolutionLevel) { int sideWidth = int(getStereoSideWidth(resolutionLevel)); return ivec4(xPos < sideWidth ? ivec2(0, 0) : ivec2(1, sideWidth), sideWidth, isStereo()); diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index bfb61b9dfb..7ea81eb706 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -178,14 +178,16 @@ RenderDeferredTask::RenderDeferredTask(CullFunctor cullFunctor) { // Debugging stages { + // Debugging Deferred buffer job + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); + addJob("DebugDeferredBuffer", debugFramebuffers); + addJob("DebugScattering", deferredLightingInputs); const auto debugAmbientOcclusionInputs = DebugAmbientOcclusion::Inputs(deferredFrameTransform, deferredFramebuffer, linearDepthTarget, ambientOcclusionUniforms).hasVarying(); addJob("DebugAmbientOcclusion", debugAmbientOcclusionInputs); - // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer)); - addJob("DebugDeferredBuffer", debugFramebuffers); + // Scene Octree Debuging job { diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index 01be0e463a..3a3203e2e3 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -126,6 +126,115 @@ float getBlurCoef(int c) { <@endfunc@> +<@func declareSamplingDisk()@> + +float evalDiskRadius(float Zeye, vec2 imageSize) { + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Zeye ) * getPerspectiveScale(); + + // clamp the disk to fit in the image otherwise too many unknown + ssDiskRadius = min(ssDiskRadius, imageSize.y * 0.5); + + return ssDiskRadius; +} + +const float TWO_PI = 6.28; + +vec3 getUnitTapLocation(int sampleNumber, float spinAngle){ + // Radius relative to ssR + float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; + return vec3(cos(angle), sin(angle), alpha); +} + +vec3 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { + vec3 tap = getUnitTapLocation(sampleNumber, spinAngle); + tap.xy *= tap.z; + tap *= outerRadius; + return tap; +} + +vec3 getTapLocationClamped(int sampleNumber, float spinAngle, float outerRadius, vec2 pixelPos, vec2 imageSize) { + vec3 tap = getTapLocation(sampleNumber, spinAngle, outerRadius); + vec2 tapPos = pixelPos + tap.xy; + + if (!(isBorderingEnabled() > 0.0)) { + return tap; + } + bool redoTap = false; + + if ((tapPos.x < 0.5)) { + tapPos.x = -tapPos.x; + redoTap = true; + } else if ((tapPos.x > imageSize.x - 0.5)) { + tapPos.x -= (imageSize.x - tapPos.x); + redoTap = true; + } + + if ((tapPos.y < 0.5)) { + tapPos.y = -tapPos.y; + redoTap = true; + } else if ((tapPos.y > imageSize.y - 0.5)) { + tapPos.y -= (imageSize.y - tapPos.y); + redoTap = true; + } + + if (redoTap) { + tap.xy = tapPos - pixelPos; + tap.z = length(tap.xy); + } +/* + if ((tapPos.x < 0.0) || (tapPos.x >= imageSize.x)) { + // tapPos.x = pixelPos.x - tapVec.x; + tap.x = -tap.x; + } + if ((tapPos.y < 0.0) || (tapPos.y >= imageSize.y)) { + // tapPos.y = pixelPos.y - tapVec.y; + tap.y = -tap.y; + }*/ + + return tap; +} + +<@endfunc@> + + +<@func declareFetchDepthPyramidMap()@> + +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 getOffsetPosition(ivec3 side, ivec2 ssC, vec3 tap, vec2 imageSize) { + // Derivation: + // mipLevel = floor(log(ssR / MAX_OFFSET)); + int mipLevel = clamp(findMSB(int(tap.z)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + + ivec2 ssP = ivec2(tap.xy) + ssC; + 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)) / imageSize; + P = evalEyePositionFromZeye(side.x, P.z, tapUV); + return P; +} + +<@endfunc@> + <@func declareBlurPass(axis)@> <$declarePackOcclusionDepth()$> diff --git a/libraries/render-utils/src/ssao_debugOcclusion.slf b/libraries/render-utils/src/ssao_debugOcclusion.slf index 0c40042ba2..7e6a792c3b 100644 --- a/libraries/render-utils/src/ssao_debugOcclusion.slf +++ b/libraries/render-utils/src/ssao_debugOcclusion.slf @@ -11,6 +11,9 @@ <@include ssao.slh@> <$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declareSamplingDisk()$> + <$declarePackOcclusionDepth()$> struct DebugParams{ @@ -24,15 +27,6 @@ uniform debugAmbientOcclusionBuffer { vec2 getDebugCursorTexcoord(){ return debugParams.pixelInfo.xy; } -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; -} out vec4 outFragColor; @@ -43,63 +37,7 @@ float getAngleDithering(in ivec2 pixelPos) { 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 getTapLocation(int sampleNumber, float spinAngle, float outerRadius) { - // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); - float angle = alpha * (getNumSpiralTurns() * TWO_PI) + spinAngle; - return vec2(cos(angle), sin(angle), alpha * outerRadius); -} - -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(getResolutionLevel()).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); - +float evalAO(in vec3 C, in vec3 n_C, vec3 Q) { vec3 v = Q - C; float vv = dot(v, v); float vn = dot(v, n_C); @@ -114,7 +52,9 @@ float sampleAO(in ivec3 side, in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssD void main(void) { // Pixel Debugged vec2 cursorUV = getDebugCursorTexcoord(); - ivec2 ssC = ivec2(cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel()))); + vec2 imageSize = getSideImageSize(getResolutionLevel()); + vec2 cursorPixelPos = cursorUV * imageSize; + ivec2 ssC = ivec2(cursorPixelPos); // Pixel being shaded //ivec2 ssC = ivec2(gl_FragCoord.xy); @@ -126,17 +66,16 @@ void main(void) { // From now on, ssC is the pixel pos in the side ssC.x -= side.y; - vec2 fragPos = (vec2(ssC) + vec2(0.5)) / vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())); + vec2 fragPos = (vec2(ssC) + vec2(0.5)) / imageSize; // 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(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); - vec2 fragToCursor = cursorUV * vec2(getStereoSideWidth(getResolutionLevel()), getStereoSideHeight(getResolutionLevel())) - gl_FragCoord.xy; + vec2 fragToCursor = cursorPixelPos - gl_FragCoord.xy; if (dot(fragToCursor,fragToCursor) > ssDiskRadius * ssDiskRadius) { discard; } @@ -149,18 +88,18 @@ void main(void) { float keepTapRadius = 2.0; bool keep = (dot(fragToCursor,fragToCursor) < keepTapRadius); for (int i = 0; i < getNumSamples(); ++i) { - vec3 tap = getTapLocation(i, randomPatternRotationAngle, outerRadius); - - vec2 fragToTap = vec2(ssC) + tap.xy * tap.z - gl_FragCoord.xy; + vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, cursorPixelPos, imageSize); + // The occluding point in camera space + vec2 fragToTap = vec2(ssC) + tap.xy - gl_FragCoord.xy; if (dot(fragToTap,fragToTap) < keepTapRadius) { keep = true; } - sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + + vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + + sum += evalAO(Cp, Cn, Q); } - if (!keep) { - discard; - } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); @@ -190,4 +129,9 @@ void main(void) { //outFragColor = vec4((v + vec3(1.0))* 0.5, 1.0); // outFragColor = vec4((Cn + vec3(1.0))* 0.5, 1.0); //outFragColor = vec4(vec3(ssDiskRadius / 100.0), 1.0); + + + if (!keep) { + outFragColor = vec4(0.1); + } } diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 7bad1420bf..739dc1ddcb 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -11,27 +11,12 @@ <@include ssao.slh@> <$declareAmbientOcclusion()$> +<$declareFetchDepthPyramidMap()$> +<$declareSamplingDisk()$> + <$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; @@ -40,18 +25,7 @@ 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)); @@ -77,19 +51,9 @@ vec3 getOffsetPosition(ivec3 side, ivec2 ssC, vec2 unitOffset, float ssR) { 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); +}*/ +float evalAO(in vec3 C, in vec3 n_C, in vec3 Q) { vec3 v = Q - C; float vv = dot(v, v); float vn = dot(v, n_C); @@ -104,6 +68,8 @@ void main(void) { // Pixel being shaded ivec2 ssC = ivec2(gl_FragCoord.xy); + vec2 imageSize = getSideImageSize(getResolutionLevel()); + // Fetch the z under the pixel (stereo or not) float Zeye = getZEye(ssC); @@ -119,8 +85,7 @@ void main(void) { vec3 Cn = evalEyeNormal(Cp); // Choose the screen-space sample radius - // proportional to the projected area of the sphere - float ssDiskRadius = -( getProjScale(getResolutionLevel()) * getRadius() / Cp.z ) * getPerspectiveScale(); + float ssDiskRadius = evalDiskRadius(Cp.z, imageSize); // Let's make noise float randomPatternRotationAngle = getAngleDithering(ssC); @@ -128,7 +93,11 @@ void main(void) { // Accumulate the Obscurance for each samples float sum = 0.0; for (int i = 0; i < getNumSamples(); ++i) { - sum += sampleAO(side.xyz, ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + vec3 tap = getTapLocationClamped(i, randomPatternRotationAngle, ssDiskRadius, ssC, imageSize); + + vec3 Q = getOffsetPosition(side.xyz, ssC, tap, imageSize); + + sum += evalAO(Cp, Cn, Q); } float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); diff --git a/scripts/developer/utilities/render/ambientOcclusionPass.qml b/scripts/developer/utilities/render/ambientOcclusionPass.qml index ae4ec7770b..527c8d8cd0 100644 --- a/scripts/developer/utilities/render/ambientOcclusionPass.qml +++ b/scripts/developer/utilities/render/ambientOcclusionPass.qml @@ -22,7 +22,9 @@ Column { model: [ "Radius:radius:2.0:false", "Level:obscuranceLevel:1.0:false", - "Scale:perspectiveScale:2.0:false", + "Num Taps:numSamples:32:true", + "Taps Spiral:numSpiralTurns:10.0:false", + "Blur Radius:blurRadius:10.0:false", ] ConfigSlider { label: qsTr(modelData.split(":")[0])