diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index fe5ce1e668..ce697481d9 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -109,6 +109,21 @@ panel.newSlider("Ambient Occlusion Level", 0.0, 1.0, function() { return Render.ambientOcclusion.level; }, function (value) { return (value); }); +panel.newSlider("Ambient Occlusion Num Samples", 1, 32, + function (value) { Render.ambientOcclusion.numSamples = value; }, + function() { return Render.ambientOcclusion.numSamples; }, + function (value) { return (value); }); + +panel.newSlider("Ambient Occlusion Num Spiral Turns", 0.0, 30.0, + function (value) { Render.ambientOcclusion.numSpiralTurns = value; }, + function() { return Render.ambientOcclusion.numSpiralTurns; }, + function (value) { return (value); }); + +panel.newCheckbox("Ambient Occlusion Dithering", + function (value) { Render.ambientOcclusion.ditheringEnabled = value; }, + function() { return Render.ambientOcclusion.ditheringEnabled; }, + function (value) { return (value); }); + var tickTackPeriod = 500; function updateCounters() { diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index dcc1447935..44dfde13dc 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -177,11 +177,35 @@ void AmbientOcclusionEffect::setLevel(float level) { } } - void AmbientOcclusionEffect::setDithering(bool enabled) { if (enabled != isDitheringEnabled()) { - auto& current = _parametersBuffer.edit()._performanceCaps; - current.x = (float)enabled; + auto& current = _parametersBuffer.edit()._sampleInfo; + current.w = (float)enabled; + } +} + +void AmbientOcclusionEffect::setNumSamples(int numSamples) { + numSamples = std::max(1.f, (float) numSamples); + if (numSamples != getNumSamples()) { + auto& current = _parametersBuffer.edit()._sampleInfo; + current.x = numSamples; + current.y = 1.0 / numSamples; + } +} + +void AmbientOcclusionEffect::setNumSpiralTurns(float numTurns) { + numTurns = std::max(0.f, (float)numTurns); + if (numTurns != getNumSpiralTurns()) { + auto& current = _parametersBuffer.edit()._sampleInfo; + current.z = numTurns; + } +} + +void AmbientOcclusionEffect::setEdgeSharpness(float sharpness) { + sharpness = std::max(0.f, (float)sharpness); + if (sharpness != getEdgeSharpness()) { + auto& current = _parametersBuffer.edit()._blurInfo; + current.x = sharpness; } } @@ -314,6 +338,8 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext setDepthInfo(args->_viewFrustum->getNearClip(), args->_viewFrustum->getFarClip()); _parametersBuffer.edit()._projection[0] = monoProjMat; _parametersBuffer.edit()._pixelInfo = args->_viewport; + _parametersBuffer.edit()._ditheringInfo.y += 0.25f; + auto pyramidPipeline = getPyramidPipeline(); auto occlusionPipeline = getOcclusionPipeline(); diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 4c7093bebb..ce8a0abfe2 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -31,8 +31,19 @@ public: float getLevel() const { return _parametersBuffer.get()._radiusInfo.w; } void setDithering(bool enabled); - bool isDitheringEnabled() const { return _parametersBuffer.get()._performanceCaps.x; } + bool isDitheringEnabled() const { return _parametersBuffer.get()._ditheringInfo.w; } + // Number of samples per pixel to evaluate the Obscurance + void setNumSamples(int numSamples); + int getNumSamples() const { return (int)_parametersBuffer.get()._sampleInfo.x; } + + // Number of spiral turns defining an angle span to distribute the samples ray directions + void setNumSpiralTurns(float numTurns); + float getNumSpiralTurns() const { return _parametersBuffer.get()._sampleInfo.z; } + + // Edge blurring setting + void setEdgeSharpness(float sharpness); + int getEdgeSharpness() const { return (int)_parametersBuffer.get()._blurInfo.x; } using JobModel = render::Task::Job::Model; @@ -45,8 +56,12 @@ private: public: // radius info is { R, R^2, 1 / R^6, ObscuranceScale} glm::vec4 _radiusInfo{ 0.5, 0.5 * 0.5, 1.0 / (0.25 * 0.25 * 0.25), 1.0 }; - // Performance parameters to adjust the effect - glm::vec4 _performanceCaps{ 1.0, 1.0, 1.0, 1.0 }; + // Dithering info + glm::vec4 _ditheringInfo{ 1.0, 0.0, 0.0, 0.0 }; + // Sampling info + glm::vec4 _sampleInfo{ 11.0, 1.0/11.0, 7.0, 1.0 }; + // Blurring info + glm::vec4 _blurInfo{ 1.0, 0.0, 0.0, 0.0 }; // Pixel info is { viemport width height and stereo on off} glm::vec4 _pixelInfo; // Depth info is { n.f, f - n, -f} diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c7af44a8bb..a7bdd42b7e 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -183,7 +183,10 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend if (_occlusionJobIndex >= 0) { _jobs[_occlusionJobIndex].edit().setRadius(renderContext->getAmbientOcclusion().radius); _jobs[_occlusionJobIndex].edit().setLevel(renderContext->getAmbientOcclusion().level); + _jobs[_occlusionJobIndex].edit().setNumSamples(renderContext->getAmbientOcclusion().numSamples); + _jobs[_occlusionJobIndex].edit().setNumSpiralTurns(renderContext->getAmbientOcclusion().numSpiralTurns); _jobs[_occlusionJobIndex].edit().setDithering(renderContext->getAmbientOcclusion().ditheringEnabled); + _jobs[_occlusionJobIndex].edit().setEdgeSharpness(renderContext->getAmbientOcclusion().edgeSharpness); } setAntialiasingStatus(renderContext->getFxaaStatus()); diff --git a/libraries/render-utils/src/RenderScriptingInterface.h b/libraries/render-utils/src/RenderScriptingInterface.h index af6d67801b..30ff17c47b 100644 --- a/libraries/render-utils/src/RenderScriptingInterface.h +++ b/libraries/render-utils/src/RenderScriptingInterface.h @@ -72,7 +72,10 @@ namespace RenderScripting { public: Q_PROPERTY(float radius MEMBER radius) Q_PROPERTY(float level MEMBER level) + Q_PROPERTY(int numSamples MEMBER numSamples) + Q_PROPERTY(float numSpiralTurns MEMBER numSpiralTurns) Q_PROPERTY(bool ditheringEnabled MEMBER ditheringEnabled) + Q_PROPERTY(float edgeSharpness MEMBER edgeSharpness) }; using AmbientOcclusionPointer = std::unique_ptr; }; diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh index fa08f7c4bc..61bc6cbe89 100644 --- a/libraries/render-utils/src/ssao.slh +++ b/libraries/render-utils/src/ssao.slh @@ -33,7 +33,9 @@ vec2 unpackOcclusionDepth(vec3 raw) { struct AmbientOcclusionParams { vec4 _radiusInfo; - vec4 _performanceCaps; + vec4 _ditheringInfo; + vec4 _sampleInfo; + vec4 _blurInfo; vec4 _pixelInfo; vec4 _depthInfo; mat4 _projection[2]; @@ -67,7 +69,25 @@ float getObscuranceScaling() { } float isDitheringEnabled() { - return params._performanceCaps.x; + return params._ditheringInfo.x; +} +float getFrameDithering() { + return params._ditheringInfo.y; +} + + +float getNumSamples() { + return params._sampleInfo.x; +} +float getInvNumSamples() { + return params._sampleInfo.y; +} +float getNumSpiralTurns() { + return params._sampleInfo.z; +} + +float getBlurEdgeSharpness() { + return params._blurInfo.x; } float evalZeyeFromZdb(float depth) { @@ -92,6 +112,7 @@ vec3 evalEyeNormal(vec3 C) { <@func declareBlurPass(axis)@> <$declarePackOcclusionDepth()$> +<$declareAmbientOcclusion()$> // the source occlusion texture uniform sampler2D occlusionMap; @@ -108,7 +129,6 @@ vec2 fetchOcclusionDepth(ivec2 coords) { const int BLUR_RADIUS = 4; const int RADIUS_SCALE = 2; -const float EDGE_SHARPNESS = 1.0; const float gaussian[BLUR_RADIUS + 1] = // float[](0.356642, 0.239400, 0.072410, 0.009869); @@ -139,7 +159,7 @@ vec3 getBlurredOcclusion(vec2 coord) { float weight = 0.3 + gaussian[abs(r)]; // range domain (the "bilateral" weight). As depth difference increases, decrease weight. - weight *= max(0.0, 1.0 - (EDGE_SHARPNESS * 2000.0) * abs(tapOZ.y - key)); + weight *= max(0.0, 1.0 - (getBlurEdgeSharpness() * 2000.0) * abs(tapOZ.y - key)); sum += tapOZ.x * weight; totalWeight += weight; diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf index 03d5f04633..1da18b8ef8 100644 --- a/libraries/render-utils/src/ssao_makeOcclusion.slf +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -13,9 +13,7 @@ <$declareAmbientOcclusion()$> <$declarePackOcclusionDepth()$> -const int NUM_SAMPLES = 11; -const float INV_NUM_SAMPLES = 1.0 / float(NUM_SAMPLES); -const int NUM_SPIRAL_TURNS= 7; + const int LOG_MAX_OFFSET = 3; const int MAX_MIP_LEVEL = 5; @@ -32,10 +30,15 @@ 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(); +} + vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * INV_NUM_SAMPLES; - float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle; + float alpha = float(sampleNumber + 0.5) * getInvNumSamples(); + float angle = alpha * (getNumSpiralTurns() * 6.28) + spinAngle; ssR = alpha; return vec2(cos(angle), sin(angle)); @@ -53,8 +56,8 @@ vec3 getOffsetPosition(ivec2 ssC, vec2 unitOffset, float ssR) { // 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(ssP >> mipLevel, ivec2(0), textureSize(pyramidMap, mipLevel) - ivec2(1)); -// P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; - P.z = -texelFetch(pyramidMap, ssP, 0).r; + P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; +// P.z = -texelFetch(pyramidMap, ssP, 0).r; // Offset to pixel center //P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z); @@ -104,8 +107,7 @@ void main(void) { vec3 Cp = evalEyePosition(varTexCoord0); - // Hash function used in the HPG12 AlchemyAO paper - float randomPatternRotationAngle = isDitheringEnabled() * (3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10; + float randomPatternRotationAngle = getAngleDithering(ssC); vec3 Cn = evalEyeNormal(Cp); @@ -114,11 +116,11 @@ void main(void) { float ssDiskRadius = -getProjScale() * getRadius() / Cp.z; float sum = 0.0; - for (int i = 0; i < NUM_SAMPLES; ++i) { + for (int i = 0; i < getNumSamples(); ++i) { sum += sampleAO(ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); } - float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * INV_NUM_SAMPLES); + float A = max(0.0, 1.0 - sum * getObscuranceScaling() * 5.0 * getInvNumSamples()); // Bilateral box-filter over a quad for free, respecting depth edges // (the difference that this makes is subtle) diff --git a/libraries/render/src/render/Context.h b/libraries/render/src/render/Context.h index bfcf9eb858..fd6c25dfde 100644 --- a/libraries/render/src/render/Context.h +++ b/libraries/render/src/render/Context.h @@ -76,7 +76,10 @@ public: public: float radius = 0.5f; // radius in meters of the AO effect float level = 0.5f; // Level of the obscrance value + int numSamples = 11; // Num Samples per pixel + float numSpiralTurns = 7.0f; bool ditheringEnabled = true; + float edgeSharpness = 1.0f; }; RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode);