diff --git a/include/openspace/rendering/abufferrenderer.h b/include/openspace/rendering/abufferrenderer.h index ae799eb4b3..b669ccade5 100644 --- a/include/openspace/rendering/abufferrenderer.h +++ b/include/openspace/rendering/abufferrenderer.h @@ -65,6 +65,12 @@ public: void setHDRExposure(float hdrExposure) override; void setHDRBackground(float hdrBackground) override; void setGamma(float gamma) override; + void setMaxWhite(float maxWhite) override; + void setToneMapOperator(int tmOp) override; + void setBloomThreMin(float minV) override; + void setBloomThreMax(float maxV) override; + void setBloomOrigFactor(float origFactor) override; + void setBloomNewFactor(float newFactor) override; float hdrBackground() const override; int nAaSamples() const override; @@ -135,7 +141,13 @@ private: float _hdrExposure = 0.4f; float _hdrBackground = 2.8f; float _gamma = 2.2f; + float _maxWhite = 1.f; float _blackoutFactor; + float _bloomThresholdMin = 0.0; + float _bloomThresholdMax = 1.0; + float _bloomOrigFactor = 1.0; + float _bloomNewFactor = 1.0; + int _toneMapOperator = 0; std::vector _mSAAPattern; diff --git a/include/openspace/rendering/framebufferrenderer.h b/include/openspace/rendering/framebufferrenderer.h index 2334b2d47b..72fb72a57b 100644 --- a/include/openspace/rendering/framebufferrenderer.h +++ b/include/openspace/rendering/framebufferrenderer.h @@ -76,7 +76,9 @@ public: void updateResolution(); void updateRaycastData(); void updateDeferredcastData(); - void updateHDRData(); + void updateHDRAndFiltering(); + void updateAveLum(); + void updateBloomConfig(); void updateMSAASamplingPattern(); void setResolution(glm::ivec2 res) override; @@ -84,6 +86,12 @@ public: void setHDRExposure(float hdrExposure) override; void setHDRBackground(float hdrBackground) override; void setGamma(float gamma) override; + void setMaxWhite(float maxWhite) override; + void setToneMapOperator(int tmOp) override; + void setBloomThreMin(float minV) override; + void setBloomThreMax(float maxV) override; + void setBloomOrigFactor(float origFactor) override; + void setBloomNewFactor(float newFactor) override; float hdrBackground() const override; int nAaSamples() const override; @@ -105,6 +113,11 @@ public: virtual void deferredcastersChanged(Deferredcaster& deferredcaster, DeferredcasterListener::IsAttached isAttached) override; +private: + float computeBufferAveLuminance(); + float computeBufferAveLuminanceGPU(); + void applyBloomFilter(); + private: std::map _raycastData; RaycasterProgObjMap _exitPrograms; @@ -113,14 +126,26 @@ private: std::map _deferredcastData; DeferredcasterProgObjMap _deferredcastPrograms; - std::unique_ptr _hdrBackGroundProgram; + + std::unique_ptr _hdrFilteringProgram; + std::unique_ptr _aveLumProgram; + std::unique_ptr _bloomProgram; + std::unique_ptr _bloomResolveProgram; std::unique_ptr _resolveProgram; UniformCache(mainColorTexture, blackoutFactor, nAaSamples) _uniformCache; + + UniformCache(deferredResultsTexture, blackoutFactor, backgroundConstant, + backgroundExposure, gamma, toneMapOperator, aveLum, maxWhite) + _hdrUniformCache; + + UniformCache(renderedImage, bloomImage, bloomThresholdMin, bloomThresholdMax, + bloomOrigFactor, bloomNewFactor) _bloomUniformCache; GLuint _screenQuad; GLuint _vertexPositionBuffer; GLuint _mainColorTexture; + GLuint _mainFilterTexture; GLuint _mainPositionTexture; GLuint _mainNormalTexture; GLuint _mainDepthTexture; @@ -128,8 +153,14 @@ private: GLuint _mainFramebuffer; GLuint _exitDepthTexture; GLuint _exitFramebuffer; - GLuint _deferredFramebuffer; - GLuint _deferredColorTexture; + GLuint _hdrFilteringFramebuffer; + GLuint _hdrFilteringTexture; + + GLuint _bloomFilterFBO[3]; + GLuint _bloomTexture[3]; + + GLuint _computeAveLumFBO; + GLuint _computeAveLumTexture; bool _dirtyDeferredcastData; bool _dirtyRaycastData; @@ -141,6 +172,12 @@ private: float _hdrExposure = 0.4f; float _hdrBackground = 2.8f; float _gamma = 2.2f; + float _maxWhite = 1.0f; + float _bloomThresholdMin = 0.0; + float _bloomThresholdMax = 1.0; + float _bloomOrigFactor = 1.0; + float _bloomNewFactor = 1.0; + int _toneMapOperator = 0; std::vector _mSAAPattern; diff --git a/include/openspace/rendering/renderengine.h b/include/openspace/rendering/renderengine.h index 6254e51615..6078c45500 100644 --- a/include/openspace/rendering/renderengine.h +++ b/include/openspace/rendering/renderengine.h @@ -64,6 +64,20 @@ public: Invalid }; + enum class ToneMapOperators { + EXPONENTIAL = 0, + LINEAR, + SIMPLE_REINHARD, + LUM_BASED_REINHARD, + WHITE_PRESERVING, + ROM_BIN_DA_HOUSE, + FILMIC, + UNCHARTED, + COSTA, + ADAPTIVE, + GLOBAL + }; + RenderEngine(); ~RenderEngine(); @@ -193,6 +207,12 @@ private: properties::FloatProperty _hdrExposure; properties::FloatProperty _hdrBackground; properties::FloatProperty _gamma; + properties::FloatProperty _maxWhite; + properties::FloatProperty _bloomThreshouldMin; + properties::FloatProperty _bloomThreshouldMax; + properties::FloatProperty _bloomOrigColorFactor; + properties::FloatProperty _bloomNewColorFactor; + properties::OptionProperty _toneMapOperator; uint64_t _frameNumber = 0; diff --git a/include/openspace/rendering/renderer.h b/include/openspace/rendering/renderer.h index 8ae5130954..0eaac41734 100644 --- a/include/openspace/rendering/renderer.h +++ b/include/openspace/rendering/renderer.h @@ -53,6 +53,12 @@ public: virtual void setHDRExposure(float hdrExposure) = 0; virtual void setHDRBackground(float hdrBackground) = 0; virtual void setGamma(float gamma) = 0; + virtual void setMaxWhite(float maxWhite) = 0; + virtual void setToneMapOperator(int tmOp) = 0; + virtual void setBloomThreMin(float minV) = 0; + virtual void setBloomThreMax(float maxV) = 0; + virtual void setBloomOrigFactor(float origFactor) = 0; + virtual void setBloomNewFactor(float newFactor) = 0; virtual float hdrBackground() const = 0; virtual int nAaSamples() const = 0; diff --git a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl index f3de0b64b2..cf3d8bc928 100644 --- a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl +++ b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl @@ -633,7 +633,8 @@ void main() { if (position.xyz != vec3(0.0) && (pixelDepth < offset)) { // ATM Occluded - Something in fron of ATM. - atmosphereFinalColor += vec4(HDR(color.xyz * backgroundConstant, atmExposure), color.a); + //atmosphereFinalColor += vec4(HDR(color.xyz * backgroundConstant, atmExposure), color.a); + atmosphereFinalColor += vec4(color.xyz * backgroundConstant, color.a); } else { // Following paper nomenclature double t = offset; @@ -682,13 +683,15 @@ void main() { } // Final Color of ATM plus terrain: - vec4 finalRadiance = vec4(HDR(inscatterColor + groundColorV + sunColorV, atmExposure), 1.0); - + //vec4 finalRadiance = vec4(HDR(inscatterColor + groundColorV + sunColorV, atmExposure), 1.0); + vec4 finalRadiance = vec4(inscatterColor + groundColorV + sunColorV, 1.0); + atmosphereFinalColor += finalRadiance; } } else { // no intersection - atmosphereFinalColor += vec4(HDR(color.xyz * backgroundConstant, atmExposure), color.a); + //atmosphereFinalColor += vec4(HDR(color.xyz * backgroundConstant, atmExposure), color.a); + atmosphereFinalColor += vec4(color.xyz * backgroundConstant, color.a); } } @@ -704,7 +707,8 @@ void main() { bColor += texelFetch(mainColorTexture, fragCoords, f); } bColor /= float(nAaSamples); - renderTarget = vec4(HDR(bColor.xyz * backgroundConstant, atmExposure), bColor.a); + //renderTarget = vec4(HDR(bColor.xyz * backgroundConstant, atmExposure), bColor.a); + renderTarget = vec4(bColor.xyz * backgroundConstant, bColor.a); } else { discard; diff --git a/modules/base/shaders/renderabletrail_fs.glsl b/modules/base/shaders/renderabletrail_fs.glsl index df8778fe70..cb55366fe4 100644 --- a/modules/base/shaders/renderabletrail_fs.glsl +++ b/modules/base/shaders/renderabletrail_fs.glsl @@ -71,5 +71,8 @@ Fragment getFragment() { // There is no normal here frag.gNormal = vec4(0.0, 0.0, -1.0, 1.0); + // Bloom filter + frag.filterFlag = 1; + return frag; } diff --git a/shaders/fragment.glsl b/shaders/fragment.glsl index 97755f6734..3cdb665ce8 100644 --- a/shaders/fragment.glsl +++ b/shaders/fragment.glsl @@ -32,6 +32,7 @@ struct Fragment { vec4 color; vec4 gPosition; vec4 gNormal; + uint filterFlag; float depth; uint blend; bool forceFboRendering; diff --git a/shaders/framebuffer/bloomFilter.frag b/shaders/framebuffer/bloomFilter.frag new file mode 100644 index 0000000000..56b4b795fd --- /dev/null +++ b/shaders/framebuffer/bloomFilter.frag @@ -0,0 +1,75 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout (location = 0) out vec4 finalColor; + +uniform int pass; +uniform sampler2DMS filterImage; +uniform sampler2D filterFirstPass; + +// Gaussian Weights from OpenGL SuperBible 7 ed. +const float weights[] = float[](0.0024499299678342, + 0.0043538453346397, + 0.0073599963704157, + 0.0118349786570722, + 0.0181026699707781, + 0.0263392293891488, + 0.0364543006660986, + 0.0479932050577658, + 0.0601029809166942, + 0.0715974486241365, + 0.0811305381519717, + 0.0874493212267511, + 0.0896631113333857, + 0.0874493212267511, + 0.0811305381519717, + 0.0715974486241365, + 0.0601029809166942, + 0.0479932050577658, + 0.0364543006660986, + 0.0263392293891488, + 0.0181026699707781, + 0.0118349786570722, + 0.0073599963704157, + 0.0043538453346397, + 0.0024499299678342); + +void main(void) +{ + vec4 color = vec4(0.0); + // Transpose the image so the filter can be applied on X and Y + ivec2 P = ivec2(gl_FragCoord.yx) - ivec2(0, weights.length() >> 1); + + for (int i = 0; i < weights.length(); i++) + { + if (pass == 1) + color += vec4(texelFetch(filterImage, P + ivec2(0, i), 0).rgb, 1.0) * weights[i]; + else if (pass == 2) + color += vec4(texelFetch(filterFirstPass, P + ivec2(0, i), 0).rgb, 1.0) * weights[i]; + } + + finalColor = vec4(color.rgb, 1.0); +} diff --git a/shaders/framebuffer/bloomFilter.vert b/shaders/framebuffer/bloomFilter.vert new file mode 100644 index 0000000000..818a0d62af --- /dev/null +++ b/shaders/framebuffer/bloomFilter.vert @@ -0,0 +1,35 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +void main(void) +{ + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.5, 1.0), + vec4( 1.0, -1.0, 0.5, 1.0), + vec4(-1.0, 1.0, 0.5, 1.0), + vec4( 1.0, 1.0, 0.5, 1.0)); + + gl_Position = vertices[gl_VertexID]; +} \ No newline at end of file diff --git a/shaders/framebuffer/bloomResolveFilter.frag b/shaders/framebuffer/bloomResolveFilter.frag new file mode 100644 index 0000000000..fdbc368507 --- /dev/null +++ b/shaders/framebuffer/bloomResolveFilter.frag @@ -0,0 +1,43 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout (location = 0) out vec4 finalColor; + +uniform float bloomOrigFactor; +uniform float bloomNewFactor; +uniform sampler2D renderedImage; +uniform sampler2D bloomImage; + +void main(void) +{ + vec4 color = vec4(0.0); + + color += texelFetch(renderedImage, ivec2(gl_FragCoord.xy), 0) * bloomOrigFactor; + float alpha = color.a; + color += texelFetch(bloomImage, ivec2(gl_FragCoord.xy), 0) * bloomNewFactor; + + finalColor = vec4(color.rgb, alpha); +} diff --git a/shaders/framebuffer/bloomResolveFilter.vert b/shaders/framebuffer/bloomResolveFilter.vert new file mode 100644 index 0000000000..818a0d62af --- /dev/null +++ b/shaders/framebuffer/bloomResolveFilter.vert @@ -0,0 +1,35 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +void main(void) +{ + const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.5, 1.0), + vec4( 1.0, -1.0, 0.5, 1.0), + vec4(-1.0, 1.0, 0.5, 1.0), + vec4( 1.0, 1.0, 0.5, 1.0)); + + gl_Position = vertices[gl_VertexID]; +} \ No newline at end of file diff --git a/shaders/framebuffer/computeAveLum.frag b/shaders/framebuffer/computeAveLum.frag new file mode 100644 index 0000000000..faa3bde1f8 --- /dev/null +++ b/shaders/framebuffer/computeAveLum.frag @@ -0,0 +1,51 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout (location = 0) out vec4 finalColor; + +uniform int bufferWidth; +uniform int bufferHeight; +uniform sampler2D hdrTexture; + +in vec2 texCoord; + +void main() { + vec4 color = vec4(0.0); + float fH = float(bufferHeight); + float fW = float(bufferWidth); + + float sum = 0.f; + for (int i = 0; i < bufferHeight; ++i) { + for (int j = 0; j < bufferWidth; ++j) { + vec2 texCoord = vec2(float(i) / fH, float(j) / fW); + vec4 tmpColor = texture(hdrTexture, texCoord); + float lum = dot(tmpColor.xyz, vec3(0.2126f, 0.7152f, 0.0722f)); + sum += log(lum + 0.00001); + } + } + + finalColor = vec4(vec3(exp(sum / (fH * fW))), 1.0); +} \ No newline at end of file diff --git a/shaders/framebuffer/computeAveLum.vert b/shaders/framebuffer/computeAveLum.vert new file mode 100644 index 0000000000..4927e34304 --- /dev/null +++ b/shaders/framebuffer/computeAveLum.vert @@ -0,0 +1,31 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout(location = 0) in vec4 position; + +void main() { + gl_Position = position; +} diff --git a/shaders/framebuffer/hdrAndFiltering.frag b/shaders/framebuffer/hdrAndFiltering.frag new file mode 100644 index 0000000000..26ab41b9bc --- /dev/null +++ b/shaders/framebuffer/hdrAndFiltering.frag @@ -0,0 +1,120 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +#include "hdr.glsl" + +layout (location = 0) out vec4 finalColor; + +uniform float backgroundConstant; +uniform float backgroundExposure; +uniform float blackoutFactor; +uniform float gamma; +uniform float maxWhite; +uniform float aveLum; +uniform int toneMapOperator; + +uniform sampler2D deferredResultsTexture; + +in vec2 texCoord; + +vec4 adaptiveToneMap() { + int i; + float lum[25]; + vec2 tex_scale = vec2(1.0) / textureSize(deferredResultsTexture, 0); + + for (i = 0; i < 25; i++) + { + vec2 tc = (gl_FragCoord.xy + 3.5 * vec2(i % 5 - 2, i / 5 - 2)); + vec3 col = texture(deferredResultsTexture, tc * tex_scale).rgb; + lum[i] = dot(col, vec3(0.3, 0.59, 0.11)); + } + + // Calculate weighted color of region + vec3 vColor = texelFetch(deferredResultsTexture, ivec2(gl_FragCoord.xy), 0).rgb; + + float kernelLuminance = ( + (1.0 * (lum[0] + lum[4] + lum[20] + lum[24])) + + (4.0 * (lum[1] + lum[3] + lum[5] + lum[9] + + lum[15] + lum[19] + lum[21] + lum[23])) + + (7.0 * (lum[2] + lum[10] + lum[14] + lum[22])) + + (16.0 * (lum[6] + lum[8] + lum[16] + lum[18])) + + (26.0 * (lum[7] + lum[11] + lum[13] + lum[17])) + + (41.0 * lum[12]) + ) / 273.0; + + // Compute the corresponding exposure + float exposure = sqrt(8.0 / (kernelLuminance + 0.25)); + + // Apply the exposure to this texel + vec4 fColor; + fColor.rgb = 1.0 - exp2(-vColor * exposure); + fColor.a = 1.0f; + + return fColor; +} + + +void main() { + vec4 color = vec4(0.0); + color = texture(deferredResultsTexture, texCoord); + //color = texelFetch(deferredResultsTexture, ivec2(gl_FragCoord.xy), 0); + color.a *= blackoutFactor; + + if (toneMapOperator == EXPONENTIAL) { + vec3 tColor = exponentialToneMapping(color.rgb, backgroundExposure, gamma); + finalColor = vec4(tColor, color.a); + } else if (toneMapOperator == LINEAR) { + vec3 tColor = linearToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == SIMPLE_REINHARD) { + vec3 tColor = simpleReinhardToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == LUM_BASED_REINHARD) { + vec3 tColor = lumaBasedReinhardToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == WHITE_PRESERVING) { + vec3 tColor = whitePreservingLumaBasedReinhardToneMapping(color.rgb, backgroundExposure, maxWhite); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == ROM_BIN_DA_HOUSE) { + vec3 tColor = RomBinDaHouseToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == FILMIC) { + vec3 tColor = filmicToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == UNCHARTED) { + vec3 tColor = Uncharted2ToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == COSTA) { + vec3 tColor = jToneMapping(color.rgb, backgroundExposure); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == ADAPTIVE) { + vec3 tColor = vec3(adaptiveToneMap()); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } else if (toneMapOperator == GLOBAL) { + vec3 tColor = globalToneMappingOperatorRTR(color.rgb, backgroundExposure, maxWhite, aveLum); + finalColor = vec4(gammaCorrection(tColor, gamma), color.a); + } +} \ No newline at end of file diff --git a/shaders/framebuffer/hdrAndFiltering.vert b/shaders/framebuffer/hdrAndFiltering.vert new file mode 100644 index 0000000000..06506ba824 --- /dev/null +++ b/shaders/framebuffer/hdrAndFiltering.vert @@ -0,0 +1,33 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2018 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#version __CONTEXT__ + +layout(location = 0) in vec4 position; +out vec2 texCoord; + +void main() { + texCoord = 0.5 + position.xy * 0.5; + gl_Position = position; +} diff --git a/shaders/framebuffer/renderframebuffer.frag b/shaders/framebuffer/renderframebuffer.frag index 4ec538c538..8cf3617358 100644 --- a/shaders/framebuffer/renderframebuffer.frag +++ b/shaders/framebuffer/renderframebuffer.frag @@ -28,11 +28,29 @@ layout(location = 0) out vec4 _out_color_; layout(location = 1) out vec4 gPosition; layout(location = 2) out vec4 gNormal; +layout(location = 3) out vec4 filterBuffer; void main() { Fragment f = getFragment(); _out_color_ = f.color; gPosition = f.gPosition; gNormal = f.gNormal; + + bool automaticBloom = false; + if (automaticBloom) { + // Extract luminance + float Y = dot(f.color.rgb, vec3(0.299, 0.587, 0.144)); + + // Apply Bloom on the bloom threshold range values + //vec4 bColor = f.color * 4.0 * smoothstep(bloom_thresh_min, bloom_thresh_max, Y); + //filterBuffer = bColor + filterBuffer = vec4(f.filterFlag); + } else { + if (f.filterFlag == 1) + filterBuffer = f.color; + else + filterBuffer = vec4(0); + } + gl_FragDepth = normalizeFloat(f.depth); } diff --git a/shaders/hdr.glsl b/shaders/hdr.glsl index cb282bed8f..e3c1384d73 100644 --- a/shaders/hdr.glsl +++ b/shaders/hdr.glsl @@ -21,10 +21,51 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * ****************************************************************************************/ - -uniform float gamma; -vec3 exponentialToneMapping(vec3 color, float exposure) { +#define EXPONENTIAL 0 +#define LINEAR 1 +#define SIMPLE_REINHARD 2 +#define LUM_BASED_REINHARD 3 +#define WHITE_PRESERVING 4 +#define ROM_BIN_DA_HOUSE 5 +#define FILMIC 6 +#define UNCHARTED 7 +#define COSTA 8 +#define ADAPTIVE 9 +#define GLOBAL 10 + +const mat3 rgb2xyz = mat3( + 0.4124564, 0.2126729, 0.0193339, + 0.3575761, 0.7151522, 0.1191920, + 0.1804375, 0.0721750, 0.9503041 ); + +const mat3 xyz2rgb = mat3( + 3.2404542, -0.9692660, 0.0556434, + -1.5371385, 1.8760108, -0.2040259, + -0.4985314, 0.0415560, 1.0572252 ); + +vec3 globalToneMappingOperatorRTR(vec3 color, float exposure, float maxWhite, float aveLum) { + // Convert color to XYZ + vec3 xyzCol = rgb2xyz * color; + + // Convert from XYZ to xyY + float xyzSum = xyzCol.x + xyzCol.y + xyzCol.z; + vec3 xyYCol = vec3( xyzCol.x / xyzSum, xyzCol.y / xyzSum, xyzCol.y); + + // Apply the tone mapping operation to the luminance (xyYCol.z or xyzCol.y) + float L = (exposure * xyYCol.z) / aveLum; + L = (L * ( 1 + L / (maxWhite * maxWhite) )) / ( 1 + L ); + + // Using the new luminance, convert back to XYZ + xyzCol.x = (L * xyYCol.x) / (xyYCol.y); + xyzCol.y = L; + xyzCol.z = (L * (1 - xyYCol.x - xyYCol.y))/xyYCol.y; + + // Convert back to RGB and send to output buffer + return xyz2rgb * xyzCol; +} + +vec3 exponentialToneMapping(vec3 color, float exposure, float gamma) { color *= exposure; color.r = color.r < 1.413 ? pow(color.r * 0.38317, 1.0 / gamma) : 1.0 - exp(-color.r); @@ -37,14 +78,12 @@ vec3 exponentialToneMapping(vec3 color, float exposure) { vec3 linearToneMapping(vec3 color, float exposure) { float tExposure = 0.08f; color = clamp(tExposure * color, 0.f, 1.f); - color = pow(color, vec3(1.f / gamma)); return color; } vec3 simpleReinhardToneMapping(vec3 color, float exposure) { - float tExposure = 1.5f; + float tExposure = exposure; color *= tExposure/(1.f + color / tExposure); - color = pow(color, vec3(1.f / gamma)); return color; } @@ -52,23 +91,19 @@ vec3 lumaBasedReinhardToneMapping(vec3 color, float exposure) { float luma = dot(color, vec3(0.2126f, 0.7152f, 0.0722f)); float toneMappedLuma = luma / (1.f + luma); color *= toneMappedLuma / luma; - color = pow(color, vec3(1.f / gamma)); return color; } -vec3 whitePreservingLumaBasedReinhardToneMapping(vec3 color, float exposure) { - float white = 4.f; +vec3 whitePreservingLumaBasedReinhardToneMapping(vec3 color, float exposure, float maxWhite) { //float luma = dot(color, vec3(0.2126f, 0.7152f, 0.0722f)); float luma = dot(color, vec3(0.4126f, 0.9152f, 0.2722f)); - float toneMappedLuma = luma * (1.f + luma / (white * white)) / (1.f + luma); + float toneMappedLuma = luma * (1.f + luma / (maxWhite * maxWhite)) / (1.f + luma); color *= toneMappedLuma / luma; - color = pow(color, vec3(1.f / gamma)); return color; } vec3 RomBinDaHouseToneMapping(vec3 color, float exposure) { color = exp( -1.f / ( 2.72f * color + 0.15f ) ); - color = pow(color, vec3(1.7f / gamma)); return color; } @@ -92,7 +127,6 @@ vec3 Uncharted2ToneMapping(vec3 color, float exposure) { color = ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; float white = ((W * (A * W + C * B) + D * E) / (W * (A * W + B) + D * F)) - E / F; color /= white; - color = pow(color, vec3(1.0f / gamma)); return color; } @@ -100,6 +134,10 @@ vec3 jToneMapping(vec3 color, float exposure) { return 1.0 - exp(-exposure * color); } +vec3 gammaCorrection(vec3 color, float gamma) { + return pow(color, vec3(1.0f / gamma)); +} + vec3 HDR(vec3 color, float exposure) { //return exponentialToneMapping(color, exposure); //return linearToneMapping(color, exposure); @@ -107,7 +145,7 @@ vec3 HDR(vec3 color, float exposure) { //return lumaBasedReinhardToneMapping(color, exposure); //return whitePreservingLumaBasedReinhardToneMapping(color, exposure); //return RomBinDaHouseToneMapping(color, exposure); - //return filmicToneMapping(color, exposure); + return filmicToneMapping(color, exposure); //return Uncharted2ToneMapping(color, exposure); - return jToneMapping(color, exposure); + //return jToneMapping(color, exposure); } diff --git a/src/rendering/abufferrenderer.cpp b/src/rendering/abufferrenderer.cpp index a3c56fb98d..c511015d86 100644 --- a/src/rendering/abufferrenderer.cpp +++ b/src/rendering/abufferrenderer.cpp @@ -727,6 +727,34 @@ void ABufferRenderer::setGamma(float gamma) { } } +void ABufferRenderer::setMaxWhite(float maxWhite) { + _maxWhite = maxWhite; +} + +void ABufferRenderer::setToneMapOperator(int tmOp) { + _toneMapOperator = tmpOp; +}; + +void ABufferRenderer::setToneMapOperator(int tmOp) { + _toneMapOperator = tmOp; +} + +void ABufferRenderer::setBloomThreMin(float minV) { + _bloomThresholdMin = minV; +} + +void ABufferRenderer::setBloomThreMax(float maxV) { + _bloomThresholdMax = maxV; +} + +void ABufferRenderer::setBloomOrigFactor(float origFactor) { + _bloomOrigFactor = origFactor; +} + +void ABufferRenderer::setBloomNewFactor(float newFactor) { + _bloomNewFactor = newFactor; +} + float ABufferRenderer::hdrBackground() const { return _hdrBackground; } diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index 7b5502cc3b..15063a28c9 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -55,6 +55,16 @@ namespace { "mainColorTexture", "blackoutFactor", "nAaSamples" }; + constexpr const std::array HDRUniformNames = { + "deferredResultsTexture", "blackoutFactor", "backgroundConstant", + "backgroundExposure", "gamma", "toneMapOperator", "aveLum", "maxWhite" + }; + + constexpr const std::array BLoomUniformNames = { + "renderedImage", "bloomImage", "bloomThresholdMin", "bloomThresholdMax", + "bloomOrigFactor", "bloomNewFactor" + }; + constexpr const char* ExitFragmentShaderPath = "${SHADERS}/framebuffer/exitframebuffer.frag"; constexpr const char* RaycastFragmentShaderPath = @@ -127,6 +137,7 @@ void FramebufferRenderer::initialize() { // Main framebuffer glGenTextures(1, &_mainColorTexture); glGenTextures(1, &_mainDepthTexture); + glGenTextures(1, &_mainFilterTexture); glGenFramebuffers(1, &_mainFramebuffer); // Exit framebuffer @@ -134,16 +145,27 @@ void FramebufferRenderer::initialize() { glGenTextures(1, &_exitDepthTexture); glGenFramebuffers(1, &_exitFramebuffer); - // Deferred framebuffer - glGenTextures(1, &_deferredColorTexture); + // Deferred textures glGenTextures(1, &_mainPositionTexture); glGenTextures(1, &_mainNormalTexture); - glGenFramebuffers(1, &_deferredFramebuffer); + // HDR / Filtering Framebuffer + glGenFramebuffers(1, &_hdrFilteringFramebuffer); + glGenTextures(1, &_hdrFilteringTexture); + + // Compute Average Luminosity + glGenTextures(1, &_computeAveLumTexture); + glGenFramebuffers(1, &_computeAveLumFBO); + + // Bloom Filter + glGenFramebuffers(3, _bloomFilterFBO); + glGenTextures(3, _bloomTexture); + updateResolution(); updateRendererData(); - updateRaycastData(); + updateRaycastData(); + // Builds Main Framebuffer glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, @@ -152,7 +174,6 @@ void FramebufferRenderer::initialize() { _mainColorTexture, 0 ); - // G-buffer glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, @@ -167,6 +188,13 @@ void FramebufferRenderer::initialize() { _mainNormalTexture, 0 ); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT3, + GL_TEXTURE_2D_MULTISAMPLE, + _mainFilterTexture, + 0 + ); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, @@ -180,6 +208,7 @@ void FramebufferRenderer::initialize() { LERROR("Main framebuffer is not complete"); } + // Builds Exit Framebuffer glBindFramebuffer(GL_FRAMEBUFFER, _exitFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, @@ -201,23 +230,59 @@ void FramebufferRenderer::initialize() { LERROR("Exit framebuffer is not complete"); } - glBindFramebuffer(GL_FRAMEBUFFER, _deferredFramebuffer); + // Builds HDR/Filtering Framebuffer + glBindFramebuffer(GL_FRAMEBUFFER, _hdrFilteringFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - _deferredColorTexture, + _hdrFilteringTexture, 0 ); status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { - LERROR("Deferred framebuffer is not complete"); + LERROR("HDR/Filtering framebuffer is not complete"); + } + + // Buids Average Lum FBO + glBindFramebuffer(GL_FRAMEBUFFER, _computeAveLumFBO); + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + _computeAveLumTexture, + 0 + ); + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Average Luminosity framebuffer is not complete"); + } + + // Builds Bloom Filter FBOs + //const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; + const GLenum buffers[] = { GL_COLOR_ATTACHMENT0 }; + for (int i = 0; i < 3; i++) + { + glBindFramebuffer(GL_FRAMEBUFFER, _bloomFilterFBO[i]); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _bloomTexture[i], 0); + glDrawBuffers(1, buffers); + } + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Bloom framebuffer is not complete"); } // JCC: Moved to here to avoid NVidia: "Program/shader state performance warning" - updateHDRData(); + // Builds HDR and Filtering programs + updateHDRAndFiltering(); + updateAveLum(); + updateBloomConfig(); + // Builds deferred casters programs updateDeferredcastData(); + _dirtyMsaaSamplingPattern = true; glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); @@ -228,8 +293,21 @@ void FramebufferRenderer::initialize() { absPath("${SHADERS}/framebuffer/resolveframebuffer.frag") ); - ghoul::opengl::updateUniformLocations(*_resolveProgram, _uniformCache, UniformNames); - + ghoul::opengl::updateUniformLocations( + *_resolveProgram, + _uniformCache, + UniformNames + ); + ghoul::opengl::updateUniformLocations( + *_hdrFilteringProgram, + _hdrUniformCache, + HDRUniformNames + ); + ghoul::opengl::updateUniformLocations( + *_bloomResolveProgram, + _bloomUniformCache, + BLoomUniformNames + ); global::raycasterManager.addListener(*this); global::deferredcasterManager.addListener(*this); } @@ -239,15 +317,18 @@ void FramebufferRenderer::deinitialize() { glDeleteFramebuffers(1, &_mainFramebuffer); glDeleteFramebuffers(1, &_exitFramebuffer); - glDeleteFramebuffers(1, &_deferredFramebuffer); + glDeleteFramebuffers(1, &_hdrFilteringFramebuffer); + glDeleteFramebuffers(1, &_computeAveLumFBO); + glDeleteFramebuffers(3, _bloomFilterFBO); glDeleteTextures(1, &_mainColorTexture); glDeleteTextures(1, &_mainDepthTexture); - // DEBUG: deferred g-buffer - glDeleteTextures(1, &_deferredColorTexture); + glDeleteTextures(1, &_hdrFilteringTexture); glDeleteTextures(1, &_mainPositionTexture); glDeleteTextures(1, &_mainNormalTexture); + glDeleteTextures(1, &_computeAveLumTexture); + glDeleteTextures(3, _bloomTexture); glDeleteTextures(1, &_exitColorTexture); glDeleteTextures(1, &_exitDepthTexture); @@ -271,6 +352,155 @@ void FramebufferRenderer::deferredcastersChanged(Deferredcaster&, _dirtyDeferredcastData = true; } +float FramebufferRenderer::computeBufferAveLuminance() { + unsigned int texDimension = _resolution.x * _resolution.y; + + std::unique_ptr texData(new GLfloat[texDimension * 3]); + + ghoul::opengl::TextureUnit hdrTextureUnit; + hdrTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _hdrFilteringTexture); + + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_FLOAT, texData.get()); + + float sum = 0.0f; + + for (unsigned int i = 0; i < texDimension; i++) { + float lum = glm::dot( + glm::vec3(texData[i * 3 + 0], texData[i * 3 + 1], texData[i * 3 + 2]), + glm::vec3(0.2126f, 0.7152f, 0.0722f) + ); + sum += logf(lum + 0.00001f); + } + + return expf(sum / texDimension); +} + +float FramebufferRenderer::computeBufferAveLuminanceGPU() { + // Capture standard fbo + GLint defaultFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); + + glBindFramebuffer(GL_FRAMEBUFFER, _computeAveLumFBO); + GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 }; + glDrawBuffers(1, textureBuffers); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, 1, 1); + + _aveLumProgram->activate(); + + //float averageLuminaceInFB = computeBufferAveLuminance(); + //std::cout << "=== Average Lum on CPU = " << averageLuminaceInFB << " ===" << std::endl; + + ghoul::opengl::TextureUnit hdrTextureUnit; + hdrTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _hdrFilteringTexture); + _aveLumProgram->setUniform("hdrTexture", hdrTextureUnit); + _aveLumProgram->setUniform("bufferWidth", _resolution.x); + _aveLumProgram->setUniform("bufferHeight", _resolution.y); + + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + _aveLumProgram->deactivate(); + + std::vector gpuAveLum; + saveTextureToMemory(GL_COLOR_ATTACHMENT0, 1, 1, gpuAveLum); + + //std::cout << "=== Average Lum on GPU = " << gpuAveLum[0] << " ===" << std::endl; + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + glViewport(0, 0, _resolution.x, _resolution.y); + + return static_cast(gpuAveLum[0]); +} + +void FramebufferRenderer::applyBloomFilter() { + GLint defaultFbo; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); + + GLuint vao; + glGenVertexArrays(1, &vao); + + glBindFramebuffer(GL_FRAMEBUFFER, _bloomFilterFBO[0]); + glClear(GL_COLOR_BUFFER_BIT); + glViewport(0, 0, _resolution.y, _resolution.x); + glBindVertexArray(vao); + + _bloomProgram->activate(); + + { + ghoul::opengl::TextureUnit filterTextureUnit; + filterTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainFilterTexture); + _bloomProgram->setUniform("pass", 1); + _bloomProgram->setUniform("filterImage", filterTextureUnit); + ghoul::opengl::TextureUnit dummyTextureUnit; + dummyTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _bloomTexture[0]); + _bloomProgram->setUniform("filterFirstPass", dummyTextureUnit); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + glBindFramebuffer(GL_FRAMEBUFFER, _bloomFilterFBO[1]); + glViewport(0, 0, _resolution.x, _resolution.y); + glClear(GL_COLOR_BUFFER_BIT); + glBindVertexArray(vao); + + { + ghoul::opengl::TextureUnit filterTextureUnit; + filterTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _bloomTexture[0]); + _bloomProgram->setUniform("pass", 2); + _bloomProgram->setUniform("filterFirstPass", filterTextureUnit); + ghoul::opengl::TextureUnit dummyTextureUnit; + _bloomProgram->setUniform("filterImage", dummyTextureUnit); + + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + _bloomProgram->deactivate(); + + glBindFramebuffer(GL_FRAMEBUFFER, _bloomFilterFBO[2]); + glViewport(0, 0, _resolution.x, _resolution.y); + glClear(GL_COLOR_BUFFER_BIT); + + _bloomResolveProgram->activate(); + + { + ghoul::opengl::TextureUnit deferredResultsTextureUnit; + deferredResultsTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _hdrFilteringTexture); + _bloomResolveProgram->setUniform( + _bloomUniformCache.renderedImage, + deferredResultsTextureUnit + ); + + ghoul::opengl::TextureUnit bloomTextureUnit; + bloomTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _bloomTexture[1]); + _bloomResolveProgram->setUniform(_bloomUniformCache.bloomImage, bloomTextureUnit); + + _bloomResolveProgram->setUniform( + _bloomUniformCache.bloomOrigFactor, + _bloomOrigFactor + ); + _bloomResolveProgram->setUniform( + _bloomUniformCache.bloomNewFactor, + _bloomNewFactor + ); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } + + _bloomResolveProgram->deactivate(); + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); +} + void FramebufferRenderer::update() { if (_dirtyMsaaSamplingPattern) { updateMSAASamplingPattern(); @@ -289,12 +519,6 @@ void FramebufferRenderer::update() { updateDeferredcastData(); } - // If the resolve dictionary changed (or a file changed on disk) - // then rebuild the resolve program. - if (_hdrBackGroundProgram && _hdrBackGroundProgram->isDirty()) { - _hdrBackGroundProgram->rebuildFromFile(); - } - if (_resolveProgram->isDirty()) { _resolveProgram->rebuildFromFile(); @@ -303,6 +527,34 @@ void FramebufferRenderer::update() { _uniformCache, UniformNames ); + + } + + if (_aveLumProgram->isDirty()) { + _aveLumProgram->rebuildFromFile(); + } + + if (_bloomProgram->isDirty()) { + _bloomProgram->rebuildFromFile(); + } + + if (_bloomResolveProgram->isDirty()) { + _bloomResolveProgram->rebuildFromFile(); + ghoul::opengl::updateUniformLocations( + *_bloomResolveProgram, + _bloomUniformCache, + BLoomUniformNames + ); + } + + if (_hdrFilteringProgram->isDirty()) { + _hdrFilteringProgram->rebuildFromFile(); + + ghoul::opengl::updateUniformLocations( + *_hdrFilteringProgram, + _hdrUniformCache, + HDRUniformNames + ); } using K = VolumeRaycaster*; @@ -362,14 +614,25 @@ void FramebufferRenderer::updateResolution() { glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, _nAaSamples, - GL_RGBA, + GL_RGBA32F, _resolution.x, _resolution.y, true ); - // G-buffer - glBindTexture(GL_TEXTURE_2D, _deferredColorTexture); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainFilterTexture); + + glTexImage2DMultisample( + GL_TEXTURE_2D_MULTISAMPLE, + _nAaSamples, + GL_RGBA32F, + _resolution.x, + _resolution.y, + true + ); + + // HDR / Filtering + glBindTexture(GL_TEXTURE_2D, _hdrFilteringTexture); glTexImage2D( GL_TEXTURE_2D, @@ -386,6 +649,36 @@ void FramebufferRenderer::updateResolution() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // Average Luminosity Computation Texture + glBindTexture(GL_TEXTURE_2D, _computeAveLumTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + 1, + 1, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + + // Bloom Filter + for (int i = 0; i < 3; i++) + { + glBindTexture(GL_TEXTURE_2D, _bloomTexture[i]); + glTexStorage2D( + GL_TEXTURE_2D, + 1, + GL_RGBA16F, + i ? _resolution.x : _resolution.y, + i ? _resolution.y : _resolution.x + ); + } + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainPositionTexture); glTexImage2DMultisample( @@ -422,7 +715,7 @@ void FramebufferRenderer::updateResolution() { glTexImage2D( GL_TEXTURE_2D, 0, - GL_RGBA16, + GL_RGBA16F, _resolution.x, _resolution.y, 0, @@ -577,15 +870,45 @@ void FramebufferRenderer::updateDeferredcastData() { _dirtyDeferredcastData = false; } -void FramebufferRenderer::updateHDRData() { - _hdrBackGroundProgram = ghoul::opengl::ProgramObject::Build( - "HDR Background Control", - absPath("${SHADERS}/framebuffer/hdrBackground.vert"), - absPath("${SHADERS}/framebuffer/hdrBackground.frag") +void FramebufferRenderer::updateAveLum() { + _aveLumProgram = ghoul::opengl::ProgramObject::Build( + "Computes Average Luminace on GPU", + absPath("${SHADERS}/framebuffer/computeAveLum.vert"), + absPath("${SHADERS}/framebuffer/computeAveLum.frag") ); using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _hdrBackGroundProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); - _hdrBackGroundProgram->setIgnoreUniformLocationError(IgnoreError::Yes); + //_aveLumProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_aveLumProgram->setIgnoreUniformLocationError(IgnoreError::Yes); +} + +void FramebufferRenderer::updateBloomConfig() { + _bloomProgram = ghoul::opengl::ProgramObject::Build( + "Appies the Bloom Filter", + absPath("${SHADERS}/framebuffer/bloomFilter.vert"), + absPath("${SHADERS}/framebuffer/bloomFilter.frag") + ); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; + //_bloomProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_bloomProgram->setIgnoreUniformLocationError(IgnoreError::Yes); + + _bloomResolveProgram = ghoul::opengl::ProgramObject::Build( + "Adds bloom to final image", + absPath("${SHADERS}/framebuffer/bloomResolveFilter.vert"), + absPath("${SHADERS}/framebuffer/bloomResolveFilter.frag") + ); + //_bloomResolveProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_bloomResolveProgram->setIgnoreUniformLocationError(IgnoreError::Yes); +} + +void FramebufferRenderer::updateHDRAndFiltering() { + _hdrFilteringProgram = ghoul::opengl::ProgramObject::Build( + "HDR and Filtering Program", + absPath("${SHADERS}/framebuffer/hdrAndFiltering.vert"), + absPath("${SHADERS}/framebuffer/hdrAndfiltering.frag") + ); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; + //_hdrFilteringProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_hdrFilteringProgram->setIgnoreUniformLocationError(IgnoreError::Yes); } void FramebufferRenderer::updateMSAASamplingPattern() { @@ -921,9 +1244,7 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac if (!scene || !camera) { return; - } - - glEnable(GL_DEPTH_TEST); + } // Capture standard fbo GLint defaultFbo; @@ -932,18 +1253,20 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); glEnable(GL_DEPTH_TEST); - // deferred g-buffer - GLenum textureBuffers[3] = { + // deferred g-buffer plus filter + GLenum textureBuffers[4] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 }; - glDrawBuffers(3, textureBuffers); + glDrawBuffers(4, textureBuffers); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnablei(GL_BLEND, 0); glDisablei(GL_BLEND, 1); glDisablei(GL_BLEND, 2); + glDisablei(GL_BLEND, 3); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Time time = global::timeManager.time(); @@ -977,9 +1300,11 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac performRaycasterTasks(tasks.raycasterTasks); } - glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + //glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + glBindFramebuffer(GL_FRAMEBUFFER, _hdrFilteringFramebuffer); GLenum dBuffer[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, dBuffer); + glClear(GL_COLOR_BUFFER_BIT); { std::unique_ptr perfInternal; @@ -991,6 +1316,7 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac performDeferredTasks(tasks.deferredcasterTasks); } + /* if (tasks.deferredcasterTasks.empty()) { glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); _resolveProgram->activate(); @@ -1008,6 +1334,62 @@ void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFac _resolveProgram->deactivate(); } + */ + + // DEBUG - JCC + { + glDisable(GL_DEPTH_TEST); + + applyBloomFilter(); + + float averageLuminaceInFB = 0.0; + if (_toneMapOperator == + static_cast(openspace::RenderEngine::ToneMapOperators::GLOBAL) + ) + { + averageLuminaceInFB = computeBufferAveLuminanceGPU(); + if (std::isnan(averageLuminaceInFB)) { + averageLuminaceInFB = 1000.0; + } + } + + //float averageLuminaceInFB = 0.5; + + glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); + glViewport(0, 0, _resolution.x, _resolution.y); + _hdrFilteringProgram->activate(); + + // No Bloom + /*ghoul::opengl::TextureUnit deferredResultsTextureUnit; + deferredResultsTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _hdrFilteringTexture); + _hdrFilteringProgram->setUniform(_hdrUniformCache.deferredResultsTexture, + deferredResultsTextureUnit);*/ + + // Bloom Enabled + ghoul::opengl::TextureUnit bloomResultsTextureUnit; + bloomResultsTextureUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _bloomTexture[2]); + _hdrFilteringProgram->setUniform(_hdrUniformCache.deferredResultsTexture, + bloomResultsTextureUnit); + + _hdrFilteringProgram->setUniform(_hdrUniformCache.blackoutFactor, blackoutFactor); + _hdrFilteringProgram->setUniform(_hdrUniformCache.backgroundConstant, + _hdrBackground); + _hdrFilteringProgram->setUniform(_hdrUniformCache.backgroundExposure, _hdrExposure); + _hdrFilteringProgram->setUniform(_hdrUniformCache.gamma, _gamma); + _hdrFilteringProgram->setUniform(_hdrUniformCache.toneMapOperator, _toneMapOperator); + _hdrFilteringProgram->setUniform(_hdrUniformCache.aveLum, averageLuminaceInFB); + _hdrFilteringProgram->setUniform(_hdrUniformCache.maxWhite, _maxWhite); + + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + _hdrFilteringProgram->deactivate(); + } + + //glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); } void FramebufferRenderer::performRaycasterTasks(const std::vector& tasks) { @@ -1222,6 +1604,31 @@ void FramebufferRenderer::setGamma(float gamma) { _gamma = gamma; } +void FramebufferRenderer::setMaxWhite(float maxWhite) { + ghoul_assert(gamma > 0.f, "Max White value must be greater than zero"); + _maxWhite = maxWhite; +} + +void FramebufferRenderer::setToneMapOperator(int tmOp) { + _toneMapOperator = tmOp; +} + +void FramebufferRenderer::setBloomThreMin(float minV) { + _bloomThresholdMin = minV; +} + +void FramebufferRenderer::setBloomThreMax(float maxV) { + _bloomThresholdMax = maxV; +} + +void FramebufferRenderer::setBloomOrigFactor(float origFactor) { + _bloomOrigFactor = origFactor; +} + +void FramebufferRenderer::setBloomNewFactor(float newFactor) { + _bloomNewFactor = newFactor; +} + float FramebufferRenderer::hdrBackground() const { return _hdrBackground; } diff --git a/src/rendering/renderengine.cpp b/src/rendering/renderengine.cpp index 0ee89eff7d..47ea110852 100644 --- a/src/rendering/renderengine.cpp +++ b/src/rendering/renderengine.cpp @@ -195,6 +195,43 @@ namespace { "Gamma, is the nonlinear operation used to encode and decode luminance or " "tristimulus values in the image." }; + + constexpr openspace::properties::Property::PropertyInfo MaxWhiteInfo = { + "MaxWhite", + "Max White Value", + "Max value for white color [0.01-10.0] to be used by tone mapping operators." + }; + + constexpr openspace::properties::Property::PropertyInfo BloomThreshouldMinInfo = { + "BloomThreshouldMin", + "Bloom Threshould Min Value", + "Min value a pixel must have to be bloomed." + }; + + constexpr openspace::properties::Property::PropertyInfo BloomThreshouldMaxInfo = { + "BloomThreshouldMax", + "Bloom Threshould Max Value", + "Max value a pixel must have to be bloomed." + }; + + constexpr openspace::properties::Property::PropertyInfo BloomOrigColorFactorInfo = { + "BloomOrigColorFactor", + "Bloom Original Color Factor Value", + "Bloom Original Color Factor Value." + }; + + constexpr openspace::properties::Property::PropertyInfo BloomNewColorFactorInfo = { + "BloomNewColorFactor", + "Bloom New Color Factor Value", + "Bloom New Color Factor Value." + }; + + constexpr openspace::properties::Property::PropertyInfo ToneMapOperatorInfo = { + "ToneMapOperator", + "ToneMap Operator", + "ToneMap Operator is the method used to tranform the pixels using a HDR to" + "pixels using a LDR distribution." + }; } // namespace @@ -216,6 +253,12 @@ RenderEngine::RenderEngine() , _hdrExposure(HDRExposureInfo, 0.4f, 0.01f, 10.0f) , _hdrBackground(BackgroundExposureInfo, 2.8f, 0.01f, 10.0f) , _gamma(GammaInfo, 2.2f, 0.01f, 10.0f) + , _bloomThreshouldMin(BloomThreshouldMinInfo, 0.5, 0.0, 100.0) + , _bloomThreshouldMax(BloomThreshouldMaxInfo, 1.0, 0.0, 100.0) + , _bloomOrigColorFactor(BloomOrigColorFactorInfo, 1.0, 0.0, 100.0) + , _bloomNewColorFactor(BloomNewColorFactorInfo, 1.0, 0.0, 100.0) + , _maxWhite(MaxWhiteInfo, 4.f, 0.001f, 10000.0f) + , _toneMapOperator(ToneMapOperatorInfo, properties::OptionProperty::DisplayType::Dropdown) { _doPerformanceMeasurements.onChange([this](){ global::performanceManager.setEnabled(_doPerformanceMeasurements); @@ -255,6 +298,62 @@ RenderEngine::RenderEngine() }); addProperty(_gamma); + _maxWhite.onChange([this]() { + if (_renderer) { + _renderer->setMaxWhite(_maxWhite); + } + }); + addProperty(_maxWhite); + + _toneMapOperator.addOption(static_cast(ToneMapOperators::EXPONENTIAL), "Exponential"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::LINEAR), "Linear"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::SIMPLE_REINHARD), "Simple Reinhard"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::LUM_BASED_REINHARD), "Lum based Reinhard"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::WHITE_PRESERVING), "White Preserving"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::ROM_BIN_DA_HOUSE), "RomBin da House"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::FILMIC), "Filmic"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::UNCHARTED), "Uncharted 2"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::COSTA), "Costa"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::ADAPTIVE), "Adaptive"); + _toneMapOperator.addOption(static_cast(ToneMapOperators::GLOBAL), "Global"); + _toneMapOperator.set(8); + + _toneMapOperator.onChange([this]() { + if (_renderer) { + _renderer->setToneMapOperator(_toneMapOperator); + } + }); + + addProperty(_toneMapOperator); + + addProperty(_bloomThreshouldMin); + _bloomThreshouldMin.onChange([this]() { + if (_renderer) { + _renderer->setBloomThreMin(_bloomThreshouldMin); + } + }); + + addProperty(_bloomThreshouldMax); + _bloomThreshouldMax.onChange([this]() { + if (_renderer) { + _renderer->setBloomThreMax(_bloomThreshouldMax); + } + }); + + addProperty(_bloomOrigColorFactor); + _bloomOrigColorFactor.onChange([this]() { + if (_renderer) { + _renderer->setBloomOrigFactor(_bloomOrigColorFactor); + } + }); + + addProperty(_bloomNewColorFactor); + _bloomNewColorFactor.onChange([this]() { + if (_renderer) { + _renderer->setBloomNewFactor(_bloomNewColorFactor); + } + }); + addProperty(_applyWarping); _takeScreenshot.onChange([this](){