diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index c26f3b613c..cb7ef696ad 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -51,7 +51,7 @@ #include "AmbientOcclusionEffect.h" #include "AntialiasingEffect.h" -#include "ToneMappingEffect.h" +#include "ToneMapAndResampleTask.h" #include "SubsurfaceScattering.h" #include "DrawHaze.h" #include "BloomEffect.h" @@ -239,8 +239,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping - const auto toneMappingInputs = ToneMappingDeferred::Input(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappingInputs = ToneMapAndResample::Input(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapAndResample", toneMappingInputs); // Debugging task is happening in the "over" layer after tone mapping and just before HUD { // Debug the bounds of the rendered items, still look at the zbuffer @@ -250,11 +250,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DebugRenderDeferredTask", debugInputs); } - // Upscale to finale resolution - const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", toneMappedBuffer); - // HUD Layer - const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); + const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); } diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index b6b17ee376..0c13d2cfb8 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -28,7 +28,7 @@ #include "StencilMaskPass.h" #include "ZoneRenderer.h" #include "FadeEffect.h" -#include "ToneMappingEffect.h" +#include "ToneMapAndResampleTask.h" #include "BackgroundStage.h" #include "FramebufferCache.h" #include "TextureCache.h" @@ -159,16 +159,13 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend // Lighting Buffer ready for tone mapping // Forward rendering on GLES doesn't support tonemapping to and from the same FBO, so we specify // the output FBO as null, which causes the tonemapping to target the blit framebuffer - const auto toneMappingInputs = ToneMappingDeferred::Input(resolvedFramebuffer, resolvedFramebuffer).asVarying(); - const auto toneMappedBuffer = task.addJob("ToneMapping", toneMappingInputs); + const auto toneMappingInputs = ToneMapAndResample::Input(resolvedFramebuffer, resolvedFramebuffer).asVarying(); + const auto toneMappedBuffer = task.addJob("ToneMapAndResample", toneMappingInputs); #endif - // Upscale to finale resolution - const auto primaryFramebuffer = task.addJob("PrimaryBufferUpscale", toneMappedBuffer); - // HUD Layer - const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(primaryFramebuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); + const auto renderHUDLayerInputs = RenderHUDLayerTask::Input(toneMappedBuffer, lightingModel, hudOpaque, hudTransparent, hazeFrame).asVarying(); task.addJob("RenderHUDLayer", renderHUDLayerInputs); } diff --git a/libraries/render-utils/src/ToneMapAndResampleTask.cpp b/libraries/render-utils/src/ToneMapAndResampleTask.cpp new file mode 100644 index 0000000000..521c23685d --- /dev/null +++ b/libraries/render-utils/src/ToneMapAndResampleTask.cpp @@ -0,0 +1,139 @@ +// +// ToneMapAndResampleTask.cpp +// libraries/render-utils/src +// +// Created by Sam Gateau on 12/7/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 +// + +#include "ToneMapAndResampleTask.h" + +#include +#include + +#include "render-utils/ShaderConstants.h" +#include "StencilMaskPass.h" +#include "FramebufferCache.h" + +using namespace shader::render_utils::program; + +gpu::PipelinePointer ToneMapAndResample::_pipeline; +gpu::PipelinePointer ToneMapAndResample::_mirrorPipeline; + +ToneMapAndResample::ToneMapAndResample() { + Parameters parameters; + _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); +} + +void ToneMapAndResample::init(RenderArgs* args) { + + // shared_ptr to gpu::State + gpu::StatePointer blitState = gpu::StatePointer(new gpu::State()); + + // TODO why was this in the upsample task + //blitState->setDepthTest(gpu::State::DepthTest(false, false)); + blitState->setColorWriteMask(true, true, true, true); + + _pipeline = gpu::PipelinePointer(gpu::Pipeline::create(gpu::Shader::createProgram(toneMapping), blitState)); + _mirrorPipeline = gpu::PipelinePointer(gpu::Pipeline::create(gpu::Shader::createProgram(toneMappingMirroredX), blitState)); +} + +void ToneMapAndResample::setExposure(float exposure) { + auto& params = _parametersBuffer.get(); + if (params._exposure != exposure) { + _parametersBuffer.edit()._exposure = exposure; + _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); + } +} + +void ToneMapAndResample::setToneCurve(ToneCurve curve) { + auto& params = _parametersBuffer.get(); + if (params._toneCurve != (int)curve) { + _parametersBuffer.edit()._toneCurve = (int)curve; + } +} + +gpu::FramebufferPointer ToneMapAndResample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer) { + if (_factor == 1.0f) { + return sourceFramebuffer; + } + + auto resampledFramebufferSize = glm::uvec2(glm::vec2(sourceFramebuffer->getSize()) * _factor); + + if (!_destinationFrameBuffer || resampledFramebufferSize != _destinationFrameBuffer->getSize()) { + _destinationFrameBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("ResampledOutput")); + + auto sampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + auto target = gpu::Texture::createRenderBuffer(sourceFramebuffer->getRenderBuffer(0)->getTexelFormat(), resampledFramebufferSize.x, resampledFramebufferSize.y, gpu::Texture::SINGLE_MIP, sampler); + _destinationFrameBuffer->setRenderBuffer(0, target); + } + return _destinationFrameBuffer; +} + +// TODO why was destination const +void ToneMapAndResample::render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationFramebuffer) { + + if (!_pipeline) { + init(args); + } + + if (!lightingBuffer || !destinationFramebuffer) { + return; + } + + //auto framebufferSize = glm::ivec2(lightingBuffer->getDimensions()); + + auto framebufferSize = destinationFramebuffer->getSize(); + + gpu::doInBatch("ToneMapAndResample::render", args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setFramebuffer(destinationFramebuffer); + + // FIXME: Generate the Luminosity map + //batch.generateTextureMips(lightingBuffer); + + batch.setViewportTransform(args->_viewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); + batch.setPipeline(args->_renderMode == RenderArgs::MIRROR_RENDER_MODE ? _mirrorPipeline : _pipeline); + + batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); + batch.setResourceTexture(render_utils::slot::texture::ToneMappingColor, lightingBuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + }); + + destinationFramebuffer = getResampledFrameBuffer(destinationFramebuffer); + + const auto bufferSize = destinationFramebuffer->getSize(); + glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + + //Set full final viewport + args->_viewport = viewport; +} + +void ToneMapAndResample::configure(const Config& config) { + setExposure(config.exposure); + setToneCurve((ToneCurve)config.curve); +} + +void ToneMapAndResample::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) { + + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + RenderArgs* args = renderContext->args; + + auto lightingBuffer = input.get0()->getRenderBuffer(0); + + auto resampledFramebuffer = args->_blitFramebuffer; + + render(args, lightingBuffer, resampledFramebuffer); + + output = resampledFramebuffer; +} + + diff --git a/libraries/render-utils/src/ToneMappingEffect.h b/libraries/render-utils/src/ToneMapAndResampleTask.h similarity index 65% rename from libraries/render-utils/src/ToneMappingEffect.h rename to libraries/render-utils/src/ToneMapAndResampleTask.h index faf6e514e9..439146e05f 100644 --- a/libraries/render-utils/src/ToneMappingEffect.h +++ b/libraries/render-utils/src/ToneMapAndResampleTask.h @@ -1,5 +1,5 @@ // -// ToneMappingEffect.h +// ToneMapAndResample.h // libraries/render-utils/src // // Created by Sam Gateau on 12/7/2015. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_ToneMappingEffect_h -#define hifi_ToneMappingEffect_h +#ifndef hifi_ToneMapAndResample_h +#define hifi_ToneMapAndResample_h #include #include @@ -20,27 +20,64 @@ #include #include +enum class ToneCurve { + // Different tone curve available + None, + Gamma22, + Reinhard, + Filmic, +}; -class ToneMappingEffect { +class ToneMappingConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); + Q_PROPERTY(int curve MEMBER curve WRITE setCurve); public: - ToneMappingEffect(); - virtual ~ToneMappingEffect() {} + ToneMappingConfig() : render::Job::Config(true) {} - void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& destinationBuffer); + void setExposure(float newExposure) { exposure = newExposure; emit dirty(); } + void setCurve(int newCurve) { curve = std::max((int)ToneCurve::None, std::min((int)ToneCurve::Filmic, newCurve)); emit dirty(); } + + + float exposure{ 0.0f }; + int curve{ (int)ToneCurve::Gamma22 }; +signals: + void dirty(); +}; + +class ToneMapAndResample { +public: + ToneMapAndResample(); + virtual ~ToneMapAndResample() {} + + void render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, gpu::FramebufferPointer& destinationBuffer); void setExposure(float exposure); float getExposure() const { return _parametersBuffer.get()._exposure; } - // Different tone curve available - enum ToneCurve { - None = 0, - Gamma22, - Reinhard, - Filmic, - }; void setToneCurve(ToneCurve curve); ToneCurve getToneCurve() const { return (ToneCurve)_parametersBuffer.get()._toneCurve; } + // Inputs: lightingFramebuffer, destinationFramebuffer + using Input = render::VaryingSet2; + using Output = gpu::FramebufferPointer; + using Config = ToneMappingConfig; + using JobModel = render::Job::ModelIO; + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); + +protected: + + static gpu::PipelinePointer _pipeline; + static gpu::PipelinePointer _mirrorPipeline; + + gpu::FramebufferPointer _destinationFrameBuffer; + + float _factor{ 2.0f }; + + gpu::FramebufferPointer ToneMapAndResample::getResampledFrameBuffer(const gpu::FramebufferPointer& sourceFramebuffer); + private: gpu::PipelinePointer _blitLightBuffer; @@ -51,7 +88,7 @@ private: float _exposure = 0.0f; float _twoPowExposure = 1.0f; glm::vec2 spareA; - int _toneCurve = Gamma22; + int _toneCurve = (int)ToneCurve::Gamma22; glm::vec3 spareB; Parameters() {} @@ -62,35 +99,4 @@ private: void init(RenderArgs* args); }; -class ToneMappingConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(float exposure MEMBER exposure WRITE setExposure); - Q_PROPERTY(int curve MEMBER curve WRITE setCurve); -public: - ToneMappingConfig() : render::Job::Config(true) {} - - void setExposure(float newExposure) { exposure = newExposure; emit dirty(); } - void setCurve(int newCurve) { curve = std::max((int)ToneMappingEffect::None, std::min((int)ToneMappingEffect::Filmic, newCurve)); emit dirty(); } - - - float exposure{ 0.0f }; - int curve{ ToneMappingEffect::Gamma22 }; -signals: - void dirty(); -}; - -class ToneMappingDeferred { -public: - // Inputs: lightingFramebuffer, destinationFramebuffer - using Input = render::VaryingSet2; - using Output = gpu::FramebufferPointer; - using Config = ToneMappingConfig; - using JobModel = render::Job::ModelIO; - - void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, const Input& input, Output& output); - - ToneMappingEffect _toneMappingEffect; -}; - -#endif // hifi_ToneMappingEffect_h +#endif // hifi_ToneMapAndResample_h diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp deleted file mode 100644 index b7cc5d3d80..0000000000 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// -// ToneMappingEffect.cpp -// libraries/render-utils/src -// -// Created by Sam Gateau on 12/7/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 -// - -#include "ToneMappingEffect.h" - -#include -#include - -#include "render-utils/ShaderConstants.h" -#include "StencilMaskPass.h" -#include "FramebufferCache.h" - - -ToneMappingEffect::ToneMappingEffect() { - Parameters parameters; - _parametersBuffer = gpu::BufferView(std::make_shared(sizeof(Parameters), (const gpu::Byte*) ¶meters)); -} - -void ToneMappingEffect::init(RenderArgs* args) { - auto blitProgram = gpu::Shader::createProgram(shader::render_utils::program::toneMapping); - - auto blitState = std::make_shared(); - blitState->setColorWriteMask(true, true, true, true); - _blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState)); -} - -void ToneMappingEffect::setExposure(float exposure) { - auto& params = _parametersBuffer.get(); - if (params._exposure != exposure) { - _parametersBuffer.edit()._exposure = exposure; - _parametersBuffer.edit()._twoPowExposure = pow(2.0, exposure); - } -} - -void ToneMappingEffect::setToneCurve(ToneCurve curve) { - auto& params = _parametersBuffer.get(); - if (params._toneCurve != curve) { - _parametersBuffer.edit()._toneCurve = curve; - } -} - -void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& lightingBuffer, const gpu::FramebufferPointer& destinationFramebuffer) { - if (!_blitLightBuffer) { - init(args); - } - - if (!lightingBuffer || !destinationFramebuffer) { - return; - } - - auto framebufferSize = glm::ivec2(lightingBuffer->getDimensions()); - gpu::doInBatch("ToneMappingEffect::render", args->_context, [&](gpu::Batch& batch) { - batch.enableStereo(false); - batch.setFramebuffer(destinationFramebuffer); - - // FIXME: Generate the Luminosity map - //batch.generateTextureMips(lightingBuffer); - - batch.setViewportTransform(args->_viewport); - batch.setProjectionTransform(glm::mat4()); - batch.resetViewTransform(); - batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(framebufferSize, args->_viewport)); - batch.setPipeline(_blitLightBuffer); - - batch.setUniformBuffer(render_utils::slot::buffer::ToneMappingParams, _parametersBuffer); - batch.setResourceTexture(render_utils::slot::texture::ToneMappingColor, lightingBuffer); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); -} - - -void ToneMappingDeferred::configure(const Config& config) { - _toneMappingEffect.setExposure(config.exposure); - _toneMappingEffect.setToneCurve((ToneMappingEffect::ToneCurve)config.curve); -} - -void ToneMappingDeferred::run(const render::RenderContextPointer& renderContext, const Input& input, Output& output) { - - auto lightingBuffer = input.get0()->getRenderBuffer(0); - auto destFbo = input.get1(); - - if (!destFbo) { - destFbo = renderContext->args->_blitFramebuffer; - } - - _toneMappingEffect.render(renderContext->args, lightingBuffer, destFbo); - output = destFbo; -} diff --git a/libraries/render-utils/src/render-utils/toneMappingMirroredX.slp b/libraries/render-utils/src/render-utils/toneMappingMirroredX.slp new file mode 100644 index 0000000000..d4d8ec4b01 --- /dev/null +++ b/libraries/render-utils/src/render-utils/toneMappingMirroredX.slp @@ -0,0 +1 @@ +VERTEX gpu::vertex::DrawViewportQuadTransformTexcoord diff --git a/libraries/render-utils/src/toneMappingMirroredX.slf b/libraries/render-utils/src/toneMappingMirroredX.slf new file mode 100644 index 0000000000..7c510cdc1b --- /dev/null +++ b/libraries/render-utils/src/toneMappingMirroredX.slf @@ -0,0 +1,71 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on Sat Oct 24 09:34:37 2015 +// +// toneMapping.frag +// +// Draw texture 0 fetched at texcoord.xy +// +// Created by Sam Gateau on 6/22/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 +// + +<@include render-utils/ShaderConstants.h@> + +struct ToneMappingParams { + vec4 _exp_2powExp_s0_s1; + ivec4 _toneCurve_s0_s1_s2; +}; + +const float GAMMA_22 = 2.2; +const float INV_GAMMA_22 = 1.0 / 2.2; +const int ToneCurveNone = 0; +const int ToneCurveGamma22 = 1; +const int ToneCurveReinhard = 2; +const int ToneCurveFilmic = 3; + +LAYOUT(binding=RENDER_UTILS_BUFFER_TM_PARAMS) uniform toneMappingParamsBuffer { + ToneMappingParams params; +}; +float getTwoPowExposure() { + return params._exp_2powExp_s0_s1.y; +} +int getToneCurve() { + return params._toneCurve_s0_s1_s2.x; +} + +LAYOUT(binding=RENDER_UTILS_TEXTURE_TM_COLOR) uniform sampler2D colorMap; + +layout(location=0) in vec2 varTexCoord0; +layout(location=0) out vec4 outFragColor; + +void main(void) { + vec4 fragColorRaw = texture(colorMap, vec2(1 - varTexCoord0.x, varTexCoord0.y)); + vec3 fragColor = fragColorRaw.xyz; + + vec3 srcColor = fragColor * getTwoPowExposure(); + + int toneCurve = getToneCurve(); + vec3 tonedColor = srcColor; + if (toneCurve == ToneCurveFilmic) { + vec3 x = max(vec3(0.0), srcColor-0.004); + tonedColor = pow((x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06), vec3(GAMMA_22)); + } else if (toneCurve == ToneCurveReinhard) { + tonedColor = srcColor/(1.0 + srcColor); + } else if (toneCurve == ToneCurveGamma22) { + // We use glEnable(GL_FRAMEBUFFER_SRGB), which automatically converts textures from RGB to SRGB + // when writing from an RGB framebuffer to an SRGB framebuffer (note that it doesn't do anything + // when writing from an SRGB framebuffer to an RGB framebuffer). + // Since the conversion happens automatically, we don't need to do anything in this shader + } else { + // toneCurve == ToneCurveNone + // For debugging purposes, we may want to see what the colors look like before the automatic OpenGL + // conversion mentioned above, so we undo it here + tonedColor = pow(srcColor, vec3(GAMMA_22)); + } + + outFragColor = vec4(tonedColor, 1.0); +}