/***************************************************************************************** * * * 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. * ****************************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr const char* _loggerCat = "ABufferRenderer"; constexpr const char* BoundsFragmentShaderPath = "${SHADERS}/abuffer/boundsabuffer.frag"; constexpr const char* RenderFragmentShaderPath = "${SHADERS}/abuffer/renderabuffer.frag"; constexpr int MaxRaycasters = 32; constexpr int MaxLayers = 32; constexpr int MaxAverageLayers = 8; } // namespace namespace openspace { ABufferRenderer::ABufferRenderer() : _resolution(glm::ivec2(0)) , _dirtyResolution(true) , _dirtyRendererData(true) , _dirtyRaycastData(true) , _dirtyResolveDictionary(true) , _resolveProgram(nullptr) , _hdrExposure(0.4f) , _hdrBackground(2.8f) , _gamma(2.2f) {} ABufferRenderer::~ABufferRenderer() {} void ABufferRenderer::initialize() { LINFO("Initializing ABufferRenderer"); const GLfloat size = 1.f; const GLfloat vertex_data[] = { // x y s t -size, -size, 0.f, 1.f, size, size, 0.f, 1.f, -size, size, 0.f, 1.f, -size, -size, 0.f, 1.f, size, -size, 0.f, 1.f, size, size, 0.f, 1.f, }; glGenVertexArrays(1, &_screenQuad); glBindVertexArray(_screenQuad); glGenBuffers(1, &_vertexPositionBuffer); glBindBuffer(GL_ARRAY_BUFFER, _vertexPositionBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 4, nullptr); glEnableVertexAttribArray(0); glGenTextures(1, &_anchorPointerTexture); glGenBuffers(1, &_anchorPointerTextureInitializer); glGenBuffers(1, &_atomicCounterBuffer); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, _atomicCounterBuffer); glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), nullptr, GL_DYNAMIC_COPY); glGenBuffers(1, &_fragmentBuffer); glGenTextures(1, &_fragmentTexture); glGenTextures(1, &_mainColorTexture); glGenTextures(1, &_mainDepthTexture); glGenFramebuffers(1, &_mainFramebuffer); GLint defaultFbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); updateResolution(); updateRendererData(); updateRaycastData(); updateResolveDictionary(); updateMSAASamplingPattern(); glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture, 0 ); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture, 0 ); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { LERROR("Main framebuffer is not complete"); } glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); try { ghoul::Dictionary dict; dict.setValue("resolveData", _resolveDictionary); dict.setValue("rendererData", _rendererData); _resolveProgram = ghoul::opengl::ProgramObject::Build( "ABuffer Resolve", "${SHADERS}/abuffer/resolveabuffer.vert", "${SHADERS}/abuffer/resolveabuffer.frag", dict ); } catch (const ghoul::RuntimeError& e) { LERRORC(e.component, e.message); } OsEng.renderEngine().raycasterManager().addListener(*this); } void ABufferRenderer::deinitialize() { LINFO("Deinitializing ABufferRenderer"); glDeleteBuffers(1, &_fragmentBuffer); glDeleteTextures(1, &_fragmentTexture); glDeleteTextures(1, &_anchorPointerTexture); glDeleteBuffers(1, &_anchorPointerTextureInitializer); glDeleteBuffers(1, &_atomicCounterBuffer); glDeleteBuffers(1, &_vertexPositionBuffer); glDeleteVertexArrays(1, &_screenQuad); OsEng.renderEngine().raycasterManager().removeListener(*this); } void ABufferRenderer::raycastersChanged(VolumeRaycaster&, bool) { _dirtyRaycastData = true; } void ABufferRenderer::update() { PerfMeasure("ABufferRenderer::update"); // Make sure that the fragment buffer has the correct resoliution // according to the output render buffer size if (_dirtyResolution) { updateResolution(); updateMSAASamplingPattern(); } // Make sure that the renderengine gets the correct render data // to feed into all render programs. // This will trigger a recompilation of all the shader programs // involved in rendering geometries. if (_dirtyRendererData) { updateRendererData(); } // Make sure that all raycaster data is up to date. if (_dirtyRaycastData) { updateRaycastData(); } // Make sure that the resolve dictionary is up to date. // The resolve dictionary contains information for all // ray casters, including shader include paths. if (_dirtyResolveDictionary) { updateResolveDictionary(); ghoul::Dictionary dict; dict.setValue("resolveData", _resolveDictionary); dict.setValue("rendererData", _rendererData); _resolveProgram->setDictionary(dict); } // If the resolve dictionary changed (or a file changed on disk) // then rebuild the resolve program. if (_resolveProgram->isDirty()) { try { _resolveProgram->rebuildFromFile(); } catch (const ghoul::RuntimeError& error) { LERRORC(error.component, error.message); } } using K = VolumeRaycaster* const; using V = std::unique_ptr; for (std::pair& program : _boundsPrograms) { if (program.second->isDirty()) { try { program.second->rebuildFromFile(); } catch (const ghoul::RuntimeError e) { LERRORC(e.component, e.message); } } } } void ABufferRenderer::updateMSAASamplingPattern() { // @CLEANUP(abock): This should probably be merged with the same code from the // framebuffer renderer? LINFO("Updating MSAA Sampling Pattern"); constexpr const int GridSize = 32; GLfloat step = 2.f / static_cast(GridSize); GLfloat sizeX = -1.0f; GLfloat sizeY = 1.0f; constexpr const int NVertex = 4 * 6; // openPixelSizeVertexData GLfloat vertexData[GridSize * GridSize * NVertex]; for (int y = 0; y < GridSize; ++y) { for (int x = 0; x < GridSize; ++x) { vertexData[y * GridSize * NVertex + x * NVertex] = sizeX; vertexData[y * GridSize * NVertex + x * NVertex + 1] = sizeY - step; vertexData[y * GridSize * NVertex + x * NVertex + 2] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 3] = 1.f; vertexData[y * GridSize * NVertex + x * NVertex + 4] = sizeX + step; vertexData[y * GridSize * NVertex + x * NVertex + 5] = sizeY; vertexData[y * GridSize * NVertex + x * NVertex + 6] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 7] = 1.f; vertexData[y * GridSize * NVertex + x * NVertex + 8] = sizeX; vertexData[y * GridSize * NVertex + x * NVertex + 9] = sizeY; vertexData[y * GridSize * NVertex + x * NVertex + 10] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 11] = 1.f; vertexData[y * GridSize * NVertex + x * NVertex + 12] = sizeX; vertexData[y * GridSize * NVertex + x * NVertex + 13] = sizeY - step; vertexData[y * GridSize * NVertex + x * NVertex + 14] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 15] = 1.f; vertexData[y * GridSize * NVertex + x * NVertex + 16] = sizeX + step; vertexData[y * GridSize * NVertex + x * NVertex + 17] = sizeY - step; vertexData[y * GridSize * NVertex + x * NVertex + 18] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 19] = 1.f; vertexData[y * GridSize * NVertex + x * NVertex + 20] = sizeX + step; vertexData[y * GridSize * NVertex + x * NVertex + 21] = sizeY; vertexData[y * GridSize * NVertex + x * NVertex + 22] = 0.f; vertexData[y * GridSize * NVertex + x * NVertex + 23] = 1.f; sizeX += step; } sizeX = -1.f; sizeY -= step; } GLuint pixelSizeQuadVAO = 0; GLuint pixelSizeQuadVBO = 0; glGenVertexArrays(1, &pixelSizeQuadVAO); glBindVertexArray(pixelSizeQuadVAO); glGenBuffers(1, &pixelSizeQuadVBO); glBindBuffer(GL_ARRAY_BUFFER, pixelSizeQuadVBO); glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat) * GridSize * GridSize * NVertex, vertexData, GL_STATIC_DRAW ); // Position glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr); glEnableVertexAttribArray(0); // Saves current state GLint defaultFbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); GLint viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); // Main framebuffer GLuint pixelSizeTexture = 0; GLuint pixelSizeFramebuffer = 0; glGenTextures(1, &pixelSizeTexture); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture); const GLsizei ONEPIXEL = 1; glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, _nAaSamples, GL_RGBA32F, ONEPIXEL, ONEPIXEL, true ); glViewport(0, 0, ONEPIXEL, ONEPIXEL); glGenFramebuffers(1, &pixelSizeFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, pixelSizeFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture, 0 ); GLenum textureBuffers[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, textureBuffers); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { LERROR("MSAA Sampling pattern framebuffer is not complete"); return; } std::unique_ptr pixelSizeProgram = ghoul::opengl::ProgramObject::Build( "OnePixel MSAA", "${SHADERS}/framebuffer/pixelSizeMSAA.vert", "${SHADERS}/framebuffer/pixelSizeMSAA.frag" ); pixelSizeProgram->activate(); // Draw sub-pixel grid glEnable(GL_SAMPLE_SHADING); glBindVertexArray(pixelSizeQuadVAO); glDisable(GL_DEPTH_TEST); glDepthMask(false); glDrawArrays(GL_TRIANGLES, 0, GridSize * GridSize * 6); glBindVertexArray(0); glDepthMask(true); glEnable(GL_DEPTH_TEST); glDisable(GL_SAMPLE_SHADING); pixelSizeProgram->deactivate(); // Now we render the Nx1 quad strip GLuint nOneStripFramebuffer = 0; GLuint nOneStripVAO = 0; GLuint nOneStripVBO = 0; GLuint nOneStripTexture = 0; sizeX = -1.f; step = 2.f / static_cast(_nAaSamples); std::vector nOneStripVertexData(_nAaSamples * (NVertex + 12)); for (int x = 0; x < _nAaSamples; ++x) { nOneStripVertexData[x * (NVertex + 12)] = sizeX; nOneStripVertexData[x * (NVertex + 12) + 1] = -1.0f; nOneStripVertexData[x * (NVertex + 12) + 2] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 3] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 4] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 5] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 6] = sizeX + step; nOneStripVertexData[x * (NVertex + 12) + 7] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 8] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 9] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 10] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 11] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 12] = sizeX; nOneStripVertexData[x * (NVertex + 12) + 13] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 14] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 15] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 16] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 17] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 18] = sizeX; nOneStripVertexData[x * (NVertex + 12) + 19] = -1.0f; nOneStripVertexData[x * (NVertex + 12) + 20] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 21] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 22] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 23] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 24] = sizeX + step; nOneStripVertexData[x * (NVertex + 12) + 25] = -1.0f; nOneStripVertexData[x * (NVertex + 12) + 26] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 27] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 28] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 29] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 30] = sizeX + step; nOneStripVertexData[x * (NVertex + 12) + 31] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 32] = 0.0f; nOneStripVertexData[x * (NVertex + 12) + 33] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 34] = 1.0f; nOneStripVertexData[x * (NVertex + 12) + 35] = 1.0f; sizeX += step; } glGenVertexArrays(1, &nOneStripVAO); glBindVertexArray(nOneStripVAO); glGenBuffers(1, &nOneStripVBO); glBindBuffer(GL_ARRAY_BUFFER, nOneStripVBO); glBufferData( GL_ARRAY_BUFFER, sizeof(GLfloat) * _nAaSamples * (NVertex + 12), nOneStripVertexData.data(), GL_STATIC_DRAW ); // position glVertexAttribPointer( 0, 4, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, nullptr ); glEnableVertexAttribArray(0); // texture coords glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, reinterpret_cast(sizeof(GLfloat) * 4) ); glEnableVertexAttribArray(1); // fbo texture buffer glGenTextures(1, &nOneStripTexture); glBindTexture(GL_TEXTURE_2D, nOneStripTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA32F, _nAaSamples, ONEPIXEL, 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); glGenFramebuffers(1, &nOneStripFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER, nOneStripFramebuffer); glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, nOneStripTexture, 0 ); status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { LERROR("nOneStrip framebuffer is not complete"); } glViewport(0, 0, _nAaSamples, ONEPIXEL); std::unique_ptr nOneStripProgram = ghoul::opengl::ProgramObject::Build( "OneStrip MSAA", "${SHADERS}/framebuffer/nOneStripMSAA.vert", "${SHADERS}/framebuffer/nOneStripMSAA.frag" ); nOneStripProgram->activate(); ghoul::opengl::TextureUnit pixelSizeTextureUnit; pixelSizeTextureUnit.activate(); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, pixelSizeTexture); nOneStripProgram->setUniform("pixelSizeTexture", pixelSizeTextureUnit); // render strip glDrawBuffers(1, textureBuffers); glClearColor(0.f, 1.f, 0.f, 1.f); glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(nOneStripVAO); glDisable(GL_DEPTH_TEST); glDepthMask(false); for (int sample = 0; sample < _nAaSamples; ++sample) { nOneStripProgram->setUniform("currentSample", sample); glDrawArrays(GL_TRIANGLES, sample * 6, 6); } /*nOneStripProgram->setUniform("currentSample", 0); glDrawArrays(GL_TRIANGLES, 0, 6 * _nAaSamples);*/ glDepthMask(true); glEnable(GL_DEPTH_TEST); glBindVertexArray(0); saveTextureToMemory(GL_COLOR_ATTACHMENT0, _nAaSamples, 1, _mSAAPattern); // Convert back to [-1, 1] range and then scales to the current viewport size: for (int d = 0; d < _nAaSamples; ++d) { _mSAAPattern[d * 3] = (2.0 * _mSAAPattern[d * 3] - 1.0) / static_cast(viewport[2]); _mSAAPattern[(d * 3) + 1] = (2.0 * _mSAAPattern[(d * 3) + 1] - 1.0) / static_cast(viewport[3]); _mSAAPattern[(d * 3) + 2] = 0.0; } nOneStripProgram->deactivate(); // Restores default state glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); glViewport(viewport[0], viewport[1], viewport[2], viewport[3]); // Deletes unused buffers glDeleteFramebuffers(1, &pixelSizeFramebuffer); glDeleteTextures(1, &pixelSizeTexture); glDeleteBuffers(1, &pixelSizeQuadVBO); glDeleteVertexArrays(1, &pixelSizeQuadVAO); glDeleteFramebuffers(1, &nOneStripFramebuffer); glDeleteTextures(1, &nOneStripTexture); glDeleteBuffers(1, &nOneStripVBO); glDeleteVertexArrays(1, &nOneStripVAO); } void ABufferRenderer::render(Scene* scene, Camera* camera, float blackoutFactor, bool doPerformanceMeasurements) { PerfMeasure("ABufferRenderer::render"); if (!scene || !camera) { return; } _mainColorTextureUnit = std::make_unique(); _mainColorTextureUnit->activate(); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); _mainDepthTextureUnit = std::make_unique(); _mainDepthTextureUnit->activate(); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); GLint defaultFbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFbo); glBindFramebuffer(GL_FRAMEBUFFER, _mainFramebuffer); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset clear(); glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); // Step 1: Render geometries to the fragment buffer // Bind head-pointer image for read-write glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, _atomicCounterBuffer); glBindImageTexture(0, _anchorPointerTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI); glBindImageTexture(1, _fragmentTexture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32UI); // Render the scene to the fragment buffer. Collect renderer tasks (active raycasters) int renderBinMask = static_cast(Renderable::RenderBin::Background) | static_cast(Renderable::RenderBin::Opaque) | static_cast(Renderable::RenderBin::Transparent) | static_cast(Renderable::RenderBin::Overlay); Time time = OsEng.timeManager().time(); RenderData data { *camera, psc(), time, doPerformanceMeasurements, renderBinMask, {} }; RendererTasks tasks; scene->render(data, tasks); _blackoutFactor = blackoutFactor; glBindFramebuffer(GL_FRAMEBUFFER, defaultFbo); // Step 2: Perform raycasting tasks requested by the scene for (const RaycasterTask& raycasterTask : tasks.raycasterTasks) { VolumeRaycaster* raycaster = raycasterTask.raycaster; ghoul::opengl::ProgramObject* program = _boundsPrograms[raycaster].get(); if (program) { program->activate(); program->setUniform("_exit_", false); raycaster->renderEntryPoints(raycasterTask.renderData, *program); program->setUniform("_exit_", true); raycaster->renderExitPoints(raycasterTask.renderData, *program); program->deactivate(); } else { LWARNING("Raycaster is not attached when trying to perform raycaster task"); } } // Step 3: Resolve the buffer _resolveProgram->activate(); // TEMPORARY GAMMA CORRECTION. glm::vec3 cameraPos = data.camera.position().vec3(); float maxComponent = std::max( std::max(std::abs(cameraPos.x), std::abs(cameraPos.y)), std::abs(cameraPos.z) ); float logDistance = std::log(glm::length(cameraPos / maxComponent) * maxComponent) / std::log(10.f); const float minLogDist = 15.f; const float maxLogDist = 20.f; float t = (logDistance - minLogDist) / (maxLogDist - minLogDist); t = glm::clamp(t, 0.0f, 1.0f); const float gamma = 1.f * (1.f - t) + 2.2f * t; _resolveProgram->setUniform("gamma", gamma); // END TEMPORARY GAMMA CORRECTION. _resolveProgram->setUniform("mainColorTexture", _mainColorTextureUnit->unitNumber()); _resolveProgram->setUniform("mainDepthTexture", _mainDepthTextureUnit->unitNumber()); _resolveProgram->setUniform("blackoutFactor", _blackoutFactor); _resolveProgram->setUniform("nAaSamples", _nAaSamples); for (const RaycasterTask& raycasterTask : tasks.raycasterTasks) { preRaycast(raycasterTask); } glBindVertexArray(_screenQuad); glDrawArrays(GL_TRIANGLES, 0, 6); for (const RaycasterTask& raycasterTask : tasks.raycasterTasks) { postRaycast(raycasterTask); } _resolveProgram->deactivate(); _mainColorTextureUnit = nullptr; _mainDepthTextureUnit = nullptr; } void ABufferRenderer::preRaycast(const RaycasterTask& raycasterTask) { VolumeRaycaster& raycaster = *raycasterTask.raycaster; const RaycastData& raycastData = _raycastData[&raycaster]; const RenderData& renderData = raycasterTask.renderData; raycaster.preRaycast(raycastData, *_resolveProgram); glm::vec3 localCameraPosition; bool cameraIsInside = raycaster.cameraIsInside(renderData, localCameraPosition); int uniformIndex = raycastData.id + 1; // uniforms are indexed from 1 (not from 0) _resolveProgram->setUniform( "insideRaycaster" + std::to_string(uniformIndex), cameraIsInside ); if (cameraIsInside) { _resolveProgram->setUniform( "cameraPosInRaycaster" + std::to_string(uniformIndex), localCameraPosition ); } } void ABufferRenderer::postRaycast(const RaycasterTask& raycasterTask) { VolumeRaycaster& raycaster = *raycasterTask.raycaster; const RaycastData& raycastData = _raycastData[&raycaster]; raycaster.postRaycast(raycastData, *_resolveProgram); } void ABufferRenderer::setResolution(glm::ivec2 res) { if (res != _resolution) { _resolution = std::move(res); _dirtyResolution = true; } } void ABufferRenderer::setNAaSamples(int nAaSamples) { _nAaSamples = nAaSamples; if (_nAaSamples == 0) { _nAaSamples = 1; } if (_nAaSamples > 8) { LERROR("Framebuffer renderer does not support more than 8 MSAA samples."); _nAaSamples = 8; } _dirtyResolution = true; } void ABufferRenderer::setHDRExposure(float hdrExposure) { _hdrExposure = hdrExposure; if (_hdrExposure < 0.0) { LERROR("HDR Exposure constant must be greater than zero."); _hdrExposure = 1.0; } } void ABufferRenderer::setHDRBackground(float hdrBackground) { _hdrBackground = hdrBackground; if (_hdrBackground < 0.0) { LERROR("HDR Background constant must be greater than zero."); _hdrBackground = 1.0; } } void ABufferRenderer::setGamma(float gamma) { _gamma = gamma; if (_gamma < 0.0f) { LERROR("Gamma value must be greater than zero."); _gamma = 2.2f; } } float ABufferRenderer::hdrBackground() const { return _hdrBackground; } int ABufferRenderer::nAaSamples() const { return _nAaSamples; } std::vector ABufferRenderer::mSSAPattern() const { return _mSAAPattern; } void ABufferRenderer::clear() { glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _anchorPointerTextureInitializer); glBindTexture(GL_TEXTURE_2D, _anchorPointerTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_R32UI, _resolution.x, _resolution.y, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr ); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); static const GLuint zero = 1; glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, _atomicCounterBuffer); glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(zero), &zero); glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, 0); } void ABufferRenderer::updateResolution() { PerfMeasure("ABufferRenderer::updateResolution"); int totalPixels = _resolution.x * _resolution.y; glBindTexture(GL_TEXTURE_2D, _anchorPointerTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_R32UI, _resolution.x, _resolution.y, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, _anchorPointerTextureInitializer); glBufferData( GL_PIXEL_UNPACK_BUFFER, totalPixels * sizeof(GLuint), nullptr, GL_STATIC_DRAW ); GLuint* data = reinterpret_cast( glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY) ); memset(data, 0x00, totalPixels * sizeof(GLuint)); glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_TEXTURE_BUFFER, _fragmentBuffer); glBufferData( GL_TEXTURE_BUFFER, MaxAverageLayers*totalPixels * sizeof(GLuint) * 4, nullptr, GL_DYNAMIC_COPY ); glBindTexture(GL_TEXTURE_BUFFER, _fragmentTexture); glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32UI, _fragmentBuffer); glBindTexture(GL_TEXTURE_BUFFER, 0); glBindImageTexture(1, _fragmentTexture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32UI); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainColorTexture); glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, _nAaSamples, GL_RGBA, _resolution.x, _resolution.y, true ); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, _mainDepthTexture); glTexImage2DMultisample( GL_TEXTURE_2D_MULTISAMPLE, _nAaSamples, GL_DEPTH_COMPONENT32F, _resolution.x, _resolution.y, true ); _dirtyResolution = false; } void ABufferRenderer::updateResolveDictionary() { ghoul::Dictionary dict; ghoul::Dictionary raycastersDict; for (const auto& raycastPair : _raycastData) { ghoul::Dictionary innerDict; int id = raycastPair.second.id; std::string namespaceName = raycastPair.second.namespaceName; std::string raycastPath = raycastPair.first->getRaycastPath(); innerDict.setValue("id", id); innerDict.setValue("namespace", namespaceName); innerDict.setValue("bitmask", 1 << id); innerDict.setValue("raycastPath", raycastPath); raycastersDict.setValue(std::to_string(id), innerDict); } dict.setValue("raycasters", raycastersDict); ghoul::Dictionary helperPathsDict; for (size_t i = 0; i < _helperPaths.size(); ++i) { helperPathsDict.setValue(std::to_string(i), _helperPaths[i]); } dict.setValue("helperPaths", helperPathsDict); dict.setValue("raycastingEnabled", _raycastData.size() > 0); dict.setValue("storeSorted", true); dict.setValue("nRaycasters", static_cast(_raycastData.size())); _resolveDictionary = dict; OsEng.renderEngine().setResolveData(dict); _dirtyResolveDictionary = false; } void ABufferRenderer::updateRaycastData() { PerfMeasure("ABufferRenderer::updateRaycastData"); _raycastData.clear(); _boundsPrograms.clear(); _helperPaths.clear(); const std::vector& raycasters = OsEng.renderEngine().raycasterManager().raycasters(); std::map namespaceIndices; // raycaster ids are positive integers starting at 0. (for raycasters, // fragment type is id+1) int nextId = 0; int nextNamespaceIndex = 0; for (auto &raycaster : raycasters) { if (nextId > MaxRaycasters) { int nIgnored = MaxRaycasters - static_cast(raycasters.size()); LWARNING(fmt::format( "ABufferRenderer does not support more than 32 raycasters. " "Ignoring {} raycasters", nIgnored )); break; } RaycastData data; data.id = nextId++; std::string helperPath = raycaster->getHelperPath(); // Each new helper path generates a new namespace, // to avoid glsl name collisions between raycaster implementaitons. // Assign a new namespace or find an already created index. if (helperPath == "") { data.namespaceName = "NAMESPACE_" + std::to_string(nextNamespaceIndex++); } else { auto iter = namespaceIndices.find(helperPath); if (iter == namespaceIndices.end()) { int namespaceIndex = nextNamespaceIndex++; data.namespaceName = std::to_string(namespaceIndex); namespaceIndices[helperPath] = namespaceIndex; _helperPaths.push_back(helperPath); } else { data.namespaceName = "NAMESPACE_" + std::to_string(iter->second); } } _raycastData[raycaster] = data; std::string vsPath = raycaster->getBoundsVsPath(); std::string fsPath = raycaster->getBoundsFsPath(); ghoul::Dictionary dict; // set path to the current renderer's main fragment shader dict.setValue("rendererData", _rendererData); // parameterize the main fragment shader program with specific contents. // fsPath should point to a shader file defining a Fragment getFragment() function // instead of a void main() setting glFragColor, glFragDepth, etc. dict.setValue("fragmentPath", fsPath); dict.setValue("fragmentType", data.id + 1); try { _boundsPrograms[raycaster] = ghoul::opengl::ProgramObject::Build( "Volume " + std::to_string(data.id) + " bounds", vsPath, BoundsFragmentShaderPath, dict ); } catch (ghoul::RuntimeError& error) { LERRORC(error.component, error.message); } } _dirtyRaycastData = false; _dirtyResolveDictionary = true; } void ABufferRenderer::updateRendererData() { PerfMeasure("ABufferRenderer::updateRendererData"); ghoul::Dictionary dict; dict.setValue("fragmentRendererPath", std::string(RenderFragmentShaderPath)); dict.setValue("maxLayers", MaxLayers); dict.setValue("maxTotalFragments", MaxLayers * _resolution.x * _resolution.y); _rendererData = dict; OsEng.renderEngine().setRendererData(dict); _dirtyRendererData = false; } void ABufferRenderer::saveTextureToMemory(const GLenum color_buffer_attachment, const int width, const int height, std::vector& memory) const { if (!memory.empty()) { memory.clear(); } memory.reserve(width * height * 3); std::vector tmpMemory(width * height * 3); if (color_buffer_attachment != GL_DEPTH_ATTACHMENT) { glReadBuffer(color_buffer_attachment); glReadPixels(0, 0, width, height, GL_RGB, GL_FLOAT, tmpMemory.data()); } else { glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, tmpMemory.data()); } for (auto i = 0; i < width*height * 3; ++i) { memory[i] = static_cast(tmpMemory[i]); } } } // namespace openspace