diff --git a/data/assets/scene/milkyway/milkyway/volume.asset b/data/assets/scene/milkyway/milkyway/volume.asset index 0d1537d45e..78195a1b5c 100644 --- a/data/assets/scene/milkyway/milkyway/volume.asset +++ b/data/assets/scene/milkyway/milkyway/volume.asset @@ -24,7 +24,9 @@ local MilkyWayVolumeGalaxy = { Type = "Volume", Filename = data .. "/MilkyWayRGBAVolume1024x1024x128.raw", Dimensions = {1024, 1024, 128}, - Size = {1.2E21, 1.2E21, 0.15E21} + Size = {1.2E21, 1.2E21, 0.15E21}, + Steps = 480, + Downscale = 0.5, }, Points = { Type = "Points", diff --git a/include/openspace/rendering/framebufferrenderer.h b/include/openspace/rendering/framebufferrenderer.h index a49568fe17..4529fd60a4 100644 --- a/include/openspace/rendering/framebufferrenderer.h +++ b/include/openspace/rendering/framebufferrenderer.h @@ -70,6 +70,7 @@ public: void updateDeferredcastData(); void updateHDRAndFiltering(); void updateFXAA(); + void updateDownscaledVolume(); void setResolution(glm::ivec2 res) override; void setHDRExposure(float hdrExposure) override; @@ -110,6 +111,8 @@ private: void resolveMSAA(float blackoutFactor); void applyTMO(float blackoutFactor); void applyFXAA(); + void updateDownscaleColorTexture(); + void writeDownscaledVolume(); std::map _raycastData; RaycasterProgObjMap _exitPrograms; @@ -122,10 +125,12 @@ private: std::unique_ptr _hdrFilteringProgram; std::unique_ptr _tmoProgram; std::unique_ptr _fxaaProgram; + std::unique_ptr _downscaledVolumeProgram; UniformCache(hdrFeedingTexture, blackoutFactor, hdrExposure, gamma, Hue, Saturation, Value) _hdrUniformCache; UniformCache(renderedTexture, inverseScreenSize) _fxaaUniformCache; + UniformCache(downscaledRenderedVolume) _writeDownscaledVolumeUniformCache; GLint _defaultFBO; GLuint _screenQuad; @@ -157,6 +162,12 @@ private: GLuint fxaaTexture; } _fxaaBuffers; + struct { + GLuint framebuffer; + GLuint colorTexture; + float currentDownscaleFactor = 1.f; + } _downscaleVolumeRendering; + unsigned int _pingPongIndex = 0u; bool _dirtyDeferredcastData; diff --git a/include/openspace/rendering/volumeraycaster.h b/include/openspace/rendering/volumeraycaster.h index f8ed000553..687a5f335a 100644 --- a/include/openspace/rendering/volumeraycaster.h +++ b/include/openspace/rendering/volumeraycaster.h @@ -128,6 +128,25 @@ public: * helper file) which should be a prefix to all symbols defined by the helper */ virtual std::string helperPath() const = 0; + + virtual void setMaxSteps(int nsteps) { _rayCastMaxSteps = nsteps; }; + + virtual const int maxSteps() const { return _rayCastMaxSteps; }; + + virtual void setDownscaleRender(float value) { _downscaleRenderConst = value; }; + + virtual const float downscaleRender() const { return _downscaleRenderConst; }; + +private: + /** + * Maximum number of integration steps to be executed by the volume integrator. + */ + int _rayCastMaxSteps = 1000; + + /** + * Enable and set the downscale rendering of the volume. Used to improve performance. + */ + float _downscaleRenderConst = 1.0f; }; } // namespace openspace diff --git a/modules/galaxy/rendering/renderablegalaxy.cpp b/modules/galaxy/rendering/renderablegalaxy.cpp index 1d88c2da23..758ab082a1 100644 --- a/modules/galaxy/rendering/renderablegalaxy.cpp +++ b/modules/galaxy/rendering/renderablegalaxy.cpp @@ -224,6 +224,18 @@ namespace openspace { LERROR("No volume dimensions specified."); } + if (volumeDictionary.hasKey("Steps")) { + _rayCastSteps = static_cast(volumeDictionary.value("Steps")); + } + else { + LINFO("Number of raycasting steps not specified for Milkway Galaxy." + " Using default value."); + } + + if (volumeDictionary.hasKey("Downscale")) { + _downScaleVolumeRendering = volumeDictionary.value("Downscale"); + } + if (!dictionary.hasKeyAndValue("Points")) { LERROR("No points dictionary specified."); } @@ -470,6 +482,8 @@ void RenderableGalaxy::update(const UpdateData& data) { volumeTransform[3] += translation; _pointTransform[3] += translation; + _raycaster->setDownscaleRender(_downScaleVolumeRendering); + _raycaster->setMaxSteps(_rayCastSteps); _raycaster->setStepSize(_stepSize); _raycaster->setAspect(_aspect); _raycaster->setModelTransform(volumeTransform); diff --git a/modules/galaxy/rendering/renderablegalaxy.h b/modules/galaxy/rendering/renderablegalaxy.h index 0b49d848c4..8346820b99 100644 --- a/modules/galaxy/rendering/renderablegalaxy.h +++ b/modules/galaxy/rendering/renderablegalaxy.h @@ -79,6 +79,8 @@ private: glm::ivec3 _volumeDimensions; std::string _pointsFilename; std::string _pointSpreadFunctionTexturePath; + int _rayCastSteps = 1000; + float _downScaleVolumeRendering = 1.f; std::unique_ptr _raycaster; std::unique_ptr>> _volume; diff --git a/shaders/framebuffer/hdrAndFiltering.frag b/shaders/framebuffer/hdrAndFiltering.frag index b22ebd9bc4..0e2b3b2090 100644 --- a/shaders/framebuffer/hdrAndFiltering.frag +++ b/shaders/framebuffer/hdrAndFiltering.frag @@ -38,7 +38,6 @@ uniform float Hue; uniform float Saturation; uniform float Value; uniform float Lightness; -uniform int nAaSamples; uniform sampler2D hdrFeedingTexture; diff --git a/shaders/framebuffer/mergeDownscaledVolume.frag b/shaders/framebuffer/mergeDownscaledVolume.frag new file mode 100644 index 0000000000..f34a300742 --- /dev/null +++ b/shaders/framebuffer/mergeDownscaledVolume.frag @@ -0,0 +1,35 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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 sampler2D downscaledRenderedVolume; + +in vec2 texCoord; + +void main() { + finalColor = texture(downscaledRenderedVolume, texCoord); +} diff --git a/shaders/framebuffer/mergeDownscaledVolume.vert b/shaders/framebuffer/mergeDownscaledVolume.vert new file mode 100644 index 0000000000..f737e51882 --- /dev/null +++ b/shaders/framebuffer/mergeDownscaledVolume.vert @@ -0,0 +1,33 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2019 * + * * + * 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/raycastframebuffer.frag b/shaders/framebuffer/raycastframebuffer.frag index b5a972cff8..120bcdd2b8 100644 --- a/shaders/framebuffer/raycastframebuffer.frag +++ b/shaders/framebuffer/raycastframebuffer.frag @@ -32,6 +32,8 @@ uniform bool insideRaycaster; uniform vec3 cameraPosInRaycaster; uniform vec2 windowSize; +uniform int rayCastSteps; + #include "blending.glsl" #include "rand.glsl" #include "floatoperations.glsl" @@ -45,7 +47,6 @@ uniform vec2 windowSize; out vec4 finalColor; #define ALPHA_LIMIT 0.99 -#define RAYCAST_MAX_STEPS 480 #include <#{getEntryPath}> @@ -99,7 +100,7 @@ void main() { for (nSteps = 0; (accumulatedAlpha.r < ALPHA_LIMIT || accumulatedAlpha.g < ALPHA_LIMIT || - accumulatedAlpha.b < ALPHA_LIMIT) && nSteps < RAYCAST_MAX_STEPS; + accumulatedAlpha.b < ALPHA_LIMIT) && nSteps < rayCastSteps; ++nSteps) { if (nextStepSize < raycastDepth / 10000000000.f) { diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index 2007f13a53..b6ce5df96e 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -59,6 +59,10 @@ namespace { "renderedTexture", "inverseScreenSize" }; + constexpr const std::array DownscaledVolumeUniformNames = { + "downscaledRenderedVolume" + }; + constexpr const char* ExitFragmentShaderPath = "${SHADERS}/framebuffer/exitframebuffer.frag"; constexpr const char* RaycastFragmentShaderPath = @@ -184,6 +188,10 @@ void FramebufferRenderer::initialize() { glGenFramebuffers(1, &_fxaaBuffers.fxaaFramebuffer); glGenTextures(1, &_fxaaBuffers.fxaaTexture); + // DownscaleVolumeRendering + glGenFramebuffers(1, &_downscaleVolumeRendering.framebuffer); + glGenTextures(1, &_downscaleVolumeRendering.colorTexture); + // Allocate Textures/Buffers Memory updateResolution(); @@ -307,11 +315,29 @@ void FramebufferRenderer::initialize() { LERROR("FXAA framebuffer is not complete"); } + //================================================// + //===== Downscale Volume Rendering Buffers =====// + //================================================// + glBindFramebuffer(GL_FRAMEBUFFER, _downscaleVolumeRendering.framebuffer); + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + _downscaleVolumeRendering.colorTexture, + 0 + ); + + status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LERROR("Downscale Volume Rendering framebuffer is not complete"); + } + + // JCC: Moved to here to avoid NVidia: "Program/shader state performance warning" // Building programs updateHDRAndFiltering(); updateFXAA(); updateDeferredcastData(); + updateDownscaledVolume(); // Sets back to default FBO glBindFramebuffer(GL_FRAMEBUFFER, _defaultFBO); @@ -326,6 +352,11 @@ void FramebufferRenderer::initialize() { _fxaaUniformCache, FXAAUniformNames ); + ghoul::opengl::updateUniformLocations( + *_downscaledVolumeProgram, + _writeDownscaledVolumeUniformCache, + DownscaledVolumeUniformNames + ); global::raycasterManager.addListener(*this); global::deferredcasterManager.addListener(*this); @@ -342,6 +373,7 @@ void FramebufferRenderer::deinitialize() { glDeleteFramebuffers(1, &_hdrBuffers.hdrFilteringFramebuffer); glDeleteFramebuffers(1, &_fxaaBuffers.fxaaFramebuffer); glDeleteFramebuffers(1, &_pingPongBuffers.framebuffer); + glDeleteFramebuffers(1, &_downscaleVolumeRendering.framebuffer); glDeleteTextures(1, &_gBuffers.colorTexture); glDeleteTextures(1, &_gBuffers.depthTexture); @@ -350,7 +382,8 @@ void FramebufferRenderer::deinitialize() { glDeleteTextures(1, &_fxaaBuffers.fxaaTexture); glDeleteTextures(1, &_gBuffers.positionTexture); glDeleteTextures(1, &_gBuffers.normalTexture); - + glDeleteTextures(1, &_downscaleVolumeRendering.colorTexture); + glDeleteTextures(1, &_pingPongBuffers.colorTexture[1]); glDeleteTextures(1, &_exitColorTexture); @@ -447,6 +480,90 @@ void FramebufferRenderer::applyFXAA() { _fxaaProgram->deactivate(); } +void FramebufferRenderer::updateDownscaleColorTexture() { + glBindTexture(GL_TEXTURE_2D, _downscaleVolumeRendering.colorTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x * _downscaleVolumeRendering.currentDownscaleFactor, + _resolution.y * _downscaleVolumeRendering.currentDownscaleFactor, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void FramebufferRenderer::writeDownscaledVolume() { + const bool doPerformanceMeasurements = global::performanceManager.isEnabled(); + std::unique_ptr perfInternal; + + if (doPerformanceMeasurements) { + perfInternal = std::make_unique( + "FramebufferRenderer::render::writeDownscaledVolume" + ); + } + + // Saving current OpenGL state + GLboolean blendEnabled = glIsEnabledi(GL_BLEND, 0); + + GLenum blendEquationRGB; + glGetIntegerv(GL_BLEND_EQUATION_RGB, &blendEquationRGB); + + GLenum blendEquationAlpha; + glGetIntegerv(GL_BLEND_EQUATION_ALPHA, &blendEquationAlpha); + + GLenum blendDestAlpha; + glGetIntegerv(GL_BLEND_DST_ALPHA, &blendDestAlpha); + + GLenum blendDestRGB; + glGetIntegerv(GL_BLEND_DST_RGB, &blendDestRGB); + + GLenum blendSrcAlpha; + glGetIntegerv(GL_BLEND_SRC_ALPHA, &blendSrcAlpha); + + GLenum blendSrcRGB; + glGetIntegerv(GL_BLEND_SRC_RGB, &blendSrcRGB); + + glEnablei(GL_BLEND, 0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + _downscaledVolumeProgram->activate(); + + ghoul::opengl::TextureUnit downscaledTextureUnit; + downscaledTextureUnit.activate(); + glBindTexture( + GL_TEXTURE_2D, + _downscaleVolumeRendering.colorTexture + ); + + _downscaledVolumeProgram->setUniform( + _writeDownscaledVolumeUniformCache.downscaledRenderedVolume, + downscaledTextureUnit + ); + + glEnablei(GL_BLEND, 0); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + glBindVertexArray(_screenQuad); + glDrawArrays(GL_TRIANGLES, 0, 6); + glBindVertexArray(0); + + _downscaledVolumeProgram->deactivate(); + + // Restores blending state + glBlendEquationSeparate(blendEquationRGB, blendEquationAlpha); + glBlendFuncSeparate(blendSrcRGB, blendDestRGB, blendSrcAlpha, blendDestAlpha); + + if (!blendEnabled) { + glDisablei(GL_BLEND, 0); + } + +} + void FramebufferRenderer::update() { if (_dirtyResolution) { updateResolution(); @@ -480,6 +597,16 @@ void FramebufferRenderer::update() { ); } + if (_downscaledVolumeProgram->isDirty()) { + _downscaledVolumeProgram->rebuildFromFile(); + + ghoul::opengl::updateUniformLocations( + *_downscaledVolumeProgram, + _writeDownscaledVolumeUniformCache, + DownscaledVolumeUniformNames + ); + } + using K = VolumeRaycaster*; using V = std::unique_ptr; for (const std::pair& program : _exitPrograms) { @@ -639,6 +766,23 @@ void FramebufferRenderer::updateResolution() { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + // Downscale Volume Rendering + glBindTexture(GL_TEXTURE_2D, _downscaleVolumeRendering.colorTexture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA32F, + _resolution.x * _downscaleVolumeRendering.currentDownscaleFactor, + _resolution.y * _downscaleVolumeRendering.currentDownscaleFactor, + 0, + GL_RGBA, + GL_FLOAT, + nullptr + ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // Volume Rendering Textures glBindTexture(GL_TEXTURE_2D, _exitColorTexture); glTexImage2D( @@ -822,6 +966,17 @@ void FramebufferRenderer::updateFXAA() { //_fxaaProgram->setIgnoreUniformLocationError(IgnoreError::Yes); } +void FramebufferRenderer::updateDownscaledVolume() { + _downscaledVolumeProgram = ghoul::opengl::ProgramObject::Build( + "Write Downscaled Volume Program", + absPath("${SHADERS}/framebuffer/mergeDownscaledVolume.vert"), + absPath("${SHADERS}/framebuffer/mergeDownscaledVolume.frag") + ); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; + //_downscaledVolumeProgram->setIgnoreSubroutineUniformLocationError(IgnoreError::Yes); + //_downscaledVolumeProgram->setIgnoreUniformLocationError(IgnoreError::Yes); +} + void FramebufferRenderer::render(Scene* scene, Camera* camera, float blackoutFactor) { // Set OpenGL default rendering state glGetIntegerv(GL_FRAMEBUFFER_BINDING, &_defaultFBO); @@ -945,7 +1100,20 @@ void FramebufferRenderer::performRaycasterTasks(const std::vector exitProgram->deactivate(); } - glBindFramebuffer(GL_FRAMEBUFFER, _gBuffers.framebuffer); + if (raycaster->downscaleRender() != 1.f) { + float scaleDown = raycaster->downscaleRender(); + glBindFramebuffer(GL_FRAMEBUFFER, _downscaleVolumeRendering.framebuffer); + glViewport(0, 0, _resolution.x * scaleDown, _resolution.y * scaleDown); + if (_downscaleVolumeRendering.currentDownscaleFactor != scaleDown) { + _downscaleVolumeRendering.currentDownscaleFactor = scaleDown; + updateDownscaleColorTexture(); + } + glClear(GL_COLOR_BUFFER_BIT); + } + else { + glBindFramebuffer(GL_FRAMEBUFFER, _gBuffers.framebuffer); + } + glm::vec3 cameraPosition; bool isCameraInside = raycaster->isCameraInside( raycasterTask.renderData, @@ -977,6 +1145,8 @@ void FramebufferRenderer::performRaycasterTasks(const std::vector } if (raycastProgram) { + raycastProgram->setUniform("rayCastSteps", raycaster->maxSteps()); + raycaster->preRaycast(_raycastData[raycaster], *raycastProgram); ghoul::opengl::TextureUnit exitColorTextureUnit; @@ -994,7 +1164,16 @@ void FramebufferRenderer::performRaycasterTasks(const std::vector glBindTexture(GL_TEXTURE_2D, _gBuffers.depthTexture); raycastProgram->setUniform("mainDepthTexture", mainDepthTextureUnit); - raycastProgram->setUniform("windowSize", static_cast(_resolution)); + if (raycaster->downscaleRender() != 1.f) { + float scaleDown = raycaster->downscaleRender(); + raycastProgram->setUniform( + "windowSize", + glm::vec2(_resolution.x * scaleDown, _resolution.y * scaleDown) + ); + } + else { + raycastProgram->setUniform("windowSize", static_cast(_resolution)); + } glDisable(GL_DEPTH_TEST); glDepthMask(false); @@ -1015,6 +1194,14 @@ void FramebufferRenderer::performRaycasterTasks(const std::vector else { LWARNING("Raycaster is not attached when trying to perform raycaster task"); } + + if (raycaster->downscaleRender() != 1.f) { + float scaleDown = raycaster->downscaleRender(); + glViewport(0, 0, _resolution.x, _resolution.y); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, _gBuffers.framebuffer); + writeDownscaledVolume(); + + } } }