diff --git a/examples/utilities/tools/renderEngineDebug.js b/examples/utilities/tools/renderEngineDebug.js index e95f7dce3f..61aea270c3 100755 --- a/examples/utilities/tools/renderEngineDebug.js +++ b/examples/utilities/tools/renderEngineDebug.js @@ -12,7 +12,7 @@ Script.include("cookies.js"); var MENU = "Developer>Render>Debug Deferred Buffer"; -var ACTIONS = ["Off", "Diffuse", "Alpha", "Specular", "Roughness", "Normal", "Depth", "Lighting", "AmbientOcclusion", "Custom"]; +var ACTIONS = ["Off", "Diffuse", "AmbientOcclusion", "Specular", "Roughness", "Normal", "Depth", "Lighting", "PyramidDepth", "OcclusionRaw", "OcclusionBlurred", "Custom"]; var SETTINGS_KEY = "EngineDebugScript.DebugMode"; Number.prototype.clamp = function(min, max) { @@ -52,6 +52,7 @@ var overlaysCounter = new CounterWidget(panel, "Overlays", Render.overlay3D); var resizing = false; var previousMode = Settings.getValue(SETTINGS_KEY, -1); +previousMode = 1; Menu.addActionGroup(MENU, ACTIONS, ACTIONS[previousMode + 1]); Render.deferredDebugMode = previousMode; Render.deferredDebugSize = { x: 0.0, y: -1.0, z: 1.0, w: 1.0 }; // Reset to default size @@ -98,6 +99,11 @@ panel.newSlider("Tone Mapping Exposure", -10, 10, function() { return Render.tone.exposure; }, function (value) { return (value); }); +panel.newSlider("Ambient Occlusion Radius", 0.0, 2.0, + function (value) { Render.ambientOcclusion.radius = value; }, + function() { return Render.ambientOcclusion.radius; }, + 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 a593c09798..a62173d908 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -12,6 +12,9 @@ #include +#include //min max and more + + #include #include #include @@ -24,19 +27,16 @@ #include "ViewFrustum.h" #include "GeometryCache.h" -#include "ambient_occlusion_vert.h" -#include "ambient_occlusion_frag.h" -#include "gaussian_blur_vertical_vert.h" -#include "gaussian_blur_horizontal_vert.h" -#include "gaussian_blur_frag.h" -#include "occlusion_blend_frag.h" - - +#include "ssao_makePyramid_frag.h" +#include "ssao_makeOcclusion_frag.h" +#include "ssao_makeHorizontalBlur_frag.h" +#include "ssao_makeVerticalBlur_frag.h" const int AmbientOcclusionEffect_ParamsSlot = 0; const int AmbientOcclusionEffect_DeferredTransformSlot = 1; const int AmbientOcclusionEffect_DepthMapSlot = 0; const int AmbientOcclusionEffect_PyramidMapSlot = 0; +const int AmbientOcclusionEffect_OcclusionMapSlot = 0; AmbientOcclusionEffect::AmbientOcclusionEffect() { Parameters parameters; @@ -45,44 +45,8 @@ AmbientOcclusionEffect::AmbientOcclusionEffect() { const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() { if (!_pyramidPipeline) { - const char AO_frag[] = R"SCRIBE(#version 410 core - // - // Created by Sam Gateau on 12/23/2015 - // Copyright 2015 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 - // - - struct AmbientOcclusionParams { - vec4 _clipInfo; - mat4 _projection; - vec4 _radius_s0_s1_s2; - }; - - uniform ambientOcclusionParamsBuffer { - AmbientOcclusionParams params; - }; - - float evalZeyeFromZdb(float depth) { - return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z); - } - - // the depth texture - uniform sampler2D depthMap; - - in vec2 varTexCoord0; - out vec4 outFragColor; - - void main(void) { - float Zdb = texture(depthMap, varTexCoord0).x; - float Zeye = -evalZeyeFromZdb(Zdb); - outFragColor = vec4(Zeye, 0.0, 0.0, 1.0); - } - - )SCRIBE"; auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(AO_frag)); + auto ps = gpu::Shader::createPixel(std::string(ssao_makePyramid_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -108,210 +72,8 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getPyramidPipeline() { const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { if (!_occlusionPipeline) { - const char AO_frag[] = R"SCRIBE(#version 410 core - // - // Created by Sam Gateau on 12/23/2015 - // Copyright 2015 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 - // - - const int NUM_SAMPLES = 11; - const int NUM_SPIRAL_TURNS= 7; - const int LOG_MAX_OFFSET = 3; - const int MAX_MIP_LEVEL = 5; - - - // the depth texture - uniform sampler2D pyramidMap; - - float getZeye(vec2 texcoord) { - return -texture(pyramidMap, texcoord, 0).x; - } - - struct AmbientOcclusionParams { - vec4 _clipInfo; - mat4 _projection; - vec4 _radius_s0_s1_s2; - }; - - uniform ambientOcclusionParamsBuffer { - AmbientOcclusionParams params; - }; - - float getProjScale() { - return 500.0; // this should be viewportHeight * Proj[1][1] / 2.0 - } - - float getRadius() { - return params._radius_s0_s1_s2.x; - } - - vec3 evalEyePositionFromZeye(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 * params._projection[2][0] - params._projection[3][0]) / params._projection[0][0]; - float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * params._projection[2][1] - params._projection[3][1]) / params._projection[1][1]; - return vec3(Xe, Ye, Zeye); - } - - vec3 evalEyePosition(vec2 texcoord) { - float Zeye = getZeye(texcoord); - return evalEyePositionFromZeye(Zeye, texcoord); - } - - vec3 evalEyeNormal(vec3 C) { - return -normalize(cross(dFdy(C), dFdx(C))); - } - - - /** Used for packing Z into the GB channels */ - float CSZToKey(float z) { - return clamp(z * (1.0 / params._clipInfo.z), 0.0, 1.0); - } - - - /** Used for packing Z into the GB channels */ - void packKey(float key, out vec2 p) { - - // Round to the nearest 1/256.0 - float temp = floor(key * 256.0); - - - - - - - // Integer part - p.x = temp * (1.0 / 256.0); - - // Fractional part - p.y = key * 256.0 - temp; - } - - - vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR){ - // Radius relative to ssR - float alpha = float(sampleNumber + 0.5) * (1.0 / NUM_SAMPLES); - float angle = alpha * (NUM_SPIRAL_TURNS * 6.28) + spinAngle; - - ssR = alpha; - return vec2(cos(angle), sin(angle)); - } - in vec2 varTexCoord0; - - vec3 getOffsetPosition(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 ssP = ivec2(ssR * unitOffset) + ssC; - - 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(ssP >> mipLevel, ivec2(0), textureSize(pyramidMap, mipLevel) - ivec2(1)); - P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; - - // Offset to pixel center - //P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z); - - vec2 tapUV = (vec2(ssP) + vec2(0.5)) / textureSize(pyramidMap, 0); - P = evalEyePositionFromZeye(P.z, tapUV); - return P; - } - - - float sampleAO(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(ssC, unitOffset, ssR); - - vec3 v = Q - C; - - float vv = dot(v, v); - float vn = dot(v, n_C); - - const float bias = 0.01; - const float epsilon = 0.01; - const float radius2 = 0.5 * 0.5; - - // A: From the HPG12 paper - // Note large epsilon to avoid overdarkening within cracks - // return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6; - - // B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] - float f = max(radius2 - vv, 0.0); return f * f * f * max((vn - bias) / (epsilon + vv), 0.0); - - // C: Medium contrast (which looks better at high radii), no division. Note that the - // contribution still falls off with radius^2, but we've adjusted the rate in a way that is - // more computationally efficient and happens to be aesthetically pleasing. - // return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0); - - // D: Low contrast, no division operation - // return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0); - } - - - out vec4 outFragColor; - - vec3 debugValue(float f, float scale) { - if (f < 0.0) { - return vec3((scale + f) / scale, 0.0, 0.0); - } else { - return vec3(0.0, (scale - f) / scale, 0.0); - } - } - - void main(void) { - // Pixel being shaded - ivec2 ssC = ivec2(gl_FragCoord.xy); - - vec3 Cp = evalEyePosition(varTexCoord0); - - // packKey(CSZToKey(Cp.z), bilateralKey); - - // Hash function used in the HPG12 AlchemyAO paper - float randomPatternRotationAngle = (3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10; - - - vec3 Cn = evalEyeNormal(Cp); - - // Choose the screen-space sample radius - // proportional to the projected area of the sphere - float ssDiskRadius = -getProjScale() * getRadius() / Cp.z; - - float sum = 0.0; - for (int i = 0; i < NUM_SAMPLES; ++i) { - sum += sampleAO(ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); - } - - float intensityDivR6 = 1.0; - float A = max(0.0, 1.0 - sum * intensityDivR6 * (5.0 / NUM_SAMPLES)); - - // Bilateral box-filter over a quad for free, respecting depth edges - // (the difference that this makes is subtle) - if (abs(dFdx(Cp.z)) < 0.02) { - A -= dFdx(A) * ((ssC.x & 1) - 0.5); - } - if (abs(dFdy(Cp.z)) < 0.02) { - A -= dFdy(A) * ((ssC.y & 1) - 0.5); - } - - - //outFragColor = vec4(debugValue(Cn.y, 10), A); - outFragColor = vec4(debugValue(Cn.y, 1), A); - } - - )SCRIBE"; auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(AO_frag)); + auto ps = gpu::Shader::createPixel(std::string(ssao_makeOcclusion_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); gpu::Shader::BindingSet slotBindings; @@ -327,8 +89,7 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { // Stencil test all the ao passes for objects pixels only, not the background state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); - // state->setColorWriteMask(false, false, false, true); - state->setColorWriteMask(true, true, true, true); + state->setColorWriteMask(true, true, true, false); // Good to go add the brand new pipeline _occlusionPipeline = gpu::Pipeline::create(program, state); @@ -336,77 +97,148 @@ const gpu::PipelinePointer& AmbientOcclusionEffect::getOcclusionPipeline() { return _occlusionPipeline; } -const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { - if (!_vBlurPipeline) { - auto vs = gpu::Shader::createVertex(std::string(gaussian_blur_vertical_vert)); - auto ps = gpu::Shader::createPixel(std::string(gaussian_blur_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - - gpu::Shader::BindingSet slotBindings; - gpu::Shader::makeProgram(*program, slotBindings); - - gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - - state->setDepthTest(false, false, gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); - - // Link the horizontal blur FBO to texture - /* _vBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, - DependencyManager::get()->getFrameBufferSize().width(), DependencyManager::get()->getFrameBufferSize().height())); - auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - auto width = _vBlurBuffer->getWidth(); - auto height = _vBlurBuffer->getHeight(); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _vBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); - */ - // Good to go add the brand new pipeline - _vBlurPipeline = gpu::Pipeline::create(program, state); - } - return _vBlurPipeline; -} const gpu::PipelinePointer& AmbientOcclusionEffect::getHBlurPipeline() { if (!_hBlurPipeline) { - auto vs = gpu::Shader::createVertex(std::string(gaussian_blur_horizontal_vert)); - auto ps = gpu::Shader::createPixel(std::string(gaussian_blur_frag)); + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(ssao_makeHorizontalBlur_frag)); gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), AmbientOcclusionEffect_DeferredTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot)); + gpu::Shader::makeProgram(*program, slotBindings); - + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); - - state->setDepthTest(false, false, gpu::LESS_EQUAL); - - // Blend on transparent - state->setBlendFunction(false, - gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, - gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); - - /* // Link the horizontal blur FBO to texture - _hBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32, - DependencyManager::get()->getFrameBufferSize().width(), DependencyManager::get()->getFrameBufferSize().height())); - auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA); - auto width = _hBlurBuffer->getWidth(); - auto height = _hBlurBuffer->getHeight(); - auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - _hBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler)); - */ + + // Stencil test all the ao passes for objects pixels only, not the background + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + + state->setColorWriteMask(true, true, true, false); + // Good to go add the brand new pipeline _hBlurPipeline = gpu::Pipeline::create(program, state); } return _hBlurPipeline; } +const gpu::PipelinePointer& AmbientOcclusionEffect::getVBlurPipeline() { + if (!_vBlurPipeline) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = gpu::Shader::createPixel(std::string(ssao_makeVerticalBlur_frag)); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("ambientOcclusionParamsBuffer"), AmbientOcclusionEffect_ParamsSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("deferredTransformBuffer"), AmbientOcclusionEffect_DeferredTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("occlusionMap"), AmbientOcclusionEffect_OcclusionMapSlot)); + + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + // Stencil test all the ao passes for objects pixels only, not the background + state->setStencilTest(true, 0xFF, gpu::State::StencilTest(0, 0xFF, gpu::NOT_EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); + + // Vertical blur write just the final result Occlusion value in the alpha channel + state->setColorWriteMask(false, false, false, true); + + // Good to go add the brand new pipeline + _vBlurPipeline = gpu::Pipeline::create(program, state); + + const char blur_frag[] = R"SCRIBE(#version 410 core + // + // Created by Sam Gateau on 12/31/2015 + // Copyright 2015 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 + // + + struct AmbientOcclusionParams { + vec4 _clipInfo; + mat4 _projection; + vec4 _radius_s0_s1_s2; + }; + + uniform ambientOcclusionParamsBuffer { + AmbientOcclusionParams params; + }; + + float evalZeyeFromZdb(float depth) { + return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z); + } + + // the depth texture + uniform sampler2D depthMap; + + in vec2 varTexCoord0; + out vec4 outFragColor; + + ivec2 ssC = ivec2(gl_FragCoord.xy); + + vec4 temp = texelFetch(source, ssC, 0); + + keyPassThrough = temp.KEY_COMPONENTS; + float key = unpackKey(keyPassThrough); + + VALUE_TYPE sum = temp.VALUE_COMPONENTS; + + if (key == 1.0) { + // Sky pixel (if you aren't using depth keying, disable this test) + result = sum; + return; + } + + // Base weight for depth falloff. Increase this for more blurriness, + // decrease it for better edge discrimination + float BASE = gaussian[0]; + float totalWeight = BASE; + sum *= totalWeight; + + + for (int r = -R; r <= R; ++r) { + // We already handled the zero case above. This loop should be unrolled and the static branch optimized out, + // so the IF statement has no runtime cost + if (r != 0) { + temp = texelFetch(source, ssC + axis * (r * SCALE), 0); + float tapKey = unpackKey(temp.KEY_COMPONENTS); + VALUE_TYPE value = temp.VALUE_COMPONENTS; + + // spatial domain: offset gaussian tap + 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(tapKey - key) + ); + + sum += value * weight; + totalWeight += weight; + } + } + + const float epsilon = 0.0001; + result = sum / (totalWeight + epsilon); + + )SCRIBE"; + } + return _vBlurPipeline; +} + + void AmbientOcclusionEffect::setClipInfo(float nearZ, float farZ) { _parametersBuffer.edit()._clipInfo = glm::vec4(nearZ*farZ, farZ -nearZ, -farZ, 0.0f); } +void AmbientOcclusionEffect::setRadius(float radius) { + radius = std::max(0.01f, radius); + _parametersBuffer.edit()._radius_radius2_InvRadius6_s2 = glm::vec4(radius, radius * radius, 1.0f / pow(radius, 6.0f), 0.0f); +} + void AmbientOcclusionEffect::updateDeferredTransformBuffer(const render::RenderContextPointer& renderContext) { // Allocate the parameters buffer used by all the deferred shaders if (!_deferredTransformBuffer[0]._buffer) { @@ -515,7 +347,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto framebufferCache = DependencyManager::get(); auto depthBuffer = framebufferCache->getPrimaryDepthTexture(); auto pyramidFBO = framebufferCache->getDepthPyramidFramebuffer(); - auto occlusionFBO = framebufferCache->getDeferredFramebufferDepthColor(); + auto occlusionFBO = framebufferCache->getOcclusionFramebuffer(); + auto occlusionBlurredFBO = framebufferCache->getOcclusionBlurredFramebuffer(); + auto occlusionFinalFBO = framebufferCache->getDeferredFramebufferDepthColor(); QSize framebufferSize = framebufferCache->getFrameBufferSize(); float sMin = args->_viewport.x / (float)framebufferSize.width(); @@ -536,7 +370,9 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext auto pyramidPipeline = getPyramidPipeline(); auto occlusionPipeline = getOcclusionPipeline(); - + auto firstHBlurPipeline = getHBlurPipeline(); + auto lastVBlurPipeline = getVBlurPipeline(); + gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); @@ -557,11 +393,12 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext // Pyramid pass batch.setFramebuffer(pyramidFBO); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(args->_viewFrustum->getFarClip(), 0.0f, 0.0f, 0.0f)); batch.setPipeline(pyramidPipeline); batch.setResourceTexture(AmbientOcclusionEffect_DepthMapSlot, depthBuffer); batch.draw(gpu::TRIANGLE_STRIP, 4); - // + // Make pyramid mips batch.setFramebuffer(occlusionFBO); batch.generateTextureMips(pyramidFBO->getRenderBuffer(0)); @@ -570,5 +407,16 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext batch.setResourceTexture(AmbientOcclusionEffect_PyramidMapSlot, pyramidFBO->getRenderBuffer(0)); batch.draw(gpu::TRIANGLE_STRIP, 4); + // Blur 1 pass + batch.setFramebuffer(occlusionBlurredFBO); + batch.setPipeline(firstHBlurPipeline); + batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + // Blur 2 pass + batch.setFramebuffer(occlusionFinalFBO); + batch.setPipeline(lastVBlurPipeline); + batch.setResourceTexture(AmbientOcclusionEffect_OcclusionMapSlot, occlusionBlurredFBO->getRenderBuffer(0)); + batch.draw(gpu::TRIANGLE_STRIP, 4); }); } diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.h b/libraries/render-utils/src/AmbientOcclusionEffect.h index 302e73f022..e058e4e030 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.h +++ b/libraries/render-utils/src/AmbientOcclusionEffect.h @@ -24,6 +24,9 @@ public: void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); typedef render::Job::Model JobModel; + void setRadius(float radius); + float getRadius() const { return _parametersBuffer.get()._radius_radius2_InvRadius6_s2.x; } + private: void setClipInfo(float nearZ, float farZ); @@ -33,7 +36,7 @@ private: public: glm::vec4 _clipInfo; glm::mat4 _projection; - glm::vec4 _radius_s0_s1_s2{ 0.5, 0.0, 0.0, 0.0 }; + glm::vec4 _radius_radius2_InvRadius6_s2{ 0.5, 0.5 * 0.5, 1.0 / (0.25 * 0.25 * 0.25), 0.0 }; Parameters() {} }; @@ -56,8 +59,8 @@ private: const gpu::PipelinePointer& getPyramidPipeline(); const gpu::PipelinePointer& getOcclusionPipeline(); - const gpu::PipelinePointer& getHBlurPipeline(); - const gpu::PipelinePointer& getVBlurPipeline(); + const gpu::PipelinePointer& getHBlurPipeline(); // first + const gpu::PipelinePointer& getVBlurPipeline(); // second gpu::PipelinePointer _pyramidPipeline; gpu::PipelinePointer _occlusionPipeline; diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index fe71102a35..6e86942875 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -32,7 +32,9 @@ enum Slots { Specular, Depth, Lighting, - Pyramid + Pyramid, + OcclusionRaw, + OcclusionBlurred }; static const std::string DEEFAULT_DIFFUSE_SHADER { @@ -40,7 +42,7 @@ static const std::string DEEFAULT_DIFFUSE_SHADER { " return vec4(pow(texture(diffuseMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" " }" }; -static const std::string DEEFAULT_ALPHA_SHADER { +static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(texture(diffuseMap, uv).a), 1.0);" " }" @@ -70,12 +72,23 @@ static const std::string DEEFAULT_LIGHTING_SHADER { " return vec4(pow(texture(lightingMap, uv).xyz, vec3(1.0 / 2.2)), 1.0);" " }" }; -static const std::string DEFAULT_AMBIENT_OCCLUSION_SHADER { +static const std::string DEFAULT_PYRAMID_DEPTH_SHADER { "vec4 getFragmentColor() {" " return vec4(vec3(1.0 - texture(pyramidMap, uv).x * 0.01), 1.0);" //" return vec4(vec3(1.0 - textureLod(pyramidMap, uv, 3).x * 0.01), 1.0);" " }" }; +static const std::string DEFAULT_OCCLUSION_RAW_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(occlusionRawMap, uv).x), 1.0);" + " }" +}; +static const std::string DEFAULT_OCCLUSION_BLURRED_SHADER { + "vec4 getFragmentColor() {" + " return vec4(vec3(texture(occlusionBlurredMap, uv).x), 1.0);" + " }" +}; + static const std::string DEEFAULT_CUSTOM_SHADER { "vec4 getFragmentColor() {" " return vec4(1.0, 0.0, 0.0, 1.0);" @@ -106,8 +119,8 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string cus switch (mode) { case DiffuseMode: return DEEFAULT_DIFFUSE_SHADER; - case AlphaMode: - return DEEFAULT_ALPHA_SHADER; + case AmbientOcclusionMode: + return DEFAULT_AMBIENT_OCCLUSION_SHADER; case SpecularMode: return DEEFAULT_SPECULAR_SHADER; case RoughnessMode: @@ -118,8 +131,12 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Modes mode, std::string cus return DEEFAULT_DEPTH_SHADER; case LightingMode: return DEEFAULT_LIGHTING_SHADER; - case AmbientOcclusionMode: - return DEFAULT_AMBIENT_OCCLUSION_SHADER; + case PyramidDepthMode: + return DEFAULT_PYRAMID_DEPTH_SHADER; + case OcclusionRawMode: + return DEFAULT_OCCLUSION_RAW_SHADER; + case OcclusionBlurredMode: + return DEFAULT_OCCLUSION_BLURRED_SHADER; case CustomMode: return getFileContent(customFile, DEEFAULT_CUSTOM_SHADER); } @@ -168,6 +185,8 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Modes mode, std::st slotBindings.insert(gpu::Shader::Binding("depthMap", Depth)); slotBindings.insert(gpu::Shader::Binding("lightingMap", Lighting)); slotBindings.insert(gpu::Shader::Binding("pyramidMap", Pyramid)); + slotBindings.insert(gpu::Shader::Binding("occlusionRawMap", OcclusionRaw)); + slotBindings.insert(gpu::Shader::Binding("occlusionBlurredMap", OcclusionBlurred)); gpu::Shader::makeProgram(*program, slotBindings); auto pipeline = gpu::Pipeline::create(program, std::make_shared()); @@ -216,6 +235,8 @@ void DebugDeferredBuffer::run(const SceneContextPointer& sceneContext, const Ren batch.setResourceTexture(Depth, framebufferCache->getPrimaryDepthTexture()); batch.setResourceTexture(Lighting, framebufferCache->getLightingTexture()); batch.setResourceTexture(Pyramid, framebufferCache->getDepthPyramidTexture()); + batch.setResourceTexture(OcclusionRaw, framebufferCache->getOcclusionTexture()); + batch.setResourceTexture(OcclusionBlurred, framebufferCache->getOcclusionBlurredTexture()); const glm::vec4 color(1.0f, 1.0f, 1.0f, 1.0f); const glm::vec2 bottomLeft(renderContext->_deferredDebugSize.x, renderContext->_deferredDebugSize.y); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index abfdf682d9..4575c6ac05 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -27,13 +27,15 @@ public: private: enum Modes : uint8_t { DiffuseMode = 0, - AlphaMode, + AmbientOcclusionMode, SpecularMode, RoughnessMode, NormalMode, DepthMode, LightingMode, - AmbientOcclusionMode, + PyramidDepthMode, + OcclusionRawMode, + OcclusionBlurredMode, CustomMode // Needs to stay last }; diff --git a/libraries/render-utils/src/FramebufferCache.cpp b/libraries/render-utils/src/FramebufferCache.cpp index 58ee3f1170..4ebf14827f 100644 --- a/libraries/render-utils/src/FramebufferCache.cpp +++ b/libraries/render-utils/src/FramebufferCache.cpp @@ -47,6 +47,10 @@ void FramebufferCache::setFrameBufferSize(QSize frameBufferSize) { _lightingFramebuffer.reset(); _depthPyramidFramebuffer.reset(); _depthPyramidTexture.reset(); + _occlusionFramebuffer.reset(); + _occlusionTexture.reset(); + _occlusionBlurredFramebuffer.reset(); + _occlusionBlurredTexture.reset(); } } @@ -105,6 +109,18 @@ void FramebufferCache::createPrimaryFramebuffer() { _depthPyramidFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); _depthPyramidFramebuffer->setRenderBuffer(0, _depthPyramidTexture); _depthPyramidFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + + _occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB), width, height, defaultSampler)); + _occlusionFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _occlusionFramebuffer->setRenderBuffer(0, _occlusionTexture); + _occlusionFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); + + + _occlusionBlurredTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGB), width, height, defaultSampler)); + _occlusionBlurredFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create()); + _occlusionBlurredFramebuffer->setRenderBuffer(0, _occlusionBlurredTexture); + _occlusionBlurredFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat); } @@ -220,4 +236,32 @@ gpu::TexturePointer FramebufferCache::getDepthPyramidTexture() { createPrimaryFramebuffer(); } return _depthPyramidTexture; +} + +gpu::FramebufferPointer FramebufferCache::getOcclusionFramebuffer() { + if (!_occlusionFramebuffer) { + createPrimaryFramebuffer(); + } + return _occlusionFramebuffer; +} + +gpu::TexturePointer FramebufferCache::getOcclusionTexture() { + if (!_occlusionTexture) { + createPrimaryFramebuffer(); + } + return _occlusionTexture; +} + +gpu::FramebufferPointer FramebufferCache::getOcclusionBlurredFramebuffer() { + if (!_occlusionBlurredFramebuffer) { + createPrimaryFramebuffer(); + } + return _occlusionBlurredFramebuffer; +} + +gpu::TexturePointer FramebufferCache::getOcclusionBlurredTexture() { + if (!_occlusionBlurredTexture) { + createPrimaryFramebuffer(); + } + return _occlusionBlurredTexture; } \ No newline at end of file diff --git a/libraries/render-utils/src/FramebufferCache.h b/libraries/render-utils/src/FramebufferCache.h index 4916882d9b..afc84c40c2 100644 --- a/libraries/render-utils/src/FramebufferCache.h +++ b/libraries/render-utils/src/FramebufferCache.h @@ -43,6 +43,12 @@ public: gpu::FramebufferPointer getDepthPyramidFramebuffer(); gpu::TexturePointer getDepthPyramidTexture(); + + gpu::FramebufferPointer getOcclusionFramebuffer(); + gpu::TexturePointer getOcclusionTexture(); + gpu::FramebufferPointer getOcclusionBlurredFramebuffer(); + gpu::TexturePointer getOcclusionBlurredTexture(); + gpu::TexturePointer getLightingTexture(); gpu::FramebufferPointer getLightingFramebuffer(); @@ -87,6 +93,13 @@ private: gpu::FramebufferPointer _depthPyramidFramebuffer; gpu::TexturePointer _depthPyramidTexture; + + + gpu::FramebufferPointer _occlusionFramebuffer; + gpu::TexturePointer _occlusionTexture; + + gpu::FramebufferPointer _occlusionBlurredFramebuffer; + gpu::TexturePointer _occlusionBlurredTexture; QSize _frameBufferSize{ 100, 100 }; }; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 019bcf7b7b..9614229122 100755 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -158,12 +158,16 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend // TODO: turn on/off AO through menu item setOcclusionStatus(renderContext->getOcclusionStatus()); - + if (_occlusionJobIndex >= 0) { + _jobs[_occlusionJobIndex].edit().setRadius(renderContext->getAmbientOcclusion().radius); + } + setAntialiasingStatus(renderContext->getFxaaStatus()); setToneMappingExposure(renderContext->getTone().exposure); setToneMappingToneCurve(renderContext->getTone().toneCurve); + renderContext->getArgs()->_context->syncCache(); for (auto job : _jobs) { diff --git a/libraries/render-utils/src/RenderScriptingInterface.cpp b/libraries/render-utils/src/RenderScriptingInterface.cpp index a99dc814d5..6c7bbab6a9 100644 --- a/libraries/render-utils/src/RenderScriptingInterface.cpp +++ b/libraries/render-utils/src/RenderScriptingInterface.cpp @@ -42,7 +42,7 @@ QString RenderScripting::Tone::getCurve() const { render::RenderContext RenderScriptingInterface::getRenderContext() { render::RenderContext::ItemsConfig items{ *_opaque, *_transparent, *_overlay3D }; - return render::RenderContext{ items, *_tone, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode }; + return render::RenderContext{ items, *_tone, *_ambientOcclusion, _drawStatus, _drawHitEffect, _deferredDebugSize, _deferredDebugMode }; } void RenderScriptingInterface::setItemCounts(const render::RenderContext::ItemsConfig& items) { diff --git a/libraries/render-utils/src/RenderScriptingInterface.h b/libraries/render-utils/src/RenderScriptingInterface.h index 08226ef6df..b330c23fc9 100644 --- a/libraries/render-utils/src/RenderScriptingInterface.h +++ b/libraries/render-utils/src/RenderScriptingInterface.h @@ -65,6 +65,14 @@ namespace RenderScripting { void setCurve(const QString& curve); }; using TonePointer = std::unique_ptr; + + class AmbientOcclusion : public QObject, public render::RenderContext::AmbientOcclusion { + Q_OBJECT + + public: + Q_PROPERTY(float radius MEMBER radius) + }; + using AmbientOcclusionPointer = std::unique_ptr; }; class RenderScriptingInterface : public QObject, public Dependency { @@ -77,7 +85,8 @@ class RenderScriptingInterface : public QObject, public Dependency { Q_PROPERTY(RenderScripting::ItemCounter* overlay3D READ getOverlay3D) Q_PROPERTY(RenderScripting::Tone* tone READ getTone) - + Q_PROPERTY(RenderScripting::AmbientOcclusion* ambientOcclusion READ getAmbientOcclusion) + Q_PROPERTY(int displayItemStatus MEMBER _drawStatus) Q_PROPERTY(bool displayHitEffect MEMBER _drawHitEffect) @@ -96,12 +105,15 @@ protected: RenderScripting::ItemCounter* getOverlay3D() const { return _overlay3D.get(); } RenderScripting::Tone* getTone() const { return _tone.get(); } + RenderScripting::AmbientOcclusion* getAmbientOcclusion() const { return _ambientOcclusion.get(); } RenderScripting::ItemStatePointer _opaque = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; RenderScripting::ItemStatePointer _transparent = RenderScripting::ItemStatePointer{new RenderScripting::ItemState{}}; RenderScripting::ItemCounterPointer _overlay3D = RenderScripting::ItemCounterPointer{new RenderScripting::ItemCounter{}}; RenderScripting::TonePointer _tone = RenderScripting::TonePointer{ new RenderScripting::Tone{} }; + + RenderScripting::AmbientOcclusionPointer _ambientOcclusion = RenderScripting::AmbientOcclusionPointer{ new RenderScripting::AmbientOcclusion{} }; // Options int _drawStatus = 0; diff --git a/libraries/render-utils/src/ambient_occlusion.slf b/libraries/render-utils/src/ambient_occlusion.slf deleted file mode 100644 index 8ab78891b0..0000000000 --- a/libraries/render-utils/src/ambient_occlusion.slf +++ /dev/null @@ -1,279 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// ambient_occlusion.frag -// fragment shader -// -// Created by Niraj Venkat on 7/15/15. -// Copyright 2015 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 DeferredBufferWrite.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -// Based on NVidia HBAO implementation in D3D11 -// http://www.nvidia.co.uk/object/siggraph-2008-HBAO.html - -in vec2 varTexcoord; - -uniform sampler2D depthTexture; -uniform sampler2D normalTexture; - -uniform float g_scale; -uniform float g_bias; -uniform float g_sample_rad; -uniform float g_intensity; - -// the distance to the near clip plane -uniform float near; - -// scale factor for depth: (far - near) / far -uniform float depthScale; - -// offset for depth texture coordinates -uniform vec2 depthTexCoordOffset; - -// scale for depth texture coordinates -uniform vec2 depthTexCoordScale; - -// the resolution of the occlusion buffer -// and its inverse -uniform vec2 renderTargetRes; -uniform vec2 renderTargetResInv; - - - -const float PI = 3.14159265; - -const float AOStrength = 1.9; - - -// TODO: R (radius) should be exposed as a uniform parameter -const float R = 0.01; -const float R2 = 0.01*0.01; -const float NegInvR2 = - 1.0 / (0.01*0.01); - - - -// can't use tan to initialize a const value -const float TanBias = 0.57735027; // tan(30.0 * PI / 180.0); -const float MaxRadiusPixels = 50.0; - -const int NumDirections = 6; -const int NumSamples = 4; - -out vec4 outFragColor; - -/** - * Gets the normal in view space from a normal texture. - * uv: the uv texture coordinates to look up in the texture at. - */ -vec3 GetViewNormalFromTexture(vec2 uv) { - // convert [0,1] -> [-1,1], note: since we're normalizing - // we don't need to do v*2 - 1.0, we can just do a v-0.5 - return normalize(texture(normalTexture, uv).xyz - 0.5); -} - -/** - * Gets the linearized depth in view space. - * d: the depth value [0-1], usually from a depth texture to convert. - */ -float ViewSpaceZFromDepth(float d){ - return near / (d * depthScale - 1.0); -} - -/** - * Converts a uv coordinate and depth value into a 3D view space coordinate. - * uv: the uv coordinates to convert - * z: the view space depth of the uv coordinate. - */ -vec3 UVToViewSpace(vec2 uv, float z){ - return vec3((depthTexCoordOffset + varTexcoord * depthTexCoordScale) * z, z); -} - -/** - * Converts a uv coordinate into a 3D view space coordinate. - * The depth of the uv coord is determined from the depth texture. - * uv: the uv coordinates to convert - */ -vec3 GetViewPos(vec2 uv) { - float z = ViewSpaceZFromDepth(texture(depthTexture, uv).r); - return UVToViewSpace(uv, z); -} - - -float TanToSin(float x) { - return x * inversesqrt(x*x + 1.0); -} - -float InvLength(vec2 V) { - return inversesqrt(dot(V, V)); -} - -float Tangent(vec3 V) { - return V.z * InvLength(V.xy); -} - -float BiasedTangent(vec3 V) { - return V.z * InvLength(V.xy) + TanBias; -} - -float Tangent(vec3 P, vec3 S) { - return -(P.z - S.z) * InvLength(S.xy - P.xy); -} - -float Length2(vec3 V) { - return dot(V, V); -} - -vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) { - vec3 V1 = Pr - P; - vec3 V2 = P - Pl; - return (Length2(V1) < Length2(V2)) ? V1 : V2; -} - -vec2 SnapUVOffset(vec2 uv) { - return round(uv * renderTargetRes) * renderTargetResInv; -} - -float Falloff(float d2) { - return d2 * NegInvR2 + 1.0f; -} - -float HorizonOcclusion(vec2 deltaUV, vec3 P, vec3 dPdu, vec3 dPdv, float randstep, float numSamples) { - float ao = 0; - - // Offset the first coord with some noise - vec2 uv = varTexcoord + SnapUVOffset(randstep*deltaUV); - deltaUV = SnapUVOffset(deltaUV); - - // Calculate the tangent vector - vec3 T = deltaUV.x * dPdu + deltaUV.y * dPdv; - - // Get the angle of the tangent vector from the viewspace axis - float tanH = BiasedTangent(T); - float sinH = TanToSin(tanH); - - float tanS; - float d2; - vec3 S; - - // Sample to find the maximum angle - for (float s = 1; s <= numSamples; ++s) { - uv += deltaUV; - S = GetViewPos(uv); - tanS = Tangent(P, S); - d2 = Length2(S - P); - - // Is the sample within the radius and the angle greater? - if (d2 < R2 && tanS > tanH) { - float sinS = TanToSin(tanS); - // Apply falloff based on the distance - ao += Falloff(d2) * (sinS - sinH); - - tanH = tanS; - sinH = sinS; - } - } - return ao; -} - -vec2 RotateDirections(vec2 Dir, vec2 CosSin) { - return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, - Dir.x*CosSin.y + Dir.y*CosSin.x); -} - -void ComputeSteps(inout vec2 stepSizeUv, inout float numSteps, float rayRadiusPix, float rand) { - // Avoid oversampling if numSteps is greater than the kernel radius in pixels - numSteps = min(NumSamples, rayRadiusPix); - - // Divide by Ns+1 so that the farthest samples are not fully attenuated - float stepSizePix = rayRadiusPix / (numSteps + 1); - - // Clamp numSteps if it is greater than the max kernel footprint - float maxNumSteps = MaxRadiusPixels / stepSizePix; - if (maxNumSteps < numSteps) { - // Use dithering to avoid AO discontinuities - numSteps = floor(maxNumSteps + rand); - numSteps = max(numSteps, 1); - stepSizePix = MaxRadiusPixels / numSteps; - } - - // Step size in uv space - stepSizeUv = stepSizePix * renderTargetResInv; -} - -float getRandom(vec2 uv) { - return fract(sin(dot(uv.xy ,vec2(12.9898,78.233))) * 43758.5453); -} - -void main(void) { - mat4 projMatrix = getTransformCamera()._projection; - - float numDirections = NumDirections; - - vec3 P, Pr, Pl, Pt, Pb; - P = GetViewPos(varTexcoord); - - // Sample neighboring pixels - Pr = GetViewPos(varTexcoord + vec2( renderTargetResInv.x, 0)); - Pl = GetViewPos(varTexcoord + vec2(-renderTargetResInv.x, 0)); - Pt = GetViewPos(varTexcoord + vec2( 0, renderTargetResInv.y)); - Pb = GetViewPos(varTexcoord + vec2( 0,-renderTargetResInv.y)); - - // Calculate tangent basis vectors using the minimum difference - vec3 dPdu = MinDiff(P, Pr, Pl); - vec3 dPdv = MinDiff(P, Pt, Pb) * (renderTargetRes.y * renderTargetResInv.x); - - // Get the random samples from the noise function - vec3 random = vec3(getRandom(varTexcoord.xy), getRandom(varTexcoord.yx), getRandom(varTexcoord.xx)); - - // Calculate the projected size of the hemisphere - float w = P.z * projMatrix[2][3] + projMatrix[3][3]; - vec2 rayRadiusUV = (0.5 * R * vec2(projMatrix[0][0], projMatrix[1][1]) / w); // [-1,1] -> [0,1] uv - float rayRadiusPix = rayRadiusUV.x * renderTargetRes.x; - - float ao = 1.0; - - // Make sure the radius of the evaluated hemisphere is more than a pixel - if(rayRadiusPix > 1.0) { - ao = 0.0; - float numSteps; - vec2 stepSizeUV; - - // Compute the number of steps - ComputeSteps(stepSizeUV, numSteps, rayRadiusPix, random.z); - - float alpha = 2.0 * PI / numDirections; - - // Calculate the horizon occlusion of each direction - for(float d = 0; d < numDirections; ++d) { - float theta = alpha * d; - - // Apply noise to the direction - vec2 dir = RotateDirections(vec2(cos(theta), sin(theta)), random.xy); - vec2 deltaUV = dir * stepSizeUV; - - // Sample the pixels along the direction - ao += HorizonOcclusion( deltaUV, - P, - dPdu, - dPdv, - random.z, - numSteps); - } - - // Average the results and produce the final AO - ao = 1.0 - ao / numDirections * AOStrength; - } - - - outFragColor = vec4(vec3(ao), 1.0); -} diff --git a/libraries/render-utils/src/ambient_occlusion.slv b/libraries/render-utils/src/ambient_occlusion.slv deleted file mode 100644 index 46da3f5fd5..0000000000 --- a/libraries/render-utils/src/ambient_occlusion.slv +++ /dev/null @@ -1,26 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// ambient_occlusion.vert -// vertex shader -// -// Created by Niraj Venkat on 7/15/15. -// Copyright 2015 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 gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -out vec2 varTexcoord; - -void main(void) { - varTexcoord = inTexCoord0.xy; - gl_Position = inPosition; -} diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index 37ae32b675..3a96486d41 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -15,6 +15,8 @@ <@include DeferredBuffer.slh@> uniform sampler2D pyramidMap; +uniform sampler2D occlusionRawMap; +uniform sampler2D occlusionBlurredMap; in vec2 uv; out vec4 outFragColor; diff --git a/libraries/render-utils/src/gaussian_blur.slf b/libraries/render-utils/src/gaussian_blur.slf deleted file mode 100644 index 01c1bc8807..0000000000 --- a/libraries/render-utils/src/gaussian_blur.slf +++ /dev/null @@ -1,43 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// gaussian_blur.frag -// fragment shader -// -// Created by Niraj Venkat on 7/17/15. -// Copyright 2015 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 DeferredBufferWrite.slh@> - -// the interpolated normal -//in vec4 interpolatedNormal; - -in vec2 varTexcoord; -in vec2 varBlurTexcoords[14]; - -uniform sampler2D occlusionTexture; - -out vec4 outFragColor; -void main(void) { - outFragColor = vec4(0.0); - outFragColor += texture(occlusionTexture, varBlurTexcoords[0])*0.0044299121055113265; - outFragColor += texture(occlusionTexture, varBlurTexcoords[1])*0.00895781211794; - outFragColor += texture(occlusionTexture, varBlurTexcoords[2])*0.0215963866053; - outFragColor += texture(occlusionTexture, varBlurTexcoords[3])*0.0443683338718; - outFragColor += texture(occlusionTexture, varBlurTexcoords[4])*0.0776744219933; - outFragColor += texture(occlusionTexture, varBlurTexcoords[5])*0.115876621105; - outFragColor += texture(occlusionTexture, varBlurTexcoords[6])*0.147308056121; - outFragColor += texture(occlusionTexture, varTexcoord)*0.159576912161; - outFragColor += texture(occlusionTexture, varBlurTexcoords[7])*0.147308056121; - outFragColor += texture(occlusionTexture, varBlurTexcoords[8])*0.115876621105; - outFragColor += texture(occlusionTexture, varBlurTexcoords[9])*0.0776744219933; - outFragColor += texture(occlusionTexture, varBlurTexcoords[10])*0.0443683338718; - outFragColor += texture(occlusionTexture, varBlurTexcoords[11])*0.0215963866053; - outFragColor += texture(occlusionTexture, varBlurTexcoords[12])*0.00895781211794; - outFragColor += texture(occlusionTexture, varBlurTexcoords[13])*0.0044299121055113265; -} diff --git a/libraries/render-utils/src/gaussian_blur_horizontal.slv b/libraries/render-utils/src/gaussian_blur_horizontal.slv deleted file mode 100644 index 7f8fb1c87f..0000000000 --- a/libraries/render-utils/src/gaussian_blur_horizontal.slv +++ /dev/null @@ -1,43 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// guassian_blur_horizontal.vert -// vertex shader -// -// Created by Niraj Venkat on 7/17/15. -// Copyright 2015 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 gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -out vec2 varTexcoord; -out vec2 varBlurTexcoords[14]; - -void main(void) { - varTexcoord = inTexCoord0.xy; - gl_Position = inPosition; - - varBlurTexcoords[0] = varTexcoord + vec2(-0.028, 0.0); - varBlurTexcoords[1] = varTexcoord + vec2(-0.024, 0.0); - varBlurTexcoords[2] = varTexcoord + vec2(-0.020, 0.0); - varBlurTexcoords[3] = varTexcoord + vec2(-0.016, 0.0); - varBlurTexcoords[4] = varTexcoord + vec2(-0.012, 0.0); - varBlurTexcoords[5] = varTexcoord + vec2(-0.008, 0.0); - varBlurTexcoords[6] = varTexcoord + vec2(-0.004, 0.0); - varBlurTexcoords[7] = varTexcoord + vec2(0.004, 0.0); - varBlurTexcoords[8] = varTexcoord + vec2(0.008, 0.0); - varBlurTexcoords[9] = varTexcoord + vec2(0.012, 0.0); - varBlurTexcoords[10] = varTexcoord + vec2(0.016, 0.0); - varBlurTexcoords[11] = varTexcoord + vec2(0.020, 0.0); - varBlurTexcoords[12] = varTexcoord + vec2(0.024, 0.0); - varBlurTexcoords[13] = varTexcoord + vec2(0.028, 0.0); -} - \ No newline at end of file diff --git a/libraries/render-utils/src/gaussian_blur_vertical.slv b/libraries/render-utils/src/gaussian_blur_vertical.slv deleted file mode 100644 index 4a4d65dcf9..0000000000 --- a/libraries/render-utils/src/gaussian_blur_vertical.slv +++ /dev/null @@ -1,43 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// guassian_blur_vertical.vert -// vertex shader -// -// Created by Niraj Venkat on 7/17/15. -// Copyright 2015 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 gpu/Inputs.slh@> - -<@include gpu/Transform.slh@> - -<$declareStandardTransform()$> - -out vec2 varTexcoord; -out vec2 varBlurTexcoords[14]; - -void main(void) { - varTexcoord = inTexCoord0.xy; - gl_Position = inPosition; - - varBlurTexcoords[0] = varTexcoord + vec2(0.0, -0.028); - varBlurTexcoords[1] = varTexcoord + vec2(0.0, -0.024); - varBlurTexcoords[2] = varTexcoord + vec2(0.0, -0.020); - varBlurTexcoords[3] = varTexcoord + vec2(0.0, -0.016); - varBlurTexcoords[4] = varTexcoord + vec2(0.0, -0.012); - varBlurTexcoords[5] = varTexcoord + vec2(0.0, -0.008); - varBlurTexcoords[6] = varTexcoord + vec2(0.0, -0.004); - varBlurTexcoords[7] = varTexcoord + vec2(0.0, 0.004); - varBlurTexcoords[8] = varTexcoord + vec2(0.0, 0.008); - varBlurTexcoords[9] = varTexcoord + vec2(0.0, 0.012); - varBlurTexcoords[10] = varTexcoord + vec2(0.0, 0.016); - varBlurTexcoords[11] = varTexcoord + vec2(0.0, 0.020); - varBlurTexcoords[12] = varTexcoord + vec2(0.0, 0.024); - varBlurTexcoords[13] = varTexcoord + vec2(0.0, 0.028); -} - \ No newline at end of file diff --git a/libraries/render-utils/src/occlusion_blend.slf b/libraries/render-utils/src/occlusion_blend.slf deleted file mode 100644 index 58873d9884..0000000000 --- a/libraries/render-utils/src/occlusion_blend.slf +++ /dev/null @@ -1,27 +0,0 @@ -<@include gpu/Config.slh@> -<$VERSION_HEADER$> -// Generated on <$_SCRIBE_DATE$> -// -// occlusion_blend.frag -// fragment shader -// -// Created by Niraj Venkat on 7/20/15. -// Copyright 2015 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 DeferredBufferWrite.slh@> - -in vec2 varTexcoord; -out vec4 outFragColor; - -uniform sampler2D blurredOcclusionTexture; - -void main(void) { - vec4 occlusionColor = texture(blurredOcclusionTexture, varTexcoord); - - outFragColor = vec4(vec3(0.0), occlusionColor.r); - -} diff --git a/libraries/render-utils/src/ssao.slh b/libraries/render-utils/src/ssao.slh new file mode 100644 index 0000000000..9692127005 --- /dev/null +++ b/libraries/render-utils/src/ssao.slh @@ -0,0 +1,58 @@ + +<@if not SSAO_SLH@> +<@def SSAO_SLH@> + +<@func declareAmbientOcclusion()@> + +struct AmbientOcclusionParams { + vec4 _clipInfo; + mat4 _projection; + vec4 _radius_radius2_InvRadius6_s2; +}; + +uniform ambientOcclusionParamsBuffer { + AmbientOcclusionParams params; +}; + +float getProjScale() { + return 500.0; // this should be viewportHeight * Proj[1][1] / 2.0 +} + +float getRadius() { + return params._radius_radius2_InvRadius6_s2.x; +} +float getRadius2() { + return params._radius_radius2_InvRadius6_s2.y; +} +float getInvRadius6() { + return params._radius_radius2_InvRadius6_s2.z; +} + +float evalZeyeFromZdb(float depth) { + return params._clipInfo.x / (depth * params._clipInfo.y + params._clipInfo.z); +} + +vec3 evalEyePositionFromZeye(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 * params._projection[2][0] - params._projection[3][0]) / params._projection[0][0]; + float Ye = (-Zeye * (texcoord.y * 2.0 - 1.0) - Zeye * params._projection[2][1] - params._projection[3][1]) / params._projection[1][1]; + return vec3(Xe, Ye, Zeye); +} + +vec3 evalEyeNormal(vec3 C) { + return normalize(cross(dFdy(C), dFdx(C))); +} +<@endfunc@> + + +<@endif@> \ No newline at end of file diff --git a/libraries/render-utils/src/ssao_makeHorizontalBlur.slf b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf new file mode 100644 index 0000000000..dcef20c480 --- /dev/null +++ b/libraries/render-utils/src/ssao_makeHorizontalBlur.slf @@ -0,0 +1,23 @@ +<@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()$> + +// the depth texture +uniform sampler2D occlusionMap; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + outFragColor = texture(occlusionMap, varTexCoord0); +} diff --git a/libraries/render-utils/src/ssao_makeOcclusion.slf b/libraries/render-utils/src/ssao_makeOcclusion.slf new file mode 100644 index 0000000000..6793be6978 --- /dev/null +++ b/libraries/render-utils/src/ssao_makeOcclusion.slf @@ -0,0 +1,162 @@ +<@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()$> + +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; + +// the depth pyramid texture +uniform sampler2D pyramidMap; + +float getZeye(vec2 texcoord) { + return -texture(pyramidMap, texcoord, 0).x; +} + +vec3 evalEyePosition(vec2 texcoord) { + float Zeye = getZeye(texcoord); + return evalEyePositionFromZeye(Zeye, texcoord); +} + +in vec2 varTexCoord0; +out vec4 outFragColor; + +/** Used for packing Z into the GB channels */ +float CSZToKey(float z) { + return clamp(z * (1.0 / params._clipInfo.z), 0.0, 1.0); +} + +/** Used for packing Z into the GB channels */ +void packKey(float key, out vec2 p) { + // Round to the nearest 1/256.0 + float temp = floor(key * 256.0); + + // Integer part + p.x = temp * (1.0 / 256.0); + + // Fractional part + p.y = key * 256.0 - temp; +} + +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; + + ssR = alpha; + return vec2(cos(angle), sin(angle)); +} + +vec3 getOffsetPosition(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 ssP = ivec2(ssR * unitOffset) + ssC; + + 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(ssP >> mipLevel, ivec2(0), textureSize(pyramidMap, mipLevel) - ivec2(1)); + P.z = -texelFetch(pyramidMap, mipP, mipLevel).r; + + // Offset to pixel center + //P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z); + + vec2 tapUV = (vec2(ssP) + vec2(0.5)) / textureSize(pyramidMap, 0); + P = evalEyePositionFromZeye(P.z, tapUV); + return P; +} + +float sampleAO(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(ssC, unitOffset, ssR); + + vec3 v = Q - C; + float vv = dot(v, v); + float vn = dot(v, n_C); + + const float bias = 0.01; + const float epsilon = 0.01; + float radius2 = getRadius2(); + + // A: From the HPG12 paper + // Note large epsilon to avoid overdarkening within cracks + // return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6; + + // B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] + float f = max(radius2 - vv, 0.0); return f * f * f * max((vn - bias) / (epsilon + vv), 0.0); + + // C: Medium contrast (which looks better at high radii), no division. Note that the + // contribution still falls off with radius^2, but we've adjusted the rate in a way that is + // more computationally efficient and happens to be aesthetically pleasing. + // return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0); + + // D: Low contrast, no division operation + // return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0); +} + +vec3 debugValue(float f, float scale) { + if (f < 0.0) { + return vec3((scale + f) / scale, 0.0, 0.0); + } else { + return vec3(0.0, (scale - f) / scale, 0.0); + } +} + +void main(void) { + // Pixel being shaded + ivec2 ssC = ivec2(gl_FragCoord.xy); + + vec3 Cp = evalEyePosition(varTexCoord0); + + // packKey(CSZToKey(Cp.z), bilateralKey); + + // Hash function used in the HPG12 AlchemyAO paper + float randomPatternRotationAngle = (3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10; + //float randomPatternRotationAngle = 0; + + + vec3 Cn = evalEyeNormal(Cp); + + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + float ssDiskRadius = -getProjScale() * getRadius() / Cp.z; + + float sum = 0.0; + for (int i = 0; i < NUM_SAMPLES; ++i) { + sum += sampleAO(ssC, Cp, Cn, ssDiskRadius, i, randomPatternRotationAngle); + } + + float A = max(0.0, 1.0 - sum * getInvRadius6() * 5.0 * INV_NUM_SAMPLES); + + // Bilateral box-filter over a quad for free, respecting depth edges + // (the difference that this makes is subtle) + if (abs(dFdx(Cp.z)) < 0.02) { + A -= dFdx(A) * ((ssC.x & 1) - 0.5); + } + if (abs(dFdy(Cp.z)) < 0.02) { + A -= dFdy(A) * ((ssC.y & 1) - 0.5); + } + + + outFragColor = vec4(A, debugValue(Cn.y, 10)); +} diff --git a/libraries/render-utils/src/ssao_makePyramid.slf b/libraries/render-utils/src/ssao_makePyramid.slf new file mode 100644 index 0000000000..577c0fd232 --- /dev/null +++ b/libraries/render-utils/src/ssao_makePyramid.slf @@ -0,0 +1,25 @@ +<@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()$> + +// the depth texture +uniform sampler2D depthMap; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + float Zdb = texture(depthMap, varTexCoord0).x; + float Zeye = -evalZeyeFromZdb(Zdb); + outFragColor = vec4(Zeye, 0.0, 0.0, 1.0); +} diff --git a/libraries/render-utils/src/ssao_makeVerticalBlur.slf b/libraries/render-utils/src/ssao_makeVerticalBlur.slf new file mode 100644 index 0000000000..e1ed114817 --- /dev/null +++ b/libraries/render-utils/src/ssao_makeVerticalBlur.slf @@ -0,0 +1,23 @@ +<@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()$> + +// the depth texture +uniform sampler2D occlusionMap; + +in vec2 varTexCoord0; +out vec4 outFragColor; + +void main(void) { + outFragColor = vec4(0.0, 0.0, 0.0, texture(occlusionMap, varTexCoord0).x); +} diff --git a/libraries/render/src/render/Engine.cpp b/libraries/render/src/render/Engine.cpp index 907c836347..6d065a3c58 100644 --- a/libraries/render/src/render/Engine.cpp +++ b/libraries/render/src/render/Engine.cpp @@ -13,11 +13,11 @@ #include "DrawTask.h" using namespace render; -RenderContext::RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) +RenderContext::RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode) : _deferredDebugMode{ deferredDebugMode }, _deferredDebugSize{ deferredDebugSize }, _args{ nullptr }, _drawStatus{ drawStatus }, _drawHitEffect{ drawHitEffect }, - _items{ items }, _tone{ tone } {} +_items{ items }, _tone{ tone }, _ambientOcclusion{ ao } {} void RenderContext::setOptions(bool occlusion, bool fxaa, bool showOwned) { _occlusionStatus = occlusion; diff --git a/libraries/render/src/render/Engine.h b/libraries/render/src/render/Engine.h index 4192dd3ed9..277d66ce35 100644 --- a/libraries/render/src/render/Engine.h +++ b/libraries/render/src/render/Engine.h @@ -76,14 +76,20 @@ public: int toneCurve = 1; // Means just Gamma 2.2 correction float exposure = 0.0; }; + + class AmbientOcclusion { + public: + float radius = 0.5; // radius in meters of the AO effect + }; - RenderContext(ItemsConfig items, Tone tone, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); - RenderContext() : RenderContext({}, {}, {}, {}, {}, {}) {}; + RenderContext(ItemsConfig items, Tone tone, AmbientOcclusion ao, int drawStatus, bool drawHitEffect, glm::vec4 deferredDebugSize, int deferredDebugMode); + RenderContext() : RenderContext({}, {}, {}, {}, {}, {}, {}) {}; void setArgs(RenderArgs* args) { _args = args; } inline RenderArgs* getArgs() { return _args; } inline ItemsConfig& getItemsConfig() { return _items; } inline Tone& getTone() { return _tone; } + inline AmbientOcclusion& getAmbientOcclusion() { return _ambientOcclusion; } inline int getDrawStatus() { return _drawStatus; } inline bool getDrawHitEffect() { return _drawHitEffect; } inline bool getOcclusionStatus() { return _occlusionStatus; } @@ -105,6 +111,7 @@ protected: ItemsConfig _items; Tone _tone; + AmbientOcclusion _ambientOcclusion; }; typedef std::shared_ptr RenderContextPointer;