diff --git a/data/assets/scene/solarsystem/planets/mars/atmosphere.asset b/data/assets/scene/solarsystem/planets/mars/atmosphere.asset index a3f30d278d..e76b1afd57 100644 --- a/data/assets/scene/solarsystem/planets/mars/atmosphere.asset +++ b/data/assets/scene/solarsystem/planets/mars/atmosphere.asset @@ -42,7 +42,6 @@ local Atmosphere = { G = 0.85 }, Debug = { - -- PreCalculatedTextureScale is a float from 1.0 to N, with N > 0.0 and N in Naturals (i.e., 1, 2, 3, 4, 5....) PreCalculatedTextureScale = 1.0, SaveCalculatedTextures = false } diff --git a/data/assets/scene/solarsystem/planets/venus/atmosphere.asset b/data/assets/scene/solarsystem/planets/venus/atmosphere.asset index a8d28ff7e4..7e39382692 100644 --- a/data/assets/scene/solarsystem/planets/venus/atmosphere.asset +++ b/data/assets/scene/solarsystem/planets/venus/atmosphere.asset @@ -42,7 +42,6 @@ local Atmosphere = { G = 0.85 }, Debug = { - -- PreCalculatedTextureScale is a float from 1.0 to N, with N > 0.0 and N in Naturals (i.e., 1, 2, 3, 4, 5....) PreCalculatedTextureScale = 1.0, SaveCalculatedTextures = false } diff --git a/include/openspace/rendering/deferredcaster.h b/include/openspace/rendering/deferredcaster.h index 3d9f476848..c0b2f12d1e 100644 --- a/include/openspace/rendering/deferredcaster.h +++ b/include/openspace/rendering/deferredcaster.h @@ -51,8 +51,6 @@ public: const DeferredcastData& /*deferredData*/, ghoul::opengl::ProgramObject& /*program*/) {}; - virtual std::filesystem::path deferredcastPath() const = 0; - virtual std::filesystem::path deferredcastVSPath() const = 0; virtual std::filesystem::path deferredcastFSPath() const = 0; diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp index 1cf0fc71ce..90b8cb0f83 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.cpp +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.cpp @@ -83,57 +83,39 @@ namespace { constexpr const float ATM_EPS = 2000.f; constexpr const float KM_TO_M = 1000.f; - void createRenderQuad(GLuint* vao, GLuint* vbo, GLfloat size) { + void createRenderQuad(GLuint* vao, GLuint* vbo) { glGenVertexArrays(1, vao); - glGenBuffers(1, vbo); glBindVertexArray(*vao); + glGenBuffers(1, vbo); glBindBuffer(GL_ARRAY_BUFFER, *vbo); const GLfloat VertexData[] = { - // x y z w - -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 + // x y z + -1.f, -1.f, + 1.f, 1.f, + -1.f, 1.f, + -1.f, -1.f, + 1.f, -1.f, + 1.f, 1.f, }; glBufferData(GL_ARRAY_BUFFER, sizeof(VertexData), VertexData, GL_STATIC_DRAW); - glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), nullptr); glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), nullptr); glBindVertexArray(0); } - void saveTextureFile(GLenum colorBufferAttachment, - const std::filesystem::path& fileName, const glm::ivec2& size) - { - std::fstream ppmFile; - ppmFile.open(fileName, std::fstream::out); + template + void saveTextureFile(const std::filesystem::path& fileName, const glm::ivec2& size) { + std::ofstream ppmFile(fileName); if (!ppmFile.is_open()) { return; } - std::vector px( - size.x * size.y * 3, - static_cast(255) - ); + std::vector px(size.x * size.y * 3, unsigned char(255)); - if (colorBufferAttachment != GL_DEPTH_ATTACHMENT) { - glReadBuffer(colorBufferAttachment); - glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, px.data()); - } - else { - glReadPixels( - 0, - 0, - size.x, - size.y, - GL_DEPTH_COMPONENT, - GL_UNSIGNED_BYTE, - px.data() - ); - } + glReadBuffer(colorBufferAttachment); + glReadPixels(0, 0, size.x, size.y, GL_RGB, GL_UNSIGNED_BYTE, px.data()); ppmFile << "P3" << '\n' << size.x << " " << size.y << '\n' << "255" << '\n'; @@ -208,63 +190,119 @@ namespace { return true; } - void renderQuadForCalc(GLuint vao, GLsizei numberOfVertices) { + void renderQuadForCalc(GLuint vao) { glBindVertexArray(vao); - glDrawArrays(GL_TRIANGLES, 0, numberOfVertices); + glDrawArrays(GL_TRIANGLES, 0, 6); glBindVertexArray(0); } + + GLuint createTexture(const glm::ivec2& size, std::string_view name) { + GLuint t; + glGenTextures(1, &t); + glBindTexture(GL_TEXTURE_2D, t); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGB32F, + size.x, + size.y, + 0, + GL_RGB, + GL_FLOAT, + nullptr + ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, t, static_cast(name.size()), name.data()); + } + return t; + } + + GLuint createTexture(const glm::ivec3& size, std::string_view name, int components) { + ghoul_assert(components == 3 || components == 4, "Only 3-4 components supported"); + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_3D, texture); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + // Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glTexImage3D( + GL_TEXTURE_3D, + 0, + (components == 3) ? GL_RGB32F : GL_RGBA32F, + size.x, + size.y, + size.z, + 0, + GL_RGB, + GL_FLOAT, + nullptr + ); + if (glbinding::Binding::ObjectLabel.isResolved()) { + glObjectLabel(GL_TEXTURE, texture, name.size(), name.data()); + } + return texture; + } } // namespace namespace openspace { +AtmosphereDeferredcaster::AtmosphereDeferredcaster(float textureScale, + std::vector shadowConfigArray, + bool saveCalculatedTextures) + : _transmittanceTableSize(glm::ivec2(256 * textureScale, 64 * textureScale) ) + , _irradianceTableSize(glm::ivec2(64 * textureScale, 16 * textureScale)) + , _deltaETableSize(glm::ivec2(64 * textureScale, 16 * textureScale)) + , _muSSamples(static_cast(32 * textureScale)) + , _nuSamples(static_cast(8 * textureScale)) + , _muSamples(static_cast(128 * textureScale)) + , _rSamples(static_cast(32 * textureScale)) + , _shadowConfArray(std::move(shadowConfigArray)) + , _saveCalculationTextures(saveCalculatedTextures) + , _textureSize(_muSSamples * _nuSamples, _muSamples, _rSamples) +{ + std::memset(_uniformNameBuffer, '\0', sizeof(_uniformNameBuffer)); + std::strcpy(_uniformNameBuffer, "shadowDataArray["); + _shadowDataArrayCache.reserve(_shadowConfArray.size()); +} + void AtmosphereDeferredcaster::initialize() { ZoneScoped - if (!_atmosphereCalculated) { - preCalculateAtmosphereParam(); - } - - std::memset(_uniformNameBuffer, 0, sizeof(_uniformNameBuffer)); - std::strcpy(_uniformNameBuffer, "shadowDataArray["); + _transmittanceTableTexture = createTexture(_transmittanceTableSize, "Transmittance"); + _irradianceTableTexture = createTexture(_irradianceTableSize, "Irradiance"); + _inScatteringTableTexture = createTexture(_textureSize, "InScattering", 4); + calculateAtmosphereParameters(); } void AtmosphereDeferredcaster::deinitialize() { ZoneScoped - _transmittanceProgramObject = nullptr; - _irradianceProgramObject = nullptr; - _irradianceSupTermsProgramObject = nullptr; - _inScatteringProgramObject = nullptr; - _inScatteringSupTermsProgramObject = nullptr; - _deltaEProgramObject = nullptr; - _deltaSProgramObject = nullptr; - _deltaSSupTermsProgramObject = nullptr; - _deltaJProgramObject = nullptr; - glDeleteTextures(1, &_transmittanceTableTexture); glDeleteTextures(1, &_irradianceTableTexture); glDeleteTextures(1, &_inScatteringTableTexture); - glDeleteTextures(1, &_deltaETableTexture); - glDeleteTextures(1, &_deltaSRayleighTableTexture); - glDeleteTextures(1, &_deltaSMieTableTexture); - glDeleteTextures(1, &_deltaJTableTexture); } -void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData, - const DeferredcastData&, - ghoul::opengl::ProgramObject& program) +void AtmosphereDeferredcaster::update(const UpdateData&) {} + +void AtmosphereDeferredcaster::preRaycast(const RenderData& data, const DeferredcastData&, + ghoul::opengl::ProgramObject& prg) { ZoneScoped // Atmosphere Frustum Culling - glm::dvec3 tPlanetPosWorld = glm::dvec3( - _modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0) - ); - - const double distance = glm::distance( - tPlanetPosWorld, - renderData.camera.eyePositionVec3() - ); + glm::dvec3 tPlanetPos = glm::dvec3(_modelTransform * glm::dvec4(0.0, 0.0, 0.0, 1.0)); + const double distance = glm::distance(tPlanetPos, data.camera.eyePositionVec3()); // Radius is in KM const double scaledRadius = glm::length( @@ -272,232 +310,180 @@ void AtmosphereDeferredcaster::preRaycast(const RenderData& renderData, ); // Number of planet radii to use as distance threshold for culling - const double DISTANCE_CULLING_RADII = 5000; - if (distance > scaledRadius * DISTANCE_CULLING_RADII) { - program.setUniform(_uniformCache.cullAtmosphere, 1); - } - else { - glm::dmat4 MV = glm::dmat4(renderData.camera.sgctInternal.projectionMatrix()) * - renderData.camera.combinedViewMatrix(); + prg.setUniform(_uniformCache.cullAtmosphere, 1); - const double totalAtmosphere = (scaledRadius + ATM_EPS); - if (!isAtmosphereInFrustum(MV, tPlanetPosWorld, totalAtmosphere)) { - program.setUniform(_uniformCache.cullAtmosphere, 1); + constexpr const double DistanceCullingRadii = 5000; + glm::dmat4 MV = glm::dmat4(data.camera.sgctInternal.projectionMatrix()) * + data.camera.combinedViewMatrix(); + if (distance <= scaledRadius * DistanceCullingRadii && + isAtmosphereInFrustum(MV, tPlanetPos, scaledRadius + ATM_EPS)) + { + prg.setUniform(_uniformCache.cullAtmosphere, 0); + prg.setUniform(_uniformCache.Rg, _atmospherePlanetRadius); + prg.setUniform(_uniformCache.Rt, _atmosphereRadius); + prg.setUniform(_uniformCache.groundRadianceEmission, _groundRadianceEmission); + prg.setUniform(_uniformCache.HR, _rayleighHeightScale); + prg.setUniform(_uniformCache.betaRayleigh, _rayleighScatteringCoeff); + prg.setUniform(_uniformCache.HM, _mieHeightScale); + prg.setUniform(_uniformCache.betaMieExtinction, _mieExtinctionCoeff); + prg.setUniform(_uniformCache.mieG, _miePhaseConstant); + prg.setUniform(_uniformCache.sunRadiance, _sunRadianceIntensity); + prg.setUniform(_uniformCache.ozoneLayerEnabled, _ozoneEnabled); + prg.setUniform(_uniformCache.HO, _ozoneHeightScale); + prg.setUniform(_uniformCache.betaOzoneExtinction, _ozoneExtinctionCoeff); + prg.setUniform(_uniformCache.SAMPLES_R, _rSamples); + prg.setUniform(_uniformCache.SAMPLES_MU, _muSamples); + prg.setUniform(_uniformCache.SAMPLES_MU_S, _muSSamples); + prg.setUniform(_uniformCache.SAMPLES_NU, _nuSamples); + + // Object Space + glm::dmat4 invModelMatrix = glm::inverse(_modelTransform); + prg.setUniform(_uniformCache.inverseModelTransformMatrix, invModelMatrix); + prg.setUniform(_uniformCache.modelTransformMatrix, _modelTransform); + + glm::dmat4 viewToWorldMatrix = glm::inverse(data.camera.combinedViewMatrix()); + + // Eye Space to World Space + prg.setUniform(_uniformCache.viewToWorldMatrix, viewToWorldMatrix); + + // Projection to Eye Space + glm::dmat4 dInvProj = glm::inverse(glm::dmat4(data.camera.projectionMatrix())); + + glm::dmat4 invWholePipeline = invModelMatrix * viewToWorldMatrix * dInvProj; + + prg.setUniform(_uniformCache.projectionToModelTransform, invWholePipeline); + + glm::dvec4 camPosObjCoords = + invModelMatrix * glm::dvec4(data.camera.eyePositionVec3(), 1.0); + prg.setUniform(_uniformCache.camPosObj, glm::dvec3(camPosObjCoords)); + + SceneGraphNode* node = sceneGraph()->sceneGraphNode("Sun"); + glm::dvec3 sunPosWorld = node ? node->worldPosition() : glm::dvec3(0.0); + + glm::dvec3 sunPosObj; + // Sun following camera position + if (_sunFollowingCameraEnabled) { + sunPosObj = invModelMatrix * glm::dvec4(data.camera.eyePositionVec3(), 1.0); } else { - program.setUniform(_uniformCache.cullAtmosphere, 0); - program.setUniform(_uniformCache.Rg, _atmospherePlanetRadius); - program.setUniform(_uniformCache.Rt, _atmosphereRadius); - program.setUniform( - _uniformCache.groundRadianceEmission, - _planetGroundRadianceEmission - ); - program.setUniform(_uniformCache.HR, _rayleighHeightScale); - program.setUniform(_uniformCache.betaRayleigh, _rayleighScatteringCoeff); - program.setUniform(_uniformCache.HM, _mieHeightScale); - program.setUniform(_uniformCache.betaMieExtinction, _mieExtinctionCoeff); - program.setUniform(_uniformCache.mieG, _miePhaseConstant); - program.setUniform(_uniformCache.sunRadiance, _sunRadianceIntensity); - program.setUniform(_uniformCache.ozoneLayerEnabled, _ozoneEnabled); - program.setUniform(_uniformCache.HO, _ozoneHeightScale); - program.setUniform(_uniformCache.betaOzoneExtinction, _ozoneExtinctionCoeff); - program.setUniform(_uniformCache.SAMPLES_R, _r_samples); - program.setUniform(_uniformCache.SAMPLES_MU, _mu_samples); - program.setUniform(_uniformCache.SAMPLES_MU_S, _mu_s_samples); - program.setUniform(_uniformCache.SAMPLES_NU, _nu_samples); - - // Object Space - glm::dmat4 inverseModelMatrix = glm::inverse(_modelTransform); - program.setUniform( - _uniformCache.inverseModelTransformMatrix, inverseModelMatrix - ); - program.setUniform(_uniformCache.modelTransformMatrix, _modelTransform); - - glm::dmat4 viewToWorldMatrix = glm::inverse( - renderData.camera.combinedViewMatrix() - ); - - // Eye Space to World Space - program.setUniform(_uniformCache.viewToWorldMatrix, viewToWorldMatrix); - - // Projection to Eye Space - glm::dmat4 dInverseProjection = glm::inverse( - glm::dmat4(renderData.camera.projectionMatrix()) - ); - - glm::dmat4 inverseWholeMatrixPipeline = - inverseModelMatrix * viewToWorldMatrix * dInverseProjection; - - program.setUniform( - _uniformCache.projectionToModelTransformMatrix, - inverseWholeMatrixPipeline - ); - - glm::dvec4 camPosObjCoords = - inverseModelMatrix * glm::dvec4(renderData.camera.eyePositionVec3(), 1.0); - program.setUniform(_uniformCache.camPosObj, glm::dvec3(camPosObjCoords)); - - SceneGraphNode* node = sceneGraph()->sceneGraphNode("Sun"); - glm::dvec3 sunPosWorld = node ? node->worldPosition() : glm::dvec3(0.0); - - glm::dvec4 sunPosObj; - // Sun following camera position - if (_sunFollowingCameraEnabled) { - sunPosObj = inverseModelMatrix * glm::dvec4( - renderData.camera.eyePositionVec3(), - 1.0 - ); - } - else { - sunPosObj = inverseModelMatrix * - glm::dvec4( - (sunPosWorld - renderData.modelTransform.translation) * 1000.0, - 1.0 - ); - } - - // Sun Position in Object Space - program.setUniform( - _uniformCache.sunDirectionObj, - glm::normalize(glm::dvec3(sunPosObj)) - ); - - ghoul::opengl::updateUniformLocations(program, _uniformCache, UniformNames); - - // Shadow calculations.. - if (!_shadowConfArray.empty()) { - ZoneScopedN("Shadow Configuration") - - _shadowDataArrayCache.clear(); - - for (const ShadowConfiguration& shadowConf : _shadowConfArray) { - // TO REMEMBER: all distances and lengths in world coordinates are in - // meters!!! We need to move this to view space... - // Getting source and caster: - double lt; - glm::dvec3 sourcePos = SpiceManager::ref().targetPosition( - shadowConf.source.first, - "SSB", - "GALACTIC", - {}, - _time, - lt - ); - sourcePos *= KM_TO_M; // converting to meters - glm::dvec3 casterPos = SpiceManager::ref().targetPosition( - shadowConf.caster.first, - "SSB", - "GALACTIC", - {}, - _time, - lt - ); - casterPos *= KM_TO_M; // converting to meters - - SceneGraphNode* sourceNode = sceneGraphNode(shadowConf.source.first); - SceneGraphNode* casterNode = sceneGraphNode(shadowConf.caster.first); - - if ((sourceNode == nullptr) || (casterNode == nullptr)) { - LERRORC( - "AtmosphereDeferredcaster", - "Invalid scenegraph node for the shadow's caster or shadow's " - "receiver" - ); - return; - } - - const double sourceRadiusScale = std::max( - glm::compMax(sourceNode->scale()), - 1.0 - ); - - const double casterRadiusScale = std::max( - glm::compMax(casterNode->scale()), - 1.0 - ); - - // First we determine if the caster is shadowing the current planet - // (all calculations in World Coordinates): - glm::dvec3 planetCasterVec = - casterPos - renderData.modelTransform.translation; - glm::dvec3 sourceCasterVec = casterPos - sourcePos; - double sc_length = glm::length(sourceCasterVec); - glm::dvec3 planetCaster_proj = ( - glm::dot(planetCasterVec, sourceCasterVec) / - (sc_length*sc_length)) * sourceCasterVec; - double d_test = glm::length(planetCasterVec - planetCaster_proj); - double xp_test = shadowConf.caster.second * casterRadiusScale * - sc_length / - (shadowConf.source.second * sourceRadiusScale + - shadowConf.caster.second * casterRadiusScale); - double rp_test = shadowConf.caster.second * casterRadiusScale * - (glm::length(planetCaster_proj) + xp_test) / xp_test; - - double casterDistSun = glm::length(casterPos - sunPosWorld); - double planetDistSun = glm::length( - renderData.modelTransform.translation - sunPosWorld - ); - - ShadowRenderingStruct shadowData; - shadowData.isShadowing = false; - - if (((d_test - rp_test) < (_atmospherePlanetRadius * KM_TO_M)) && - (casterDistSun < planetDistSun)) - { - // The current caster is shadowing the current planet - shadowData.isShadowing = true; - shadowData.rs = shadowConf.source.second * sourceRadiusScale; - shadowData.rc = shadowConf.caster.second * casterRadiusScale; - shadowData.sourceCasterVec = glm::normalize(sourceCasterVec); - shadowData.xp = xp_test; - shadowData.xu = - shadowData.rc * sc_length / (shadowData.rs - shadowData.rc); - shadowData.casterPositionVec = casterPos; - } - _shadowDataArrayCache.push_back(shadowData); - } - - // _uniformNameBuffer[0..15] = "shadowDataArray[" - unsigned int counter = 0; - for (const ShadowRenderingStruct& sd : _shadowDataArrayCache) { - // Add the counter - char* bf = fmt::format_to(_uniformNameBuffer + 16, "{}", counter); - - std::strcpy(bf, "].isShadowing\0"); - program.setUniform(_uniformNameBuffer, sd.isShadowing); - - if (sd.isShadowing) { - std::strcpy(bf, "].xp\0"); - program.setUniform(_uniformNameBuffer, sd.xp); - std::strcpy(bf, "].xu\0"); - program.setUniform(_uniformNameBuffer, sd.xu); - std::strcpy(bf, "].rc\0"); - program.setUniform(_uniformNameBuffer, sd.rc); - std::strcpy(bf, "].sourceCasterVec\0"); - program.setUniform(_uniformNameBuffer, sd.sourceCasterVec); - std::strcpy(bf, "].casterPositionVec\0"); - program.setUniform(_uniformNameBuffer, sd.casterPositionVec); - } - counter++; - } - program.setUniform(_uniformCache.hardShadows, _hardShadowsEnabled); - } + sunPosObj = invModelMatrix * + glm::dvec4((sunPosWorld - data.modelTransform.translation) * 1000.0, 1.0); } + + // Sun Position in Object Space + prg.setUniform(_uniformCache.sunDirectionObj, glm::normalize(sunPosObj)); + + // Shadow calculations.. + _shadowDataArrayCache.clear(); + for (const ShadowConfiguration& shadowConf : _shadowConfArray) { + // TO REMEMBER: all distances and lengths in world coordinates are in + // meters!!! We need to move this to view space... + double lt; + glm::dvec3 sourcePos = SpiceManager::ref().targetPosition( + shadowConf.source.first, + "SSB", + "GALACTIC", + {}, + data.time.j2000Seconds(), + lt + ); + sourcePos *= KM_TO_M; // converting to meters + glm::dvec3 casterPos = SpiceManager::ref().targetPosition( + shadowConf.caster.first, + "SSB", + "GALACTIC", + {}, + data.time.j2000Seconds(), + lt + ); + casterPos *= KM_TO_M; // converting to meters + + SceneGraphNode* sourceNode = sceneGraphNode(shadowConf.source.first); + SceneGraphNode* casterNode = sceneGraphNode(shadowConf.caster.first); + + if (!sourceNode || !casterNode) { + LERROR("Invalid scenegraph node for the shadow's caster or receiver"); + return; + } + + const double sourceScale = std::max(glm::compMax(sourceNode->scale()), 1.0); + const double casterScale = std::max(glm::compMax(casterNode->scale()), 1.0); + + // First we determine if the caster is shadowing the current planet + // (all calculations in World Coordinates): + glm::dvec3 planetCasterVec = casterPos - data.modelTransform.translation; + glm::dvec3 sourceCasterVec = casterPos - sourcePos; + double scLength = glm::length(sourceCasterVec); + glm::dvec3 planetCasterProj = + (glm::dot(planetCasterVec, sourceCasterVec) / (scLength * scLength)) * + sourceCasterVec; + double dTest = glm::length(planetCasterVec - planetCasterProj); + double xpTest = shadowConf.caster.second * casterScale * + scLength / + (shadowConf.source.second * sourceScale + + shadowConf.caster.second * casterScale); + double rpTest = shadowConf.caster.second * casterScale * + (glm::length(planetCasterProj) + xpTest) / xpTest; + + double casterDistSun = glm::length(casterPos - sunPosWorld); + double planetDistSun = glm::length( + data.modelTransform.translation - sunPosWorld + ); + + ShadowRenderingStruct shadow; + shadow.isShadowing = false; + + if (((dTest - rpTest) < (_atmospherePlanetRadius * KM_TO_M)) && + (casterDistSun < planetDistSun)) + { + // The current caster is shadowing the current planet + shadow.isShadowing = true; + shadow.rs = shadowConf.source.second * sourceScale; + shadow.rc = shadowConf.caster.second * casterScale; + shadow.sourceCasterVec = glm::normalize(sourceCasterVec); + shadow.xp = xpTest; + shadow.xu = shadow.rc * scLength / (shadow.rs - shadow.rc); + shadow.casterPositionVec = casterPos; + } + _shadowDataArrayCache.push_back(shadow); + } + + // _uniformNameBuffer[0..15] = "shadowDataArray[" + unsigned int counter = 0; + for (const ShadowRenderingStruct& sd : _shadowDataArrayCache) { + // Add the counter + char* bf = fmt::format_to(_uniformNameBuffer + 16, "{}", counter); + + std::strcpy(bf, "].isShadowing\0"); + prg.setUniform(_uniformNameBuffer, sd.isShadowing); + + if (sd.isShadowing) { + std::strcpy(bf, "].xp\0"); + prg.setUniform(_uniformNameBuffer, sd.xp); + std::strcpy(bf, "].xu\0"); + prg.setUniform(_uniformNameBuffer, sd.xu); + std::strcpy(bf, "].rc\0"); + prg.setUniform(_uniformNameBuffer, sd.rc); + std::strcpy(bf, "].sourceCasterVec\0"); + prg.setUniform(_uniformNameBuffer, sd.sourceCasterVec); + std::strcpy(bf, "].casterPositionVec\0"); + prg.setUniform(_uniformNameBuffer, sd.casterPositionVec); + } + counter++; + } + prg.setUniform(_uniformCache.hardShadows, _hardShadowsEnabled); } _transmittanceTableTextureUnit.activate(); glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - program.setUniform( - _uniformCache.transmittanceTexture, - _transmittanceTableTextureUnit - ); + prg.setUniform(_uniformCache.transmittanceTexture, _transmittanceTableTextureUnit); _irradianceTableTextureUnit.activate(); glBindTexture(GL_TEXTURE_2D, _irradianceTableTexture); - program.setUniform(_uniformCache.irradianceTexture, _irradianceTableTextureUnit); + prg.setUniform(_uniformCache.irradianceTexture, _irradianceTableTextureUnit); _inScatteringTableTextureUnit.activate(); glBindTexture(GL_TEXTURE_3D, _inScatteringTableTexture); - program.setUniform(_uniformCache.inscatterTexture, _inScatteringTableTextureUnit); + prg.setUniform(_uniformCache.inscatterTexture, _inScatteringTableTextureUnit); } void AtmosphereDeferredcaster::postRaycast(const RenderData&, const DeferredcastData&, @@ -511,10 +497,6 @@ void AtmosphereDeferredcaster::postRaycast(const RenderData&, const Deferredcast _inScatteringTableTextureUnit.deactivate(); } -std::filesystem::path AtmosphereDeferredcaster::deferredcastPath() const { - return absPath("${MODULE_ATMOSPHERE}/shaders/atmosphere_deferred_fs.glsl"); -} - std::filesystem::path AtmosphereDeferredcaster::deferredcastFSPath() const { return absPath("${MODULE_ATMOSPHERE}/shaders/atmosphere_deferred_fs.glsl"); } @@ -533,453 +515,44 @@ void AtmosphereDeferredcaster::initializeCachedVariables( ghoul::opengl::updateUniformLocations(program, _uniformCache, UniformNames); } -void AtmosphereDeferredcaster::update(const UpdateData&) {} - void AtmosphereDeferredcaster::setModelTransform(glm::dmat4 transform) { _modelTransform = std::move(transform); } -void AtmosphereDeferredcaster::setTime(double time) { - _time = time; -} - -void AtmosphereDeferredcaster::setAtmosphereRadius(float atmRadius) { - _atmosphereRadius = atmRadius; -} - -void AtmosphereDeferredcaster::setPlanetRadius(float planetRadius) { +void AtmosphereDeferredcaster::setParameters(float atmosphereRadius, float planetRadius, + float averageGroundReflectance, + float groundRadianceEmission, + float rayleighHeightScale, bool enableOzone, + float ozoneHeightScale, float mieHeightScale, + float miePhaseConstant, float sunRadiance, + glm::vec3 rayScatteringCoefficients, + glm::vec3 ozoneExtinctionCoefficients, + glm::vec3 mieScatteringCoefficients, + glm::vec3 mieExtinctionCoefficients, + bool sunFollowing) +{ + _atmosphereRadius = atmosphereRadius; _atmospherePlanetRadius = planetRadius; -} - -void AtmosphereDeferredcaster::setPlanetAverageGroundReflectance( - float averageGReflectance) -{ - _planetAverageGroundReflectance = averageGReflectance; -} - -void AtmosphereDeferredcaster::setPlanetGroundRadianceEmission( - float groundRadianceEmission) -{ - _planetGroundRadianceEmission = groundRadianceEmission; -} - -void AtmosphereDeferredcaster::setRayleighHeightScale(float rayleighHeightScale) { + _averageGroundReflectance = averageGroundReflectance; + _groundRadianceEmission = groundRadianceEmission; _rayleighHeightScale = rayleighHeightScale; -} - -void AtmosphereDeferredcaster::enableOzone(bool enable) { - _ozoneEnabled = enable; -} - -void AtmosphereDeferredcaster::setOzoneHeightScale(float ozoneHeightScale) { + _ozoneEnabled = enableOzone; _ozoneHeightScale = ozoneHeightScale; -} - -void AtmosphereDeferredcaster::setMieHeightScale(float mieHeightScale) { _mieHeightScale = mieHeightScale; -} - -void AtmosphereDeferredcaster::setMiePhaseConstant(float miePhaseConstant) { _miePhaseConstant = miePhaseConstant; -} - -void AtmosphereDeferredcaster::setSunRadianceIntensity(float sunRadiance) { _sunRadianceIntensity = sunRadiance; -} - -void AtmosphereDeferredcaster::setRayleighScatteringCoefficients(glm::vec3 rayScattCoeff) -{ - _rayleighScatteringCoeff = std::move(rayScattCoeff); -} - -void AtmosphereDeferredcaster::setOzoneExtinctionCoefficients(glm::vec3 ozoneExtCoeff) { - _ozoneExtinctionCoeff = std::move(ozoneExtCoeff); -} - -void AtmosphereDeferredcaster::setMieScatteringCoefficients(glm::vec3 mieScattCoeff) { - _mieScatteringCoeff = std::move(mieScattCoeff); -} - -void AtmosphereDeferredcaster::setMieExtinctionCoefficients(glm::vec3 mieExtCoeff) { - _mieExtinctionCoeff = std::move(mieExtCoeff); -} - -void AtmosphereDeferredcaster::setEllipsoidRadii(glm::dvec3 radii) { - _ellipsoidRadii = std::move(radii); + _rayleighScatteringCoeff = std::move(rayScatteringCoefficients); + _ozoneExtinctionCoeff = std::move(ozoneExtinctionCoefficients); + _mieScatteringCoeff = std::move(mieScatteringCoefficients); + _mieExtinctionCoeff = std::move(mieExtinctionCoefficients); + _sunFollowingCameraEnabled = sunFollowing; } void AtmosphereDeferredcaster::setHardShadows(bool enabled) { _hardShadowsEnabled = enabled; } -void AtmosphereDeferredcaster::setShadowConfigArray( - std::vector shadowConfigArray) -{ - _shadowConfArray = std::move(shadowConfigArray); - - _shadowDataArrayCache.clear(); - _shadowDataArrayCache.reserve(_shadowConfArray.size()); -} - -void AtmosphereDeferredcaster::enableSunFollowing(bool enable) { - _sunFollowingCameraEnabled = enable; -} - -void AtmosphereDeferredcaster::setPrecalculationTextureScale( - float preCalculatedTexturesScale) -{ - _transmittanceTableSize *= static_cast(preCalculatedTexturesScale); - _irradianceTableSize *= static_cast(preCalculatedTexturesScale); - _deltaETableSize *= static_cast(preCalculatedTexturesScale); - _r_samples *= static_cast(preCalculatedTexturesScale); - _mu_samples *= static_cast(preCalculatedTexturesScale); - _mu_s_samples *= static_cast(preCalculatedTexturesScale); - _nu_samples *= static_cast(preCalculatedTexturesScale); -} - -void AtmosphereDeferredcaster::enablePrecalculationTexturesSaving() { - _saveCalculationTextures = true; -} - -void AtmosphereDeferredcaster::loadComputationPrograms() { - // - // Transmittance T - if (!_transmittanceProgramObject) { - _transmittanceProgramObject = ghoul::opengl::ProgramObject::Build( - "transmittanceCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/transmittance_calc_fs.glsl") - ); - } - using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _transmittanceProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // Irradiance E - if (!_irradianceProgramObject) { - _irradianceProgramObject = ghoul::opengl::ProgramObject::Build( - "irradianceCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_calc_fs.glsl") - ); - } - _irradianceProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - if (!_irradianceSupTermsProgramObject) { - _irradianceSupTermsProgramObject = ghoul::opengl::ProgramObject::Build( - "irradianceSupTermsCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_sup_calc_fs.glsl") - ); - } - _irradianceSupTermsProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // InScattering S - if (!_inScatteringProgramObject) { - _inScatteringProgramObject = ghoul::opengl::ProgramObject::Build( - "inScatteringCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/inScattering_calc_fs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") - ); - } - _inScatteringProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - if (!_inScatteringSupTermsProgramObject) { - _inScatteringSupTermsProgramObject = ghoul::opengl::ProgramObject::Build( - "inScatteringSupTermsCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/inScattering_sup_calc_fs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") - ); - } - _inScatteringSupTermsProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // Delta E - if (!_deltaEProgramObject) { - _deltaEProgramObject = ghoul::opengl::ProgramObject::Build( - "deltaECalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/deltaE_calc_fs.glsl") - ); - } - _deltaEProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // Irradiance finel E - if (!_irradianceFinalProgramObject) { - _irradianceFinalProgramObject = ghoul::opengl::ProgramObject::Build( - "irradianceEFinalProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_final_fs.glsl") - ); - } - _irradianceFinalProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // Delta S - if (!_deltaSProgramObject) { - _deltaSProgramObject = ghoul::opengl::ProgramObject::Build( - "deltaSCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/deltaS_calc_fs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") - ); - } - _deltaSProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - if (!_deltaSSupTermsProgramObject) { - _deltaSSupTermsProgramObject = ghoul::opengl::ProgramObject::Build( - "deltaSSUPTermsCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/deltaS_sup_calc_fs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") - ); - } - _deltaSSupTermsProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); - - // - // Delta J (Radiance Scattered) - if (!_deltaJProgramObject) { - _deltaJProgramObject = ghoul::opengl::ProgramObject::Build( - "deltaJCalcProgram", - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/deltaJ_calc_fs.glsl"), - absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") - ); - } - _deltaJProgramObject->setIgnoreUniformLocationError(IgnoreError::Yes); -} - -void AtmosphereDeferredcaster::unloadComputationPrograms() { - _transmittanceProgramObject = nullptr; - _irradianceProgramObject = nullptr; - _irradianceSupTermsProgramObject = nullptr; - _inScatteringProgramObject = nullptr; - _inScatteringSupTermsProgramObject = nullptr; - _deltaEProgramObject = nullptr; - _irradianceFinalProgramObject = nullptr; - _deltaSProgramObject = nullptr; - _deltaSSupTermsProgramObject = nullptr; - _deltaJProgramObject = nullptr; -} - -void AtmosphereDeferredcaster::createComputationTextures() { - if (!_atmosphereCalculated) { - // - // Transmittance - ghoul::opengl::TextureUnit transmittanceTableTextureUnit; - transmittanceTableTextureUnit.activate(); - glGenTextures(1, &_transmittanceTableTexture); - glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // Stopped using a buffer object for GL_PIXEL_UNPACK_BUFFER - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGB32F, - _transmittanceTableSize.x, - _transmittanceTableSize.y, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); - - // - // Irradiance - ghoul::opengl::TextureUnit irradianceTableTextureUnit; - irradianceTableTextureUnit.activate(); - glGenTextures(1, &_irradianceTableTexture); - glBindTexture(GL_TEXTURE_2D, _irradianceTableTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGB32F, - _irradianceTableSize.x, - _irradianceTableSize.y, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); - - // - // InScattering - ghoul::opengl::TextureUnit inScatteringTableTextureUnit; - inScatteringTableTextureUnit.activate(); - glGenTextures(1, &_inScatteringTableTexture); - glBindTexture(GL_TEXTURE_3D, _inScatteringTableTexture); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage3D( - GL_TEXTURE_3D, - 0, - GL_RGBA32F, - _mu_s_samples * _nu_samples, - _mu_samples, - _r_samples, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); - } - - // - // Delta E - ghoul::opengl::TextureUnit deltaETableTextureUnit; - deltaETableTextureUnit.activate(); - glGenTextures(1, &_deltaETableTexture); - glBindTexture(GL_TEXTURE_2D, _deltaETableTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RGB32F, - _deltaETableSize.x, - _deltaETableSize.y, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); - - // - // Delta S - ghoul::opengl::TextureUnit deltaSRayleighTableTextureUnit; - deltaSRayleighTableTextureUnit.activate(); - glGenTextures(1, &_deltaSRayleighTableTexture); - glBindTexture(GL_TEXTURE_3D, _deltaSRayleighTableTexture); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage3D( - GL_TEXTURE_3D, - 0, - GL_RGB32F, - _mu_s_samples * _nu_samples, - _mu_samples, - _r_samples, - 0, GL_RGB, - GL_FLOAT, - nullptr - ); - - ghoul::opengl::TextureUnit deltaSMieTableTextureUnit; - deltaSMieTableTextureUnit.activate(); - glGenTextures(1, &_deltaSMieTableTexture); - glBindTexture(GL_TEXTURE_3D, _deltaSMieTableTexture); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage3D( - GL_TEXTURE_3D, - 0, - GL_RGB32F, - _mu_s_samples * _nu_samples, - _mu_samples, - _r_samples, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); - - // - // Delta J (Radiance Scattered) - ghoul::opengl::TextureUnit deltaJTableTextureUnit; - deltaJTableTextureUnit.activate(); - glGenTextures(1, &_deltaJTableTexture); - glBindTexture(GL_TEXTURE_3D, _deltaJTableTexture); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glTexImage3D( - GL_TEXTURE_3D, - 0, - GL_RGB32F, - _mu_s_samples * _nu_samples, - _mu_samples, - _r_samples, - 0, - GL_RGB, - GL_FLOAT, - nullptr - ); -} - -void AtmosphereDeferredcaster::deleteComputationTextures() { - glDeleteTextures(1, &_transmittanceTableTexture); - _transmittanceTableTexture = 0; - glDeleteTextures(1, &_irradianceTableTexture); - _irradianceTableTexture = 0; - glDeleteTextures(1, &_inScatteringTableTexture); - _inScatteringTableTexture = 0; - glDeleteTextures(1, &_deltaETableTexture); - _deltaETableTexture = 0; - glDeleteTextures(1, &_deltaSRayleighTableTexture); - _deltaSRayleighTableTexture = 0; - glDeleteTextures(1, &_deltaSMieTableTexture); - _deltaSMieTableTexture = 0; - glDeleteTextures(1, &_deltaJTableTexture); - _deltaJTableTexture = 0; -} - -void AtmosphereDeferredcaster::deleteUnusedComputationTextures() { - glDeleteTextures(1, &_deltaETableTexture); - _deltaETableTexture = 0; - glDeleteTextures(1, &_deltaSRayleighTableTexture); - _deltaSRayleighTableTexture = 0; - glDeleteTextures(1, &_deltaSMieTableTexture); - _deltaSMieTableTexture = 0; - glDeleteTextures(1, &_deltaJTableTexture); - _deltaJTableTexture = 0; -} - -void AtmosphereDeferredcaster::executeCalculations(GLuint quadCalcVAO, - GLenum drawBuffers[1], - GLsizei vertexSize) -{ - ghoul::opengl::TextureUnit transmittanceTableTextureUnit; - ghoul::opengl::TextureUnit irradianceTableTextureUnit; - ghoul::opengl::TextureUnit inScatteringTableTextureUnit; - ghoul::opengl::TextureUnit deltaETableTextureUnit; - ghoul::opengl::TextureUnit deltaSRayleighTableTextureUnit; - ghoul::opengl::TextureUnit deltaSMieTableTextureUnit; - ghoul::opengl::TextureUnit deltaJTableTextureUnit; - - glDisable(GL_BLEND); - - // See Precomputed Atmosphere Scattering from Bruneton et al. paper, algorithm 4.1: +void AtmosphereDeferredcaster::calculateTransmittance(GLuint vao) { glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -987,90 +560,113 @@ void AtmosphereDeferredcaster::executeCalculations(GLuint quadCalcVAO, 0 ); glViewport(0, 0, _transmittanceTableSize.x, _transmittanceTableSize.y); - _transmittanceProgramObject->activate(); - loadAtmosphereDataIntoShaderProgram(*_transmittanceProgramObject); + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr program = ProgramObject::Build( + "Transmittance Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/transmittance_calc_fs.glsl") + ); + program->activate(); + program->setUniform("Rg", _atmospherePlanetRadius); + program->setUniform("Rt", _atmosphereRadius); + program->setUniform("HR", _rayleighHeightScale); + program->setUniform("betaRayleigh", _rayleighScatteringCoeff); + program->setUniform("HM", _mieHeightScale); + program->setUniform("betaMieExtinction", _mieExtinctionCoeff); + program->setUniform("TRANSMITTANCE", _transmittanceTableSize); + program->setUniform("ozoneLayerEnabled", _ozoneEnabled); + program->setUniform("HO", _ozoneHeightScale); + program->setUniform("betaOzoneExtinction", _ozoneExtinctionCoeff); - static const float Black[] = { 0.f, 0.f, 0.f, 0.f }; + constexpr const float Black[] = { 0.f, 0.f, 0.f, 0.f }; glClearBufferfv(GL_COLOR, 0, Black); - renderQuadForCalc(quadCalcVAO, vertexSize); + renderQuadForCalc(vao); if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - "transmittance_texture.ppm", - _transmittanceTableSize - ); + saveTextureFile("transmittance_texture.ppm", _transmittanceTableSize); } - _transmittanceProgramObject->deactivate(); + program->deactivate(); +} - // line 2 in algorithm 4.1 - glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _deltaETableTexture, 0); +GLuint AtmosphereDeferredcaster::calculateDeltaE(GLuint vao) { + GLuint deltaE = createTexture(_deltaETableSize, "DeltaE"); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0); glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); - _irradianceProgramObject->activate(); - transmittanceTableTextureUnit.activate(); + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr program = ProgramObject::Build( + "Irradiance Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_calc_fs.glsl") + ); + program->activate(); + ghoul::opengl::TextureUnit unit; + unit.activate(); glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - _irradianceProgramObject->setUniform( - "transmittanceTexture", - transmittanceTableTextureUnit - ); - loadAtmosphereDataIntoShaderProgram(*_irradianceProgramObject); + program->setUniform("transmittanceTexture", unit); + program->setUniform("Rg", _atmospherePlanetRadius); + program->setUniform("Rt", _atmosphereRadius); + program->setUniform("OTHER_TEXTURES", _deltaETableSize); glClear(GL_COLOR_BUFFER_BIT); - renderQuadForCalc(quadCalcVAO, vertexSize); + renderQuadForCalc(vao); if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - "deltaE_table_texture.ppm", - _deltaETableSize - ); + saveTextureFile("deltaE_table_texture.ppm", _deltaETableSize); } - _irradianceProgramObject->deactivate(); + program->deactivate(); + return deltaE; +} - // line 3 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _deltaSRayleighTableTexture, - 0 - ); - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT1, - _deltaSMieTableTexture, - 0 - ); +std::pair AtmosphereDeferredcaster::calculateDeltaS(GLuint vao) { + GLuint deltaSRayleigh = createTexture(_textureSize, "DeltaS Rayleigh", 3); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0); + GLuint deltaSMie = createTexture(_textureSize, "DeltaS Mie", 3); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, deltaSMie, 0); GLenum colorBuffers[2] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 }; glDrawBuffers(2, colorBuffers); - glViewport(0, 0, _mu_s_samples * _nu_samples, _mu_samples); - _inScatteringProgramObject->activate(); - transmittanceTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - _inScatteringProgramObject->setUniform( - "transmittanceTexture", - transmittanceTableTextureUnit + glViewport(0, 0, _textureSize.x, _textureSize.y); + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr program = ProgramObject::Build( + "InScattering Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/inScattering_calc_fs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") ); - loadAtmosphereDataIntoShaderProgram(*_inScatteringProgramObject); + program->activate(); + ghoul::opengl::TextureUnit unit; + unit.activate(); + glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); + program->setUniform("transmittanceTexture", unit); + program->setUniform("Rg", _atmospherePlanetRadius); + program->setUniform("Rt", _atmosphereRadius); + program->setUniform("HR", _rayleighHeightScale); + program->setUniform("betaRayleigh", _rayleighScatteringCoeff); + program->setUniform("HM", _mieHeightScale); + program->setUniform("betaMieScattering", _mieScatteringCoeff); + program->setUniform("SAMPLES_MU_S", _muSSamples); + program->setUniform("SAMPLES_NU", _nuSamples); + program->setUniform("SAMPLES_MU", _muSamples); + program->setUniform("ozoneLayerEnabled", _ozoneEnabled); + program->setUniform("HO", _ozoneHeightScale); glClear(GL_COLOR_BUFFER_BIT); - for (int layer = 0; layer < _r_samples; ++layer) { - step3DTexture(*_inScatteringProgramObject, layer, true); - renderQuadForCalc(quadCalcVAO, vertexSize); + for (int layer = 0; layer < _rSamples; ++layer) { + program->setUniform("layer", layer); + step3DTexture(*program, layer); + renderQuadForCalc(vao); } if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - "deltaS_rayleigh_texture.ppm", - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) - ); - saveTextureFile( - GL_COLOR_ATTACHMENT1, + saveTextureFile("deltaS_rayleigh_texture.ppm", glm::ivec2(_textureSize)); + saveTextureFile( "deltaS_mie_texture.ppm", - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) + glm::ivec2(_textureSize) ); } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, 0, 0); + GLenum drawBuffers[1] = { GL_COLOR_ATTACHMENT0 }; glDrawBuffers(1, drawBuffers); - _inScatteringProgramObject->deactivate(); + program->deactivate(); + return { deltaSRayleigh, deltaSMie }; +} - // line 4 in algorithm 4.1 +void AtmosphereDeferredcaster::calculateIrradiance(GLuint quadCalcVao) { glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, @@ -1080,251 +676,292 @@ void AtmosphereDeferredcaster::executeCalculations(GLuint quadCalcVAO, glDrawBuffer(GL_COLOR_ATTACHMENT0); glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); - _deltaEProgramObject->activate(); - deltaETableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _deltaETableTexture); - _deltaEProgramObject->setUniform("deltaETexture", deltaETableTextureUnit); - loadAtmosphereDataIntoShaderProgram(*_deltaEProgramObject); + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr program = ProgramObject::Build( + "DeltaE Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/deltaE_calc_fs.glsl") + ); + program->activate(); glClear(GL_COLOR_BUFFER_BIT); - renderQuadForCalc(quadCalcVAO, vertexSize); + renderQuadForCalc(quadCalcVao); if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - "irradiance_texture.ppm", - _deltaETableSize - ); + saveTextureFile("irradiance_texture.ppm", _deltaETableSize); } - _deltaEProgramObject->deactivate(); + program->deactivate(); +} - // line 5 in algorithm 4.1 +void AtmosphereDeferredcaster::calculateInscattering(GLuint vao, GLuint deltaSRayleigh, + GLuint deltaSMie) +{ glFramebufferTexture( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _inScatteringTableTexture, 0 ); - glViewport(0, 0, _mu_s_samples * _nu_samples, _mu_samples); - _deltaSProgramObject->activate(); - deltaSRayleighTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSRayleighTableTexture); - deltaSMieTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSMieTableTexture); - _deltaSProgramObject->setUniform("deltaSRTexture", deltaSRayleighTableTextureUnit); - _deltaSProgramObject->setUniform("deltaSMTexture", deltaSMieTableTextureUnit); - loadAtmosphereDataIntoShaderProgram(*_deltaSProgramObject); + glViewport(0, 0, _textureSize.x, _textureSize.y); + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr program = ProgramObject::Build( + "deltaSCalcProgram", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/deltaS_calc_fs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") + ); + program->activate(); + + ghoul::opengl::TextureUnit deltaSRayleighUnit; + deltaSRayleighUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSRayleigh); + program->setUniform("deltaSRTexture", deltaSRayleighUnit); + + ghoul::opengl::TextureUnit deltaSMieUnit; + deltaSMieUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSMie); + program->setUniform("deltaSMTexture", deltaSMieUnit); + + program->setUniform("SAMPLES_MU_S", _muSSamples); + program->setUniform("SAMPLES_NU", _nuSamples); + program->setUniform("SAMPLES_MU", _muSamples); + program->setUniform("SAMPLES_R", _rSamples); glClear(GL_COLOR_BUFFER_BIT); - for (int layer = 0; layer < _r_samples; ++layer) { - step3DTexture(*_deltaSProgramObject, layer, false); - renderQuadForCalc(quadCalcVAO, vertexSize); + for (int layer = 0; layer < _rSamples; ++layer) { + program->setUniform("layer", layer); + renderQuadForCalc(vao); + } + if (_saveCalculationTextures) { + saveTextureFile("S_texture.ppm", glm::ivec2(_textureSize)); + } + program->deactivate(); +} + +void AtmosphereDeferredcaster::calculateDeltaJ(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, + GLuint deltaJ, GLuint deltaE, + GLuint deltaSRayleigh, GLuint deltaSMie) +{ + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaJ, 0); + glViewport(0, 0, _textureSize.x, _textureSize.y); + program.activate(); + + ghoul::opengl::TextureUnit transmittanceUnit; + transmittanceUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); + program.setUniform("transmittanceTexture", transmittanceUnit); + + ghoul::opengl::TextureUnit deltaEUnit; + deltaEUnit.activate(); + glBindTexture(GL_TEXTURE_2D, deltaE); + program.setUniform("deltaETexture", deltaEUnit); + + ghoul::opengl::TextureUnit deltaSRayleighUnit; + deltaSRayleighUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSRayleigh); + program.setUniform("deltaSRTexture", deltaSRayleighUnit); + + ghoul::opengl::TextureUnit deltaSMieUnit; + deltaSMieUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSMie); + program.setUniform("deltaSMTexture", deltaSMieUnit); + + program.setUniform("firstIteration", (scatteringOrder == 2) ? 1 : 0); + program.setUniform("Rg", _atmospherePlanetRadius); + program.setUniform("Rt", _atmosphereRadius); + program.setUniform("AverageGroundReflectance", _averageGroundReflectance); + program.setUniform("HR", _rayleighHeightScale); + program.setUniform("betaRayleigh", _rayleighScatteringCoeff); + program.setUniform("HM", _mieHeightScale); + program.setUniform("betaMieScattering", _mieScatteringCoeff); + program.setUniform("mieG", _miePhaseConstant); + program.setUniform("SAMPLES_MU_S", _muSSamples); + program.setUniform("SAMPLES_NU", _nuSamples); + program.setUniform("SAMPLES_MU", _muSamples); + program.setUniform("SAMPLES_R", _rSamples); + for (int layer = 0; layer < _rSamples; ++layer) { + program.setUniform("layer", layer); + step3DTexture(program, layer); + renderQuadForCalc(vao); } if (_saveCalculationTextures) { saveTextureFile( - GL_COLOR_ATTACHMENT0, - "S_texture.ppm", - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) + fmt::format("deltaJ_texture-scattering_order-{}.ppm", scatteringOrder), + glm::ivec2(_textureSize) ); } - _deltaSProgramObject->deactivate(); - - // loop in line 6 in algorithm 4.1 - for (int scatteringOrder = 2; scatteringOrder <= 4; ++scatteringOrder) { - // line 7 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _deltaJTableTexture, - 0 - ); - glViewport(0, 0, _mu_s_samples * _nu_samples, _mu_samples); - _deltaJProgramObject->activate(); - if (scatteringOrder == 2) { - _deltaJProgramObject->setUniform("firstIteraction", 1); - } - else { - _deltaJProgramObject->setUniform("firstIteraction", 0); - } - transmittanceTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - _deltaJProgramObject->setUniform( - "transmittanceTexture", - transmittanceTableTextureUnit - ); - deltaETableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _deltaETableTexture); - _deltaJProgramObject->setUniform("deltaETexture", deltaETableTextureUnit); - deltaSRayleighTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSRayleighTableTexture); - _deltaJProgramObject->setUniform( - "deltaSRTexture", - deltaSRayleighTableTextureUnit - ); - deltaSMieTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSMieTableTexture); - _deltaJProgramObject->setUniform("deltaSMTexture", deltaSMieTableTextureUnit); - loadAtmosphereDataIntoShaderProgram(*_deltaJProgramObject); - for (int layer = 0; layer < _r_samples; ++layer) { - step3DTexture(*_deltaJProgramObject, layer, true); - renderQuadForCalc(quadCalcVAO, vertexSize); - } - if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - fmt::format("deltaJ_texture-scattering_order-{}.ppm", scatteringOrder), - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) - ); - } - _deltaJProgramObject->deactivate(); - - // line 8 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _deltaETableTexture, - 0 - ); - glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); - _irradianceSupTermsProgramObject->activate(); - if (scatteringOrder == 2) { - _irradianceSupTermsProgramObject->setUniform("firstIteraction", 1); - } - else { - _irradianceSupTermsProgramObject->setUniform("firstIteraction", 0); - } - transmittanceTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - _irradianceSupTermsProgramObject->setUniform( - "transmittanceTexture", - transmittanceTableTextureUnit - ); - deltaSRayleighTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSRayleighTableTexture); - _irradianceSupTermsProgramObject->setUniform( - "deltaSRTexture", - deltaSRayleighTableTextureUnit - ); - deltaSMieTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSMieTableTexture); - _irradianceSupTermsProgramObject->setUniform( - "deltaSMTexture", - deltaSMieTableTextureUnit - ); - loadAtmosphereDataIntoShaderProgram(*_irradianceSupTermsProgramObject); - renderQuadForCalc(quadCalcVAO, vertexSize); - if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - fmt::format("deltaE_texture-scattering_order-{}.ppm", scatteringOrder), - _deltaETableSize - ); - } - _irradianceSupTermsProgramObject->deactivate(); - - // line 9 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _deltaSRayleighTableTexture, - 0 - ); - glViewport(0, 0, _mu_s_samples * _nu_samples, _mu_samples); - _inScatteringSupTermsProgramObject->activate(); - transmittanceTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); - _inScatteringSupTermsProgramObject->setUniform( - "transmittanceTexture", - transmittanceTableTextureUnit - ); - deltaJTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaJTableTexture); - _inScatteringSupTermsProgramObject->setUniform( - "deltaJTexture", - deltaJTableTextureUnit - ); - loadAtmosphereDataIntoShaderProgram(*_inScatteringSupTermsProgramObject); - for (int layer = 0; layer < _r_samples; ++layer) { - step3DTexture(*_inScatteringSupTermsProgramObject, layer, true); - renderQuadForCalc(quadCalcVAO, vertexSize); - } - if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - fmt::format("deltaS_texture-scattering_order-{}.ppm", scatteringOrder), - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) - ); - } - _inScatteringSupTermsProgramObject->deactivate(); - - glEnable(GL_BLEND); - glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); - glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE); - - // line 10 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _irradianceTableTexture, - 0 - ); - glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); - _irradianceFinalProgramObject->activate(); - deltaETableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_2D, _deltaETableTexture); - _irradianceFinalProgramObject->setUniform( - "deltaETexture", - deltaETableTextureUnit - ); - loadAtmosphereDataIntoShaderProgram(*_irradianceFinalProgramObject); - renderQuadForCalc(quadCalcVAO, vertexSize); - if (_saveCalculationTextures) { - saveTextureFile( - GL_COLOR_ATTACHMENT0, - fmt::format("irradianceTable_order-{}.ppm", scatteringOrder), - _deltaETableSize - ); - } - _irradianceFinalProgramObject->deactivate(); - - // line 11 in algorithm 4.1 - glFramebufferTexture( - GL_FRAMEBUFFER, - GL_COLOR_ATTACHMENT0, - _inScatteringTableTexture, - 0 - ); - glViewport(0, 0, _mu_s_samples * _nu_samples, _mu_samples); - _deltaSSupTermsProgramObject->activate(); - deltaSRayleighTableTextureUnit.activate(); - glBindTexture(GL_TEXTURE_3D, _deltaSRayleighTableTexture); - _deltaSSupTermsProgramObject->setUniform( - "deltaSTexture", - deltaSRayleighTableTextureUnit - ); - loadAtmosphereDataIntoShaderProgram(*_deltaSSupTermsProgramObject); - for (int layer = 0; layer < _r_samples; ++layer) { - step3DTexture(*_deltaSSupTermsProgramObject, layer, false); - renderQuadForCalc(quadCalcVAO, vertexSize); - } - if (_saveCalculationTextures) { - saveTextureFile(GL_COLOR_ATTACHMENT0, - fmt::format("inscatteringTable_order-{}.ppm", scatteringOrder), - glm::ivec2(_mu_s_samples * _nu_samples, _mu_samples) - ); - } - _deltaSSupTermsProgramObject->deactivate(); - - glDisable(GL_BLEND); - } - - // Restores OpenGL blending state - global::renderEngine->openglStateCache().resetBlendState(); + program.deactivate(); } -void AtmosphereDeferredcaster::preCalculateAtmosphereParam() { - // Load Shader Programs for Calculations - loadComputationPrograms(); +void AtmosphereDeferredcaster::calculateDeltaE(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, + GLuint deltaE, GLuint deltaSRayleigh, + GLuint deltaSMie) +{ + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaE, 0); + glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); + program.activate(); + + ghoul::opengl::TextureUnit deltaSRayleighUnit; + deltaSRayleighUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSRayleigh); + program.setUniform("deltaSRTexture", deltaSRayleighUnit); + + ghoul::opengl::TextureUnit deltaSMieUnit; + deltaSMieUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSMie); + program.setUniform("deltaSMTexture", deltaSMieUnit); + + program.setUniform("firstIteration", (scatteringOrder == 2) ? 1 : 0); + program.setUniform("Rg", _atmospherePlanetRadius); + program.setUniform("Rt", _atmosphereRadius); + program.setUniform("mieG", _miePhaseConstant); + program.setUniform("SKY", _irradianceTableSize); + program.setUniform("SAMPLES_MU_S", _muSSamples); + program.setUniform("SAMPLES_NU", _nuSamples); + program.setUniform("SAMPLES_MU", _muSamples); + program.setUniform("SAMPLES_R", _rSamples); + renderQuadForCalc(vao); + if (_saveCalculationTextures) { + saveTextureFile( + fmt::format("deltaE_texture-scattering_order-{}.ppm", scatteringOrder), + _deltaETableSize + ); + } + program.deactivate(); +} + +void AtmosphereDeferredcaster::calculateDeltaS(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, + GLuint deltaSRayleigh, GLuint deltaJ) +{ + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, deltaSRayleigh, 0); + glViewport(0, 0, _textureSize.x, _textureSize.y); + program.activate(); + + ghoul::opengl::TextureUnit transmittanceUnit; + transmittanceUnit.activate(); + glBindTexture(GL_TEXTURE_2D, _transmittanceTableTexture); + program.setUniform("transmittanceTexture", transmittanceUnit); + + ghoul::opengl::TextureUnit deltaJUnit; + deltaJUnit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaJ); + program.setUniform("deltaJTexture", deltaJUnit); + + program.setUniform("Rg", _atmospherePlanetRadius); + program.setUniform("Rt", _atmosphereRadius); + program.setUniform("SAMPLES_MU_S", _muSSamples); + program.setUniform("SAMPLES_NU", _nuSamples); + program.setUniform("SAMPLES_MU", _muSamples); + program.setUniform("SAMPLES_R", _rSamples); + for (int layer = 0; layer < _rSamples; ++layer) { + program.setUniform("layer", layer); + step3DTexture(program, layer); + renderQuadForCalc(vao); + } + if (_saveCalculationTextures) { + saveTextureFile( + fmt::format("deltaS_texture-scattering_order-{}.ppm", scatteringOrder), + glm::ivec2(_textureSize) + ); + } + program.deactivate(); +} + +void AtmosphereDeferredcaster::calculateIrradiance(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, + GLuint deltaE) +{ + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + _irradianceTableTexture, + 0 + ); + glViewport(0, 0, _deltaETableSize.x, _deltaETableSize.y); + program.activate(); + + ghoul::opengl::TextureUnit unit; + unit.activate(); + glBindTexture(GL_TEXTURE_2D, deltaE); + program.setUniform("deltaETexture", unit); + program.setUniform("OTHER_TEXTURES", _deltaETableSize); + + renderQuadForCalc(vao); + if (_saveCalculationTextures) { + saveTextureFile( + fmt::format("irradianceTable_order-{}.ppm", scatteringOrder), + _deltaETableSize + ); + } + program.deactivate(); +} + +void AtmosphereDeferredcaster::calculateInscattering(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& prg, + GLuint deltaSRayleigh) + +{ + glFramebufferTexture( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + _inScatteringTableTexture, + 0 + ); + glViewport(0, 0, _textureSize.x, _textureSize.y); + prg.activate(); + + ghoul::opengl::TextureUnit unit; + unit.activate(); + glBindTexture(GL_TEXTURE_3D, deltaSRayleigh); + prg.setUniform("deltaSTexture", unit); + prg.setUniform("SAMPLES_MU_S", _muSSamples); + prg.setUniform("SAMPLES_NU", _nuSamples); + prg.setUniform("SAMPLES_MU", _muSamples); + prg.setUniform("SAMPLES_R", _rSamples); + for (int layer = 0; layer < _rSamples; ++layer) { + prg.setUniform("layer", layer); + renderQuadForCalc(vao); + } + if (_saveCalculationTextures) { + saveTextureFile( + fmt::format("inscatteringTable_order-{}.ppm", scatteringOrder), + glm::ivec2(_textureSize) + ); + } + prg.deactivate(); +} + +void AtmosphereDeferredcaster::calculateAtmosphereParameters() { + using ProgramObject = ghoul::opengl::ProgramObject; + std::unique_ptr deltaJProgram = ProgramObject::Build( + "DeltaJ Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/deltaJ_calc_fs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") + ); + std::unique_ptr irradianceSupTermsProgram = ProgramObject::Build( + "IrradianceSupTerms Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_sup_calc_fs.glsl") + ); + std::unique_ptr inScatteringSupTermsProgram = ProgramObject::Build( + "InScatteringSupTerms Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/inScattering_sup_calc_fs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") + ); + std::unique_ptr irradianceFinalProgram = ProgramObject::Build( + "IrradianceEFinal Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/irradiance_final_fs.glsl") + ); + std::unique_ptr deltaSSupTermsProgram = ProgramObject::Build( + "DeltaSSUPTerms Program", + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_vs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/deltaS_sup_calc_fs.glsl"), + absPath("${MODULE_ATMOSPHERE}/shaders/calculation_gs.glsl") + ); - // Create Textures for Calculations - createComputationTextures(); // Saves current FBO first GLint defaultFBO; @@ -1341,86 +978,127 @@ void AtmosphereDeferredcaster::preCalculateAtmosphereParam() { glDrawBuffers(1, drawBuffers); // Prepare for rendering/calculations - GLuint quadCalcVAO; - GLuint quadCalcVBO; - createRenderQuad(&quadCalcVAO, &quadCalcVBO, 1.0f); - - // Starting Calculations... - LDEBUG("Starting precalculations for scattering effects"); + GLuint quadVao; + GLuint quadVbo; + createRenderQuad(&quadVao, &quadVbo); // Execute Calculations - executeCalculations(quadCalcVAO, drawBuffers, 6); + LDEBUG("Starting precalculations for scattering effects"); + glDisable(GL_BLEND); - deleteUnusedComputationTextures(); + // See Precomputed Atmosphere Scattering from Bruneton et al. paper, algorithm 4.1: + calculateTransmittance(quadVao); + + // line 2 in algorithm 4.1 + GLuint deltaETable = calculateDeltaE(quadVao); + + // line 3 in algorithm 4.1 + auto [deltaSRayleighTable, deltaSMieTable] = calculateDeltaS(quadVao); + + // line 4 in algorithm 4.1 + calculateIrradiance(quadVao); + + // line 5 in algorithm 4.1 + calculateInscattering(quadVao, deltaSRayleighTable, deltaSMieTable); + + GLuint deltaJTable = createTexture(_textureSize, "DeltaJ", 3); + + // loop in line 6 in algorithm 4.1 + for (int scatteringOrder = 2; scatteringOrder <= 4; ++scatteringOrder) { + // line 7 in algorithm 4.1 + calculateDeltaJ( + quadVao, + scatteringOrder, + *deltaJProgram, + deltaJTable, + deltaETable, + deltaSRayleighTable, + deltaSMieTable + ); + + // line 8 in algorithm 4.1 + calculateDeltaE( + quadVao, + scatteringOrder, + *irradianceSupTermsProgram, + deltaETable, + deltaSRayleighTable, + deltaSMieTable + ); + + // line 9 in algorithm 4.1 + calculateDeltaS( + quadVao, + scatteringOrder, + *inScatteringSupTermsProgram, + deltaSRayleighTable, + deltaJTable + ); + + glEnable(GL_BLEND); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendFuncSeparate(GL_ONE, GL_ONE, GL_ONE, GL_ONE); + + // line 10 in algorithm 4.1 + calculateIrradiance( + quadVao, + scatteringOrder, + *irradianceFinalProgram, + deltaETable + ); + + // line 11 in algorithm 4.1 + calculateInscattering( + quadVao, + scatteringOrder, + *deltaSSupTermsProgram, + deltaSRayleighTable + ); + + glDisable(GL_BLEND); + } + + // Restores OpenGL blending state + global::renderEngine->openglStateCache().resetBlendState(); + + glDeleteTextures(1, &deltaETable); + glDeleteTextures(1, &deltaSRayleighTable); + glDeleteTextures(1, &deltaSMieTable); + glDeleteTextures(1, &deltaJTable); // Restores system state glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO); global::renderEngine->openglStateCache().setViewportState(viewport); - glDeleteBuffers(1, &quadCalcVBO); - glDeleteVertexArrays(1, &quadCalcVAO); + glDeleteBuffers(1, &quadVao); + glDeleteVertexArrays(1, &quadVbo); glDeleteFramebuffers(1, &calcFBO); LDEBUG("Ended precalculations for Atmosphere effects"); } -void AtmosphereDeferredcaster::loadAtmosphereDataIntoShaderProgram( - ghoul::opengl::ProgramObject& shaderProg) -{ - shaderProg.setUniform("Rg", _atmospherePlanetRadius); - shaderProg.setUniform("Rt", _atmosphereRadius); - shaderProg.setUniform("AverageGroundReflectance", _planetAverageGroundReflectance); - shaderProg.setUniform("groundRadianceEmission", _planetGroundRadianceEmission); - shaderProg.setUniform("HR", _rayleighHeightScale); - shaderProg.setUniform("betaRayleigh", _rayleighScatteringCoeff); - shaderProg.setUniform("HM", _mieHeightScale); - shaderProg.setUniform("betaMieScattering", _mieScatteringCoeff); - shaderProg.setUniform("betaMieExtinction", _mieExtinctionCoeff); - shaderProg.setUniform("mieG", _miePhaseConstant); - shaderProg.setUniform("sunRadiance", _sunRadianceIntensity); - shaderProg.setUniform("TRANSMITTANCE", _transmittanceTableSize); - shaderProg.setUniform("SKY", _irradianceTableSize); - shaderProg.setUniform("OTHER_TEXTURES", _deltaETableSize); - shaderProg.setUniform("SAMPLES_R", _r_samples); - shaderProg.setUniform("SAMPLES_MU", _mu_samples); - shaderProg.setUniform("SAMPLES_MU_S", _mu_s_samples); - shaderProg.setUniform("SAMPLES_NU", _nu_samples); - shaderProg.setUniform("ozoneLayerEnabled", _ozoneEnabled); - shaderProg.setUniform("HO", _ozoneHeightScale); - shaderProg.setUniform("betaOzoneExtinction", _ozoneExtinctionCoeff); -} - -void AtmosphereDeferredcaster::step3DTexture(ghoul::opengl::ProgramObject& shaderProg, - int layer, bool doCalculation) +void AtmosphereDeferredcaster::step3DTexture(ghoul::opengl::ProgramObject& prg, int layer) { // See OpenGL redbook 8th Edition page 556 for Layered Rendering - if (doCalculation) { - const float earth2 = _atmospherePlanetRadius * _atmospherePlanetRadius; - const float diff = _atmosphereRadius * _atmosphereRadius - earth2; - const float ri = static_cast(layer) / static_cast(_r_samples - 1); - const float eps = [&]() { - if (layer == 0) { - return 0.01f; - } - else { - if (layer == (_r_samples - 1)) { - return -0.001f; - } - else { - return 0.f; - } - } - }(); - const float r = std::sqrt(earth2 + ri * ri * diff) + eps; - const float dminG = r - _atmospherePlanetRadius; - const float dminT = _atmosphereRadius - r; - const float dh = std::sqrt(r * r - earth2); - const float dH = dh + std::sqrt(diff); - - shaderProg.setUniform("r", r); - shaderProg.setUniform("dhdH", dminT, dH, dminG, dh); + const float planet2 = _atmospherePlanetRadius * _atmospherePlanetRadius; + const float diff = _atmosphereRadius * _atmosphereRadius - planet2; + const float ri = static_cast(layer) / static_cast(_rSamples - 1); + float eps = 0.01f; + if (layer > 0) { + if (layer == (_rSamples - 1)) { + eps = -0.001f; + } + else { + eps = 0.f; + } } + const float r = std::sqrt(planet2 + ri * ri * diff) + eps; + const float dminG = r - _atmospherePlanetRadius; + const float dminT = _atmosphereRadius - r; + const float dh = std::sqrt(r * r - planet2); + const float dH = dh + std::sqrt(diff); - shaderProg.setUniform("layer", layer); + prg.setUniform("r", r); + prg.setUniform("dhdH", dminT, dH, dminG, dh); } } // namespace openspace diff --git a/modules/atmosphere/rendering/atmospheredeferredcaster.h b/modules/atmosphere/rendering/atmospheredeferredcaster.h index 589d2479c7..a117882f8e 100644 --- a/modules/atmosphere/rendering/atmospheredeferredcaster.h +++ b/modules/atmosphere/rendering/atmospheredeferredcaster.h @@ -57,16 +57,17 @@ struct ShadowRenderingStruct { class AtmosphereDeferredcaster : public Deferredcaster { public: + AtmosphereDeferredcaster(float textureScale, + std::vector shadowConfigArray, bool saveCalculatedTextures); virtual ~AtmosphereDeferredcaster() = default; void initialize(); void deinitialize(); - void preRaycast(const RenderData& renderData, const DeferredcastData& deferredData, + void preRaycast(const RenderData& data, const DeferredcastData& deferredData, ghoul::opengl::ProgramObject& program) override; - void postRaycast(const RenderData& renderData, const DeferredcastData& deferredData, + void postRaycast(const RenderData& data, const DeferredcastData& deferredData, ghoul::opengl::ProgramObject& program) override; - std::filesystem::path deferredcastPath() const override; std::filesystem::path deferredcastVSPath() const override; std::filesystem::path deferredcastFSPath() const override; std::filesystem::path helperPath() const override; @@ -75,82 +76,64 @@ public: void update(const UpdateData&) override; - void preCalculateAtmosphereParam(); + void calculateAtmosphereParameters(); void setModelTransform(glm::dmat4 transform); - void setTime(double time); - void setAtmosphereRadius(float atmRadius); - void setPlanetRadius(float planetRadius); - void setPlanetAverageGroundReflectance(float averageGReflectance); - void setPlanetGroundRadianceEmission(float groundRadianceEmission); - void setRayleighHeightScale(float rayleighHeightScale); - void enableOzone(bool enable); - void setOzoneHeightScale(float ozoneHeightScale); - void setMieHeightScale(float mieHeightScale); - void setMiePhaseConstant(float miePhaseConstant); - void setSunRadianceIntensity(float sunRadiance); - void setRayleighScatteringCoefficients(glm::vec3 rayScattCoeff); - void setOzoneExtinctionCoefficients(glm::vec3 ozoneExtCoeff); - void setMieScatteringCoefficients(glm::vec3 mieScattCoeff); - void setMieExtinctionCoefficients(glm::vec3 mieExtCoeff); - void setEllipsoidRadii(glm::dvec3 radii); - void setShadowConfigArray(std::vector shadowConfigArray); - void setHardShadows(bool enabled); - void enableSunFollowing(bool enable); - void setPrecalculationTextureScale(float preCalculatedTexturesScale); - void enablePrecalculationTexturesSaving(); + void setParameters(float atmosphereRadius, float planetRadius, + float averageGroundReflectance, float groundRadianceEmission, + float rayleighHeightScale, bool enableOzone, float ozoneHeightScale, + float mieHeightScale, float miePhaseConstant, float sunRadiance, + glm::vec3 rayScatteringCoefficients, glm::vec3 ozoneExtinctionCoefficients, + glm::vec3 mieScatteringCoefficients, glm::vec3 mieExtinctionCoefficients, + bool sunFollowing); + + void setHardShadows(bool enabled); private: - void loadComputationPrograms(); - void unloadComputationPrograms(); - void createComputationTextures(); - void deleteComputationTextures(); - void deleteUnusedComputationTextures(); - void executeCalculations(GLuint quadCalcVAO, GLenum drawBuffers[1], - GLsizei vertexSize); - void step3DTexture(ghoul::opengl::ProgramObject& shaderProg, int layer, - bool doCalculation); - void loadAtmosphereDataIntoShaderProgram(ghoul::opengl::ProgramObject& shaderProg); + void step3DTexture(ghoul::opengl::ProgramObject& prg, int layer); + + void calculateTransmittance(GLuint vao); + GLuint calculateDeltaE(GLuint vao); + std::pair calculateDeltaS(GLuint vao); + void calculateIrradiance(GLuint vao); + void calculateInscattering(GLuint vao, GLuint deltaSRayleigh, GLuint deltaSMie); + void calculateDeltaJ(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, GLuint deltaJ, GLuint deltaE, + GLuint deltaSRayleigh, GLuint deltaSMie); + void calculateDeltaE(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, GLuint deltaE, GLuint deltaSRayleigh, + GLuint deltaSMie); + void calculateDeltaS(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, GLuint deltaSRayleigh, GLuint deltaJ); + void calculateIrradiance(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, GLuint deltaE); + void calculateInscattering(GLuint vao, int scatteringOrder, + ghoul::opengl::ProgramObject& program, GLuint deltaSRayleigh); - std::unique_ptr _transmittanceProgramObject; - std::unique_ptr _irradianceProgramObject; - std::unique_ptr _irradianceSupTermsProgramObject; - std::unique_ptr _irradianceFinalProgramObject; - std::unique_ptr _inScatteringProgramObject; - std::unique_ptr _inScatteringSupTermsProgramObject; - std::unique_ptr _deltaEProgramObject; - std::unique_ptr _deltaSProgramObject; - std::unique_ptr _deltaSSupTermsProgramObject; - std::unique_ptr _deltaJProgramObject; UniformCache(cullAtmosphere, Rg, Rt, groundRadianceEmission, HR, betaRayleigh, HM, betaMieExtinction, mieG, sunRadiance, ozoneLayerEnabled, HO, betaOzoneExtinction, SAMPLES_R, SAMPLES_MU, SAMPLES_MU_S, SAMPLES_NU, inverseModelTransformMatrix, - modelTransformMatrix, projectionToModelTransformMatrix, - viewToWorldMatrix, camPosObj, sunDirectionObj, hardShadows, - transmittanceTexture, irradianceTexture, inscatterTexture) _uniformCache; - - GLuint _transmittanceTableTexture = 0; - GLuint _irradianceTableTexture = 0; - GLuint _inScatteringTableTexture = 0; - GLuint _deltaETableTexture = 0; - GLuint _deltaSRayleighTableTexture = 0; - GLuint _deltaSMieTableTexture = 0; - GLuint _deltaJTableTexture = 0; + modelTransformMatrix, projectionToModelTransform, viewToWorldMatrix, + camPosObj, sunDirectionObj, hardShadows, transmittanceTexture, irradianceTexture, + inscatterTexture) _uniformCache; ghoul::opengl::TextureUnit _transmittanceTableTextureUnit; ghoul::opengl::TextureUnit _irradianceTableTextureUnit; ghoul::opengl::TextureUnit _inScatteringTableTextureUnit; + GLuint _transmittanceTableTexture = 0; + GLuint _irradianceTableTexture = 0; + GLuint _inScatteringTableTexture = 0; + // Atmosphere Data - bool _atmosphereCalculated = false; bool _ozoneEnabled = false; bool _sunFollowingCameraEnabled = false; float _atmosphereRadius = 0.f; float _atmospherePlanetRadius = 0.f; - float _planetAverageGroundReflectance = 0.f; - float _planetGroundRadianceEmission = 0.f; + float _averageGroundReflectance = 0.f; + float _groundRadianceEmission = 0.f; float _rayleighHeightScale = 0.f; float _ozoneHeightScale = 0.f; float _mieHeightScale = 0.f; @@ -161,34 +144,33 @@ private: glm::vec3 _ozoneExtinctionCoeff = glm::vec3(0.f); glm::vec3 _mieScatteringCoeff = glm::vec3(0.f); glm::vec3 _mieExtinctionCoeff = glm::vec3(0.f); - glm::dvec3 _ellipsoidRadii = glm::dvec3(0.0); // Atmosphere Textures Dimmensions - glm::ivec2 _transmittanceTableSize = glm::ivec2(256, 64); - glm::ivec2 _irradianceTableSize = glm::ivec2(64, 16); - glm::ivec2 _deltaETableSize = glm::ivec2(64, 16); - int _r_samples = 32; - int _mu_samples = 128; - int _mu_s_samples = 32; - int _nu_samples = 8; + const glm::ivec2 _transmittanceTableSize; + const glm::ivec2 _irradianceTableSize; + const glm::ivec2 _deltaETableSize; + const int _muSSamples; + const int _nuSamples; + const int _muSamples; + const int _rSamples; + const glm::ivec3 _textureSize; glm::dmat4 _modelTransform; - double _time = 0.0; // Eclipse Shadows std::vector _shadowConfArray; + std::vector _shadowDataArrayCache; bool _hardShadowsEnabled = false; // Atmosphere Debugging - bool _saveCalculationTextures = false; + const bool _saveCalculationTextures = false; - std::vector _shadowDataArrayCache; // Assuming < 1000 shadow casters, the longest uniform name that we are getting is // shadowDataArray[999].casterPositionVec // which needs to fit into the uniform buffer char _uniformNameBuffer[40]; }; -} // openspace +} // namespace openspace #endif // __OPENSPACE_MODULE_ATMOSPHERE___ATMOSPHEREDEFERREDCASTER___H__ diff --git a/modules/atmosphere/rendering/renderableatmosphere.cpp b/modules/atmosphere/rendering/renderableatmosphere.cpp index e14ff893a7..6f0fa990ac 100644 --- a/modules/atmosphere/rendering/renderableatmosphere.cpp +++ b/modules/atmosphere/rendering/renderableatmosphere.cpp @@ -244,7 +244,7 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary) MieScatteringCoeffInfo, glm::vec3(0.004f), glm::vec3(0.00001f), glm::vec3(1.f) ) - , _mieScatteringExtinctionPropCoefficient( + , _mieScatteringExtinctionPropCoeff( MieScatteringExtinctionPropCoeffInfo, 0.9f, 0.01f, 1.f ) @@ -328,16 +328,15 @@ RenderableAtmosphere::RenderableAtmosphere(const ghoul::Dictionary& dictionary) _miePhaseConstant.onChange(updateWithCalculation); addProperty(_miePhaseConstant); - _mieScatteringExtinctionPropCoefficient = + _mieScatteringExtinctionPropCoeff = _mieScattExtPropCoefProp != 1.f ? _mieScattExtPropCoefProp : _mieScatteringCoeff.value().x / _mieExtinctionCoeff.x; - _mieScatteringExtinctionPropCoefficient.onChange(updateWithCalculation); - addProperty(_mieScatteringExtinctionPropCoefficient); + _mieScatteringExtinctionPropCoeff.onChange(updateWithCalculation); + addProperty(_mieScatteringExtinctionPropCoeff); if (p.debug.has_value()) { - _preCalculatedTexturesScale = - p.debug->preCalculatedTextureScale.value_or(_preCalculatedTexturesScale); + _textureScale = p.debug->preCalculatedTextureScale.value_or(_textureScale); _saveCalculationsToTexture = p.debug->saveCalculatedTextures.value_or(_saveCalculationsToTexture); @@ -364,15 +363,13 @@ void RenderableAtmosphere::deinitializeGL() { } void RenderableAtmosphere::initializeGL() { - _deferredcaster = std::make_unique(); + _deferredcaster = std::make_unique( + _textureScale, + _shadowEnabled ? std::move(_shadowConfArray) : std::vector(), + _saveCalculationsToTexture + ); + _shadowConfArray.clear(); updateAtmosphereParameters(); - - if (_shadowEnabled) { - _deferredcaster->setShadowConfigArray(_shadowConfArray); - // We no longer need it - _shadowConfArray.clear(); - } - _deferredcaster->initialize(); global::deferredcasterManager->attachDeferredcaster(*_deferredcaster); @@ -382,13 +379,11 @@ bool RenderableAtmosphere::isReady() const { return true; } -glm::dmat4 RenderableAtmosphere::computeModelTransformMatrix( - const TransformData& transformData) -{ +glm::dmat4 RenderableAtmosphere::computeModelTransformMatrix(const TransformData& data) { // scale the planet to appropriate size since the planet is a unit sphere - return glm::translate(glm::dmat4(1.0), transformData.translation) * - glm::dmat4(transformData.rotation) * - glm::scale(glm::dmat4(1.0), glm::dvec3(transformData.scale)); + return glm::translate(glm::dmat4(1.0), data.translation) * + glm::dmat4(data.rotation) * + glm::scale(glm::dmat4(1.0), glm::dvec3(data.scale)); } void RenderableAtmosphere::render(const RenderData& data, RendererTasks& renderTask) { @@ -404,11 +399,10 @@ void RenderableAtmosphere::update(const UpdateData& data) { _deferredCasterNeedsUpdate = false; } if (_deferredCasterNeedsCalculation) { - _deferredcaster->preCalculateAtmosphereParam(); + _deferredcaster->calculateAtmosphereParameters(); _deferredCasterNeedsCalculation = false; } - _deferredcaster->setTime(data.time.j2000Seconds()); glm::dmat4 modelTransform = computeModelTransformMatrix(data.modelTransform); _deferredcaster->setModelTransform(modelTransform); _deferredcaster->update(data); @@ -416,34 +410,26 @@ void RenderableAtmosphere::update(const UpdateData& data) { void RenderableAtmosphere::updateAtmosphereParameters() { _mieExtinctionCoeff = - _mieScatteringCoeff.value() / _mieScatteringExtinctionPropCoefficient.value(); + _mieScatteringCoeff.value() / _mieScatteringExtinctionPropCoeff.value(); - _deferredcaster->setAtmosphereRadius(_planetRadius + _atmosphereHeight); - _deferredcaster->setPlanetRadius(_planetRadius); - _deferredcaster->setPlanetAverageGroundReflectance(_groundAverageReflectance); - _deferredcaster->setPlanetGroundRadianceEmission(_groundRadianceEmission); - _deferredcaster->setRayleighHeightScale(_rayleighHeightScale); - _deferredcaster->enableOzone(_ozoneEnabled); - _deferredcaster->setOzoneHeightScale(_ozoneHeightScale); - _deferredcaster->setMieHeightScale(_mieHeightScale); - _deferredcaster->setMiePhaseConstant(_miePhaseConstant); - _deferredcaster->setSunRadianceIntensity(_sunIntensity); - _deferredcaster->setRayleighScatteringCoefficients(_rayleighScatteringCoeff); - _deferredcaster->setOzoneExtinctionCoefficients(_ozoneCoeff); - _deferredcaster->setMieScatteringCoefficients(_mieScatteringCoeff); - _deferredcaster->setMieExtinctionCoefficients(_mieExtinctionCoeff); - _deferredcaster->enableSunFollowing(_sunFollowingCameraEnabled); - // TODO: Fix the ellipsoid nature of the renderable globe (JCC) - //_deferredcaster->setEllipsoidRadii(_ellipsoid.radii()); - - _deferredcaster->setPrecalculationTextureScale(_preCalculatedTexturesScale); - if (_saveCalculationsToTexture) { - _deferredcaster->enablePrecalculationTexturesSaving(); - } - - if (_shadowEnabled) { - _deferredcaster->setHardShadows(_hardShadowsEnabled); - } + _deferredcaster->setParameters( + _planetRadius + _atmosphereHeight, + _planetRadius, + _groundAverageReflectance, + _groundRadianceEmission, + _rayleighHeightScale, + _ozoneEnabled, + _ozoneHeightScale, + _mieHeightScale, + _miePhaseConstant, + _sunIntensity, + _rayleighScatteringCoeff, + _ozoneCoeff, + _mieScatteringCoeff, + _mieExtinctionCoeff, + _sunFollowingCameraEnabled + ); + _deferredcaster->setHardShadows(_hardShadowsEnabled); } } // namespace openspace diff --git a/modules/atmosphere/rendering/renderableatmosphere.h b/modules/atmosphere/rendering/renderableatmosphere.h index 63ec3afeab..c0f871ff2c 100644 --- a/modules/atmosphere/rendering/renderableatmosphere.h +++ b/modules/atmosphere/rendering/renderableatmosphere.h @@ -72,7 +72,7 @@ public: static documentation::Documentation Documentation(); private: - glm::dmat4 computeModelTransformMatrix(const openspace::TransformData& transformData); + glm::dmat4 computeModelTransformMatrix(const openspace::TransformData& data); void updateAtmosphereParameters(); properties::FloatProperty _atmosphereHeight; @@ -85,7 +85,7 @@ private: properties::Vec3Property _ozoneCoeff; properties::FloatProperty _mieHeightScale; properties::Vec3Property _mieScatteringCoeff; - properties::FloatProperty _mieScatteringExtinctionPropCoefficient; + properties::FloatProperty _mieScatteringExtinctionPropCoeff; properties::FloatProperty _miePhaseConstant; properties::FloatProperty _sunIntensity; properties::BoolProperty _sunFollowingCameraEnabled; @@ -98,7 +98,7 @@ private: // Atmosphere Debug bool _saveCalculationsToTexture = false; - float _preCalculatedTexturesScale = 1.f; + float _textureScale = 1.f; std::unique_ptr _deferredcaster; diff --git a/modules/atmosphere/shaders/atmosphere_common.glsl b/modules/atmosphere/shaders/atmosphere_common.glsl index 6a0884f861..c935dc0c50 100644 --- a/modules/atmosphere/shaders/atmosphere_common.glsl +++ b/modules/atmosphere/shaders/atmosphere_common.glsl @@ -54,85 +54,11 @@ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -// Atmosphere Rendering Parameters -uniform float Rg; -uniform float Rt; -uniform float AverageGroundReflectance; -uniform float groundRadianceEmission; -uniform float HR; -uniform vec3 betaRayleigh; -uniform float HO; -uniform vec3 betaOzoneExtinction; -uniform float HM; -uniform vec3 betaMieScattering; -uniform vec3 betaMieExtinction; -uniform float mieG; -uniform float sunRadiance; - -uniform bool ozoneLayerEnabled; - -uniform ivec2 TRANSMITTANCE; -uniform ivec2 SKY; -uniform ivec2 OTHER_TEXTURES; -uniform int SAMPLES_R; -uniform int SAMPLES_MU; -uniform int SAMPLES_MU_S; -uniform int SAMPLES_NU; +const int INSCATTER_INTEGRAL_SAMPLES = 50; +const float M_PI = 3.141592657; const float ATM_EPSILON = 1.0; -// Integration steps -const int TRANSMITTANCE_STEPS = 500; -const int INSCATTER_INTEGRAL_SAMPLES = 50; -const int IRRADIANCE_INTEGRAL_SAMPLES = 32; -const int INSCATTER_SPHERICAL_INTEGRAL_SAMPLES = 16; - -const float M_PI = 3.141592657; -const float M_2PI = 2.0 * M_PI; - -uniform sampler2D transmittanceTexture; - -float Rg2 = Rg * Rg; -float Rt2 = Rt * Rt; -float H = sqrt(Rt2 - Rg2); -float H2 = Rt2 - Rg2; -float invSamplesMu = 1.0 / float(SAMPLES_MU); -float invSamplesR = 1.0 / float(SAMPLES_R); -float invSamplesMuS = 1.0 / float(SAMPLES_MU_S); -float invSamplesNu = 1.0 / float(SAMPLES_NU); -float RtMinusRg = float(Rt - Rg); -float invRtMinusRg = 1.0 / RtMinusRg; - -float opticalDepth(float localH, float r, float mu, float d) { - float invH = 1.0 / localH; - float a = sqrt(0.5 * invH * r); - vec2 a01 = a * vec2(mu, mu + d / r); - vec2 a01s = sign(a01); - vec2 a01sq = a01 * a01; - float x = a01s.y > a01s.x ? exp(a01sq.x) : 0.0; - vec2 y = a01s / (2.3193 * abs(a01) + sqrt(1.52 * a01sq + 4.0)) * vec2(1.0, exp(-d * invH * (d / (2.0 * r) + mu))); - return sqrt(M_2PI * H * r) * exp((Rg-r)*invH) * (x + dot(y, vec2(1.0, -1.0))); -} - -vec3 analyticTransmittance(float r, float mu, float d) { - vec3 ozone = vec3(0.0); - if (ozoneLayerEnabled) { - ozone = betaOzoneExtinction * (0.0000006) * opticalDepth(HO, r, mu, d); - } - return exp(-betaRayleigh * opticalDepth(HR, r, mu, d) - ozone - - betaMieExtinction * opticalDepth(HM, r, mu, d)); -} - -vec3 irradiance(sampler2D sampler, float r, float muSun) { - float u_r = (r - Rg) * invRtMinusRg; - float u_muSun = (muSun + 0.2) / 1.2; - return texture(sampler, vec2(u_muSun, u_r)).rgb; -} - - -//================================================// -//=============== General Functions ==============// -//================================================// // In the following shaders r (altitude) is the length of vector/position x in the // atmosphere (or on the top of it when considering an observer in space), where the light // is coming from the opposite direction of the view direction, here the vector v or @@ -142,7 +68,7 @@ vec3 irradiance(sampler2D sampler, float r, float muSun) { // or top of atmosphere // r := || vec(x) || e [0, Rt] // mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r -float rayDistance(float r, float mu) { +float rayDistance(float r, float mu, float Rt, float Rg) { // The light ray starting at the observer in/on the atmosphere can have to possible end // points: the top of the atmosphere or the planet ground. So the shortest path is the // one we are looking for, otherwise we may be passing through the ground @@ -151,9 +77,8 @@ float rayDistance(float r, float mu) { float atmRadiusEps2 = (Rt + ATM_EPSILON) * (Rt + ATM_EPSILON); float mu2 = mu * mu; float r2 = r * r; - float rg2 = Rg * Rg; float rayDistanceAtmosphere = -r * mu + sqrt(r2 * (mu2 - 1.0) + atmRadiusEps2); - float delta = r2 * (mu2 - 1.0) + rg2; + float delta = r2 * (mu2 - 1.0) + Rg*Rg; // Ray may be hitting ground if (delta >= 0.0) { @@ -173,19 +98,23 @@ float rayDistance(float r, float mu) { // nu := cosone of the angle between vec(s) and vec(v) // dhdH := it is a vec4. dhdH.x stores the dminT := Rt - r, dhdH.y stores the dH value // (see paper), dhdH.z stores dminG := r - Rg and dhdH.w stores dh (see paper) -void unmappingMuMuSunNu(float r, vec4 dhdH, out float mu, out float muSun, out float nu) { +void unmappingMuMuSunNu(float r, vec4 dhdH, int SAMPLES_MU, float Rg, float Rt, + int SAMPLES_MU_S, int SAMPLES_NU, + out float mu, out float muSun, out float nu) +{ // Window coordinates of pixel (uncentering also) vec2 fragment = gl_FragCoord.xy - vec2(0.5); // Pre-calculations float r2 = r * r; + float Rg2 = Rg * Rg; float halfSAMPLE_MU = float(SAMPLES_MU) / 2.0; // If the (vec(x) dot vec(v))/r is negative, i.e., the light ray has great probability // to touch the ground, we obtain mu considering the geometry of the ground if (fragment.y < halfSAMPLE_MU) { float ud = 1.0 - (fragment.y / (halfSAMPLE_MU - 1.0)); - float d = min(max(dhdH.z, ud * dhdH.w), dhdH.w * 0.999); + float d = min(max(dhdH.z, ud * dhdH.w), dhdH.w * 0.999); // cosine law: Rg^2 = r^2 + d^2 - 2rdcos(pi-theta) where cosine(theta) = mu mu = (Rg2 - r2 - d * d) / (2.0 * r * d); // We can't handle a ray inside the planet, i.e., when r ~ Rg, so we check against it. @@ -199,12 +128,12 @@ void unmappingMuMuSunNu(float r, vec4 dhdH, out float mu, out float muSun, out f float d = (fragment.y - halfSAMPLE_MU) / (halfSAMPLE_MU - 1.0); d = min(max(dhdH.x, d * dhdH.y), dhdH.y * 0.999); // cosine law: Rt^2 = r^2 + d^2 - 2rdcos(pi-theta) where cosine(theta) = mu - mu = (Rt2 - r2 - d * d) / (2.0 * r * d); + mu = (Rt*Rt - r2 - d * d) / (2.0 * r * d); } float modValueMuSun = mod(fragment.x, float(SAMPLES_MU_S)) / (float(SAMPLES_MU_S) - 1.0); - // The following mapping is different from the paper. See Colliene for an details. - muSun = tan((2.0 * modValueMuSun - 1.0 + 0.26) * 1.1f) / tan(1.26 * 1.1); + // The following mapping is different from the paper. See Collienne for an details. + muSun = tan((2.0 * modValueMuSun - 1.0 + 0.26) * 1.1) / tan(1.26 * 1.1); nu = -1.0 + floor(fragment.x / float(SAMPLES_MU_S)) / (float(SAMPLES_NU) - 1.0) * 2.0; } @@ -213,14 +142,14 @@ void unmappingMuMuSunNu(float r, vec4 dhdH, out float mu, out float muSun, out f // hits the ground or the top of atmosphere. // r := height of starting point vect(x) // mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r -vec3 transmittance(float r, float mu) { +vec3 transmittance(sampler2D tex, float r, float mu, float Rg, float Rt) { // Given the position x (here the altitude r) and the view angle v // (here the cosine(v)= mu), we map this - float u_r = sqrt((r - Rg) * invRtMinusRg); - // See Colliene to understand the mapping + float u_r = sqrt((r - Rg) / (Rt - Rg)); + // See Collienne to understand the mapping float u_mu = atan((mu + 0.15) / 1.15 * tan(1.5)) / 1.5; - return texture(transmittanceTexture, vec2(u_mu, u_r)).rgb; + return texture(tex, vec2(u_mu, u_r)).rgb; } // Given a position r and direction mu, calculates de transmittance along the ray with @@ -228,13 +157,13 @@ vec3 transmittance(float r, float mu) { // T(a,b) = TableT(a,v)/TableT(b, v) // r := height of starting point vect(x) // mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r -vec3 transmittance(float r, float mu, float d) { +vec3 transmittance(sampler2D tex, float r, float mu, float d, float Rg, float Rt) { // Here we use the transmittance property: T(x,v) = T(x,d)*T(d,v) to, given a distance // d, calculates that transmittance along that distance starting in x (height r): // T(x,d) = T(x,v)/T(d,v). // // From cosine law: c^2 = a^2 + b^2 - 2*a*b*cos(ab) - float ri = sqrt(d * d + r * r + 2.0 * r * d * mu); + float ri = sqrt(d * d + r * r + 2.0 * r * d * mu); // mu_i = (vec(d) dot vec(v)) / r_i // = ((vec(x) + vec(d-x)) dot vec(v))/ r_i // = (r*mu + d) / r_i @@ -246,19 +175,21 @@ vec3 transmittance(float r, float mu, float d) { // x --> x0, then x0-->x. // Also, let's use the property: T(a,c) = T(a,b)*T(b,c) // Because T(a,c) and T(b,c) are already in the table T, T(a,b) = T(a,c)/T(b,c). + vec3 res; if (mu > 0.0) { - return min(transmittance(r, mu) / transmittance(ri, mui), 1.0); + res = transmittance(tex, r, mu, Rg, Rt) / transmittance(tex, ri, mui, Rg, Rt); } else { - return min(transmittance(ri, -mui) / transmittance(r, -mu), 1.0); + res = transmittance(tex, ri, -mui, Rg, Rt) / transmittance(tex, r, -mu, Rg, Rt); } + return min(res, 1.0); } // Calculates Rayleigh phase function given the scattering cosine angle mu // mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r float rayleighPhaseFunction(float mu) { - //return (3.0f / (16.0f * M_PI)) * (1.0f + mu * mu); - return 0.0596831036 * (1.0 + mu * mu); + // return (3.0 / (16.0 * M_PI)) * (1.0 + mu * mu); + return 0.0596831036 * (1.0 + mu * mu); } // Calculates Mie phase function given the scattering cosine angle mu @@ -277,38 +208,30 @@ float miePhaseFunction(float mu, float mieG) { // mu := cosine of the zeith angle of vec(v). Or mu = (vec(x) * vec(v))/r // muSun := cosine of the zeith angle of vec(s). Or muSun = (vec(s) * vec(v)) // nu := cosine of the angle between vec(s) and vec(v) -vec4 texture4D(sampler3D table, float r, float mu, float muSun, float nu) { +vec4 texture4D(sampler3D table, float r, float mu, float muSun, float nu, float Rg, + int samplesMu, float Rt, int samplesR, int samplesMuS, + int samplesNu) +{ float r2 = r * r; + float Rg2 = Rg * Rg; + float Rt2 = Rt * Rt; float rho = sqrt(r2 - Rg2); float rmu = r * mu; float delta = rmu * rmu - r2 + Rg2; vec4 cst = rmu < 0.0 && delta > 0.0 ? - vec4(1.0, 0.0, 0.0, 0.5 - 0.5 * invSamplesMu) : - vec4(-1.0, H2, H, 0.5 + 0.5 * invSamplesMu); + vec4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(samplesMu)) : + vec4(-1.0, Rt2 - Rg2, sqrt(Rt2 - Rg2), 0.5 + 0.5 / float(samplesMu)); - float u_r = 0.5 * invSamplesR + rho / H * (1.0 - invSamplesR); - float u_mu = cst.w + (rmu * cst.x + sqrt(delta + cst.y)) / (rho + cst.z) * (0.5 - invSamplesMu); - float u_mu_s = 0.5 * invSamplesMuS + - (atan(max(muSun, -0.1975) * tan(1.386)) * 0.9090909090909090 + 0.74) * 0.5 * (1.0 - invSamplesMuS); - float lerp = (nu + 1.0) / 2.0 * (float(SAMPLES_NU) - 1.0); - float u_nu = floor(lerp); - lerp = lerp - u_nu; + float u_r = 0.5 / float(samplesR) + rho / sqrt(Rt2 - Rg2) * (1.0 - 1.0 / float(samplesR)); + float u_mu = cst.w + (rmu * cst.x + sqrt(delta + cst.y)) / (rho + cst.z) * (0.5 - 1.0 / samplesMu); + float u_mu_s = 0.5 / float(samplesMuS) + + (atan(max(muSun, -0.1975) * tan(1.386)) * 0.9090909090909090 + 0.74) * 0.5 * (1.0 - 1.0 / float(samplesMuS)); + float t = (nu + 1.0) / 2.0 * (float(samplesNu) - 1.0); + float u_nu = floor(t); + t = t - u_nu; - return texture( - table, vec3((u_nu + u_mu_s) * invSamplesNu, u_mu, u_r)) * (1.0 - lerp) + - texture(table, vec3((u_nu + u_mu_s + 1.0) * invSamplesNu, u_mu, u_r)) * lerp; -} - -// Given the irradiance texture table, the cosine of zenith sun vector and the height of -// the observer (ray's stating point x), calculates the mapping for u_r and u_muSun and -// returns the value in the LUT -// lut := OpenGL texture2D sampler (the irradiance texture deltaE) -// muSun := cosine of the zeith angle of vec(s). Or muSun = (vec(s) * vec(v)) -// r := height of starting point vect(x) -vec3 irradianceLUT(sampler2D lut, float muSun, float r) { - // See Bruneton paper and Coliene to understand the mapping - float u_muSun = (muSun + 0.2) / 1.2; - float u_r = (r - Rg) * invRtMinusRg; - return texture(lut, vec2(u_muSun, u_r)).rgb; + vec4 v1 = texture(table, vec3((u_nu + u_mu_s) / float(samplesNu), u_mu, u_r)); + vec4 v2 = texture(table, vec3((u_nu + u_mu_s + 1.0) / float(samplesNu), u_mu, u_r)); + return mix(v1, v2, t); } diff --git a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl index f0f97c8de1..6c524a5072 100644 --- a/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl +++ b/modules/atmosphere/shaders/atmosphere_deferred_fs.glsl @@ -64,27 +64,37 @@ out vec4 renderTarget; uniform int cullAtmosphere; +uniform float Rg; +uniform float Rt; +uniform float groundRadianceEmission; +uniform float HR; +uniform vec3 betaRayleigh; +uniform float HO; +uniform vec3 betaOzoneExtinction; +uniform float HM; +uniform vec3 betaMieExtinction; +uniform float mieG; +uniform float sunRadiance; +uniform bool ozoneLayerEnabled; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; +uniform sampler2D transmittanceTexture; uniform sampler2D irradianceTexture; uniform sampler3D inscatterTexture; uniform sampler2D mainPositionTexture; uniform sampler2D mainNormalTexture; uniform sampler2D mainColorTexture; - uniform dmat4 inverseModelTransformMatrix; uniform dmat4 modelTransformMatrix; uniform dmat4 viewToWorldMatrix; uniform dmat4 projectionToModelTransformMatrix; - uniform vec4 viewport; uniform vec2 resolution; - uniform dvec3 camPosObj; uniform dvec3 sunDirectionObj; -uniform dvec3 sunWorld; -uniform dvec3 viewDirWorld; -uniform dvec3 sunModel; - /******************************************************************************* ***** ALL CALCULATIONS FOR ECLIPSE ARE IN METERS AND IN WORLD SPACE SYSTEM **** *******************************************************************************/ @@ -92,8 +102,10 @@ uniform dvec3 sunModel; const uint numberOfShadows = 1; struct ShadowRenderingStruct { - double xu, xp; - double rs, rc; + double xu; + double xp; + double rs; + double rc; dvec3 sourceCasterVec; dvec3 casterPositionVec; bool isShadowing; @@ -113,15 +125,15 @@ float calcShadow(ShadowRenderingStruct shadowInfoArray[numberOfShadows], dvec3 p } dvec3 pc = shadowInfoArray[0].casterPositionVec - position; - dvec3 sc_norm = shadowInfoArray[0].sourceCasterVec; - dvec3 pc_proj = dot(pc, sc_norm) * sc_norm; - dvec3 d = pc - pc_proj; + dvec3 scNorm = shadowInfoArray[0].sourceCasterVec; + dvec3 pcProj = dot(pc, scNorm) * scNorm; + dvec3 d = pc - pcProj; float length_d = float(length(d)); - double length_pc_proj = length(pc_proj); + double lengthPcProj = length(pcProj); - float r_p_pi = float(shadowInfoArray[0].rc * (length_pc_proj + shadowInfoArray[0].xp) / shadowInfoArray[0].xp); - float r_u_pi = float(shadowInfoArray[0].rc * (shadowInfoArray[0].xu - length_pc_proj) / shadowInfoArray[0].xu); + float r_p_pi = float(shadowInfoArray[0].rc * (lengthPcProj + shadowInfoArray[0].xp) / shadowInfoArray[0].xp); + float r_u_pi = float(shadowInfoArray[0].rc * (shadowInfoArray[0].xu - lengthPcProj) / shadowInfoArray[0].xu); if (length_d < r_u_pi) { // umbra @@ -142,6 +154,33 @@ float calcShadow(ShadowRenderingStruct shadowInfoArray[numberOfShadows], dvec3 p } } +float opticalDepth(float localH, float r, float mu, float d, float Rg) { + float invH = 1.0 / localH; + float a = sqrt(0.5 * invH * r); + vec2 a01 = a * vec2(mu, mu + d / r); + vec2 a01s = sign(a01); + vec2 a01sq = a01 * a01; + float x = a01s.y > a01s.x ? exp(a01sq.x) : 0.0; + vec2 y = a01s / (2.3193 * abs(a01) + sqrt(1.52 * a01sq + 4.0)) * + vec2(1.0, exp(-d * invH * (d / (2.0 * r) + mu))); + return sqrt(2.0 * M_PI * sqrt(Rt*Rt - Rg*Rg) * r) * exp((Rg-r)*invH) * (x + dot(y, vec2(1.0, -1.0))); +} + +vec3 analyticTransmittance(float r, float mu, float d) { + vec3 ozone = vec3(0.0); + if (ozoneLayerEnabled) { + ozone = betaOzoneExtinction * 0.0000006 * opticalDepth(HO, r, mu, d, Rg); + } + return exp(-betaRayleigh * opticalDepth(HR, r, mu, d, Rg) - ozone - + betaMieExtinction * opticalDepth(HM, r, mu, d, Rg)); +} + +vec3 irradiance(sampler2D s, float r, float muSun) { + float u_r = (r - Rg) / (Rt - Rg); + float u_muSun = (muSun + 0.2) / 1.2; + return texture(s, vec2(u_muSun, u_r)).rgb; +} + ////////////////////////////////////////////////////////////////////////////////////////// // ALL CALCULATIONS FOR ATMOSPHERE ARE KM AND IN WORLD SPACE SYSTEM // ////////////////////////////////////////////////////////////////////////////////////////// @@ -173,10 +212,11 @@ bool atmosphereIntersection(Ray ray, double atmRadius, out double offset, double l2 = dot(l, l); double r2 = atmRadius * atmRadius; // avoiding surface acne + offset = 0.0; + maxLength = 0.0; + // Ray origin (eye position) is behind sphere if ((s < 0.0) && (l2 > r2)) { - offset = 0.0; - maxLength = 0.0; return false; } @@ -184,13 +224,9 @@ bool atmosphereIntersection(Ray ray, double atmRadius, out double offset, // Ray misses atmosphere if (m2 > r2) { - offset = 0.0; - maxLength = 0.0; return false; } - // We already now the ray hits the atmosphere - // If q = 0.0, there is only one intersection double q = sqrt(r2 - m2); @@ -248,9 +284,9 @@ Ray calculateRayRenderableGlobe(vec2 st) { * the reflectance R[L] */ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 v, vec3 s, - float r, out float mu, out vec3 attenuation, vec3 fragPosObj, - out bool groundHit, double maxLength, double pixelDepth, - vec3 spaceColor, float sunIntensity) + float r, vec3 fragPosObj, double maxLength, double pixelDepth, + vec3 spaceColor, float sunIntensity, + out float mu, out vec3 attenuation, out bool groundHit) { const float INTERPOLATION_EPS = 0.004; // precision const from Brunetton @@ -268,7 +304,11 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // I.e. the next line has the scattering light for the "infinite" ray passing through // the atmosphere. If this ray hits something inside the atmosphere, we will subtract // the attenuated scattering light from that path in the current path - vec4 inscatterRadiance = max(texture4D(inscatterTexture, r, mu, muSun, nu), 0.0); + vec4 inscatterRadiance = max( + texture4D(inscatterTexture, r, mu, muSun, nu, Rg, SAMPLES_MU, Rt, SAMPLES_R, + SAMPLES_MU_S, SAMPLES_NU), + 0.0 + ); // After removing the initial path from camera pos to top of atmosphere (for an // observer in the space) we test if the light ray is hitting the atmosphere @@ -286,13 +326,14 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // attenuation = analyticTransmittance(r, mu, t); // JCC: change from analytical to LUT transmittance to avoid // acme on planet surface when looking from far away. (11/02/2017) - attenuation = transmittance(r, mu, t); + attenuation = transmittance(transmittanceTexture, r, mu, t, Rg, Rt); // Here we use the idea of S[L](a->b) = S[L](b->a), and get the S[L](x0, v, s) // Then we calculate S[L] = S[L]|x - T(x, x0)*S[L]|x0 // The "infinite" ray hist something inside the atmosphere, so we need to remove // the unsused contribution to the final radiance. - vec4 inscatterFromSurface = texture4D(inscatterTexture, r0, mu0, muSun0, nu); + vec4 inscatterFromSurface = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg, + SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU); inscatterRadiance = max(inscatterRadiance - attenuation.rgbr * inscatterFromSurface, 0.0); // We set the irradianceFactor to 1.0 so the reflected irradiance will be considered @@ -307,7 +348,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // cos(PI-thetaH) = dist/r // cos(thetaH) = -dist/r // muHorizon = -sqrt(r^2-Rg^2)/r = -sqrt(1-(Rg/r)^2) - float muHorizon = -sqrt(1.0 - Rg2 / r2); + float muHorizon = -sqrt(1.0 - Rg*Rg / r2); // In order to avoid precision problems near horizon, we interpolate between two // points: above and below horizon @@ -318,10 +359,10 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // Above Horizon mu = muHorizon - INTERPOLATION_EPS; - // r0 = sqrt(r * r + t * t + 2.0f * r * t * mu); + // r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); // From cosine law where t = distance between x and x0 // r0^2 = r^2 + t^2 - 2 * r * t * cos(PI-theta) - // r0 = sqrt(r2 + t2 + 2.0f * r * t * mu); + // r0 = sqrt(r2 + t2 + 2.0 * r * t * mu); float halfCosineLaw1 = r2 + (t * t); float halfCosineLaw2 = 2.0 * r * t; r0 = sqrt(halfCosineLaw1 + halfCosineLaw2 * mu); @@ -332,20 +373,24 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // mu0 = (r*mu + t) / r0 mu0 = (r * mu + t) * (1.0 / r0); - vec4 inScatterAboveX = texture4D(inscatterTexture, r, mu, muSun, nu); - vec4 inScatterAboveXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu); + vec4 inScatterAboveX = texture4D(inscatterTexture, r, mu, muSun, nu, Rg, + SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU); + vec4 inScatterAboveXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg, + SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU); // Attention for the attenuation.r value applied to the S_Mie vec4 inScatterAbove = max(inScatterAboveX - attenuation.rgbr * inScatterAboveXs, 0.0); // Below Horizon mu = muHorizon + INTERPOLATION_EPS; - //r0 = sqrt(r2 + t2 + 2.0f * r * t * mu); + //r0 = sqrt(r2 + t2 + 2.0 * r * t * mu); r0 = sqrt(halfCosineLaw1 + halfCosineLaw2 * mu); mu0 = (r * mu + t) * (1.0 / r0); - vec4 inScatterBelowX = texture4D(inscatterTexture, r, mu, muSun, nu); - vec4 inScatterBelowXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu); + vec4 inScatterBelowX = texture4D(inscatterTexture, r, mu, muSun, nu, Rg, + SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU); + vec4 inScatterBelowXs = texture4D(inscatterTexture, r0, mu0, muSun0, nu, Rg, + SAMPLES_MU, Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU); // Attention for the attenuation.r value applied to the S_Mie vec4 inScatterBelow = max(inScatterBelowX - attenuation.rgbr * inScatterBelowXs, 0.0); @@ -370,13 +415,7 @@ vec3 inscatterRadiance(vec3 x, inout float t, inout float irradianceFactor, vec3 // Finally we add the Lsun (all calculations are done with no Lsun so we can change it // on the fly with no precomputations) vec3 finalScatteringRadiance = radiance * sunIntensity; - - if (groundHit) { - return finalScatteringRadiance; - } - else { - return spaceColor + finalScatteringRadiance; - } + return groundHit ? finalScatteringRadiance : spaceColor + finalScatteringRadiance; } /* @@ -401,8 +440,6 @@ vec3 groundColor(vec3 x, float t, vec3 v, vec3 s, vec3 attenuationXtoX0, vec3 gr vec3 normal, float irradianceFactor, float waterReflectance, float sunIntensity) { - vec3 reflectedRadiance = vec3(0.0); - // First we obtain the ray's end point on the surface float r0 = length(x + t * v); @@ -415,7 +452,7 @@ vec3 groundColor(vec3 x, float t, vec3 v, vec3 s, vec3 attenuationXtoX0, vec3 gr // Is direct Sun light arriving at x0? If not, there is no direct light from Sun (shadowed) vec3 transmittanceL0 = - muSun < -sqrt(1.0 - (Rg2 / (r0 * r0))) ? vec3(0.0) : transmittance(r0, muSun); + muSun < -sqrt(1.0 - (Rg*Rg / (r0 * r0))) ? vec3(0.0) : transmittance(transmittanceTexture, r0, muSun, Rg, Rt); // E[L*] at x0 vec3 irradianceReflected = irradiance(irradianceTexture, r0, muSun) * irradianceFactor; @@ -433,16 +470,14 @@ vec3 groundColor(vec3 x, float t, vec3 v, vec3 s, vec3 attenuationXtoX0, vec3 gr // Fresnell Schlick's approximation float fresnel = 0.02 + 0.98 * pow(1.0 - dot(-v, h), 5.0); // Walter BRDF approximation - float waterBrdf = fresnel * pow(max(dot(h, normal), 0.0), 150.0); + float waterBrdf = max(fresnel * pow(max(dot(h, normal), 0.0), 150.0), 0.0); // Adding Fresnell and Water BRDFs approximation to the final surface color // after adding the sunRadiance and the attenuation of the Sun through atmosphere - groundRadiance += waterReflectance * max(waterBrdf, 0.0) * transmittanceL0 * sunIntensity; + groundRadiance += waterReflectance * waterBrdf * transmittanceL0 * sunIntensity; } // Finally, we attenuate the surface Radiance from the point x0 to the camera location - reflectedRadiance = attenuationXtoX0 * groundRadiance; - - // Returns reflectedRadiance = 0.0 if the ray doesn't hit the ground. + vec3 reflectedRadiance = attenuationXtoX0 * groundRadiance; return reflectedRadiance; } @@ -464,7 +499,7 @@ vec3 sunColor(vec3 v, vec3 s, float r, float mu, float irradianceFactor) { // JCC: Change this function to a impostor texture with gaussian decay color weighted // by the sunRadiance, transmittance and irradianceColor (11/03/2017) - // @TODO (abock, 2021-07-01) This value is hard-coded to our sun right now + // @TODO (abock, 2021-07-01) This value is hard-coded to our sun+earth right now // Convert 0.3 degrees -> radians const float SunAngularSize = (0.3 * M_PI / 180.0); const float FuzzyFactor = 0.5; // How fuzzy should the edges be @@ -474,7 +509,7 @@ vec3 sunColor(vec3 v, vec3 s, float r, float mu, float irradianceFactor) { float t = (angle - p1) / (p2 - p1); float scale = clamp(t, 0.0, 1.0); - return scale * transmittance(r, mu) * sunRadiance * (1.0 - irradianceFactor); + return scale * transmittance(transmittanceTexture, r, mu, Rg, Rt) * sunRadiance * (1.0 - irradianceFactor); } void main() { @@ -588,24 +623,22 @@ void main() { bool groundHit = false; vec3 attenuation; - vec3 inscatterColor = inscatterRadiance(x, tF, irradianceFactor, v, s, r, mu, - attenuation, vec3(positionObjectsCoords), groundHit, maxLength, pixelDepth, - color, sunIntensityInscatter); + vec3 inscatterColor = inscatterRadiance(x, tF, irradianceFactor, v, s, r, + vec3(positionObjectsCoords), maxLength, pixelDepth, color, sunIntensityInscatter, mu, + attenuation, groundHit); vec3 atmColor = vec3(0.0); if (groundHit) { float eclipseShadowPlanet = calcShadow(shadowDataArray, positionWorldCoords.xyz, true); float sunIntensityGround = sunRadiance * eclipseShadowPlanet; - atmColor = groundColor(x, tF, v, s, attenuation, color, normal.xyz, - irradianceFactor, normal.w, sunIntensityGround); + atmColor = groundColor(x, tF, v, s, attenuation, color, normal.xyz, irradianceFactor, + normal.w, sunIntensityGround); } else { // In order to get better performance, we are not tracing multiple rays per pixel // when the ray doesn't intersect the ground - atmColor = sunColor(v, s, r, mu, irradianceFactor); } // Final Color of ATM plus terrain: - vec4 finalRadiance = vec4(inscatterColor + atmColor, 1.0); - renderTarget = finalRadiance; + renderTarget = vec4(inscatterColor + atmColor, 1.0);; } diff --git a/modules/atmosphere/shaders/calculation_vs.glsl b/modules/atmosphere/shaders/calculation_vs.glsl index be225fa6a6..a7d480b829 100644 --- a/modules/atmosphere/shaders/calculation_vs.glsl +++ b/modules/atmosphere/shaders/calculation_vs.glsl @@ -24,8 +24,8 @@ #version __CONTEXT__ -layout(location = 0) in vec3 in_position; +layout(location = 0) in vec2 in_position; void main() { - gl_Position = vec4(in_position, 1.0); + gl_Position = vec4(in_position, 0.0, 1.0); } diff --git a/modules/atmosphere/shaders/deltaE_calc_fs.glsl b/modules/atmosphere/shaders/deltaE_calc_fs.glsl index fe0163b6b2..117bd0a631 100644 --- a/modules/atmosphere/shaders/deltaE_calc_fs.glsl +++ b/modules/atmosphere/shaders/deltaE_calc_fs.glsl @@ -24,8 +24,6 @@ #version __CONTEXT__ -#include "atmosphere_common.glsl" - out vec4 renderTableColor; void main() { diff --git a/modules/atmosphere/shaders/deltaJ_calc_fs.glsl b/modules/atmosphere/shaders/deltaJ_calc_fs.glsl index 37bacbe3b9..044ea1de30 100644 --- a/modules/atmosphere/shaders/deltaJ_calc_fs.glsl +++ b/modules/atmosphere/shaders/deltaJ_calc_fs.glsl @@ -28,19 +28,45 @@ out vec4 renderTarget; +uniform float Rg; +uniform float Rt; +uniform float AverageGroundReflectance; +uniform float HR; +uniform vec3 betaRayleigh; +uniform float HM; +uniform vec3 betaMieScattering; +uniform float mieG; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; +uniform sampler2D transmittanceTexture; uniform float r; uniform vec4 dhdH; - uniform sampler2D deltaETexture; uniform sampler3D deltaSRTexture; uniform sampler3D deltaSMTexture; +uniform int firstIteration; -uniform int firstIteraction; +const int INSCATTER_SPHERICAL_INTEGRAL_SAMPLES = 16; // -- Spherical Coordinates Steps. phi e [0,2PI] and theta e [0, PI] const float stepPhi = (2.0 * M_PI) / float(INSCATTER_SPHERICAL_INTEGRAL_SAMPLES); const float stepTheta = M_PI / float(INSCATTER_SPHERICAL_INTEGRAL_SAMPLES); +// Given the irradiance texture table, the cosine of zenith sun vector and the height of +// the observer (ray's stating point x), calculates the mapping for u_r and u_muSun and +// returns the value in the LUT +// lut := OpenGL texture2D sampler (the irradiance texture deltaE) +// muSun := cosine of the zeith angle of vec(s). Or muSun = (vec(s) * vec(v)) +// r := height of starting point vect(x) +vec3 irradianceLUT(sampler2D lut, float muSun, float r) { + // See Bruneton paper and Coliene to understand the mapping + float u_muSun = (muSun + 0.2) / 1.2; + float u_r = (r - Rg) / (Rt - Rg); + return texture(lut, vec2(u_muSun, u_r)).rgb; +} + vec3 inscatter(float r, float mu, float muSun, float nu) { // Be sure to not get a cosine or height out of bounds r = clamp(r, Rg, Rt); @@ -59,7 +85,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) { float muSun2 = muSun * muSun; float sinThetaSinSigma = sqrt(1.0 - mu2) * sqrt(1.0 - muSun2); // cos(sigma + theta) = cos(theta)cos(sigma)-sin(theta)sin(sigma) - // cos(ni) = nu = mu * muSun - sqrt(1.0f - mu*mu)*sqrt(1.0 - muSun*muSun) // sin(theta) = sqrt(1.0 - mu*mu) + // cos(ni) = nu = mu * muSun - sqrt(1.0 - mu*mu)*sqrt(1.0 - muSun*muSun) // sin(theta) = sqrt(1.0 - mu*mu) // Now we make sure the angle between vec(s) and vec(v) is in the right range: nu = clamp(nu, muSun * mu - sinThetaSinSigma, muSun * mu + sinThetaSinSigma); @@ -69,7 +95,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) { // -cos(theta) = sqrt(r*r-Rg*Rg)/r float Rg2 = Rg * Rg; float r2 = r * r; - float cosHorizon = -sqrt(r2 - Rg2)/r; + float cosHorizon = -sqrt(r2 - Rg2) / r; // Now we get vec(v) and vec(s) from mu, muSun and nu: // Assuming: @@ -97,6 +123,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) { // In order to integrate over 4PI, we scan the sphere using the spherical coordinates // previously defined + vec3 radianceJAcc = vec3(0.0); for (int theta_i = 0; theta_i < INSCATTER_SPHERICAL_INTEGRAL_SAMPLES; theta_i++) { float theta = (float(theta_i) + 0.5) * stepTheta; float cosineTheta = cos(theta); @@ -131,7 +158,7 @@ vec3 inscatter(float r, float mu, float muSun, float nu) { // float muGround = (r2 - distanceToGround*distanceToGround - Rg2)/(2*distanceToGround*Rg); // Access the Transmittance LUT in order to calculate the transmittance from the // ground point Rg, thorugh the atmosphere, at a distance: distanceToGround - groundTransmittance = transmittance(Rg, muGround, distanceToGround); + groundTransmittance = transmittance(transmittanceTexture, Rg, muGround, distanceToGround, Rg, Rt); } for (int phi_i = 0; phi_i < INSCATTER_SPHERICAL_INTEGRAL_SAMPLES; ++phi_i) { @@ -161,43 +188,48 @@ vec3 inscatter(float r, float mu, float muSun, float nu) { // We calculate the Rayleigh and Mie phase function for the new scattering angle: // cos(angle between vec(s) and vec(w)), ||s|| = ||w|| = 1 float nuSW = dot(s, w); - // The first iteraction is different from the others. In the first iteraction all + // The first iteration is different from the others. In the first iteration all // the light InScattered is coming from the initial pre-computed single InScattered // light. We stored these values in the deltaS textures (Ray and Mie), and in order // to avoid problems with the high angle dependency in the phase functions, we don't // include the phase functions on those tables (that's why we calculate them now). - if (firstIteraction == 1) { + if (firstIteration == 1) { float phaseRaySW = rayleighPhaseFunction(nuSW); float phaseMieSW = miePhaseFunction(nuSW, mieG); // We can now access the values for the single InScattering in the textures deltaS textures. - vec3 singleRay = texture4D(deltaSRTexture, r, w.z, muSun, nuSW).rgb; - vec3 singleMie = texture4D(deltaSMTexture, r, w.z, muSun, nuSW).rgb; + vec3 singleRay = texture4D(deltaSRTexture, r, w.z, muSun, nuSW, Rg, SAMPLES_MU, + Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb; + vec3 singleMie = texture4D(deltaSMTexture, r, w.z, muSun, nuSW, Rg, SAMPLES_MU, + Rt, SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb; // Initial InScattering including the phase functions radianceJ1 += singleRay * phaseRaySW + singleMie * phaseMieSW; } else { // On line 9 of the algorithm, the texture table deltaSR is updated, so when we - // are not in the first iteraction, we are getting the updated result of deltaSR + // are not in the first iteration, we are getting the updated result of deltaSR // (not the single inscattered light but the accumulated (higher order) // inscattered light. // w.z is the cosine(theta) = mu for vec(w) - radianceJ1 += texture4D(deltaSRTexture, r, w.z, muSun, nuSW).rgb; + radianceJ1 += texture4D(deltaSRTexture, r, w.z, muSun, nuSW, Rg, SAMPLES_MU, Rt, + SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb; } // Finally, we add the atmospheric scale height (See: Radiation Transfer on the // Atmosphere and Ocean from Thomas and Stamnes, pg 9-10. - return radianceJ1 * (betaRayleigh * exp(-(r - Rg) / HR) * phaseRayleighWV + + radianceJAcc += radianceJ1 * (betaRayleigh * exp(-(r - Rg) / HR) * phaseRayleighWV + betaMieScattering * exp(-(r - Rg) / HM) * phaseMieWV) * dw; } } + + return radianceJAcc; } void main() { // InScattering Radiance to be calculated at different points in the ray path // Unmapping the variables from texture texels coordinates to mapped coordinates float mu, muSun, nu; - unmappingMuMuSunNu(r, dhdH, mu, muSun, nu); + unmappingMuMuSunNu(r, dhdH, SAMPLES_MU, Rg, Rt, SAMPLES_MU_S, SAMPLES_NU, mu, muSun, nu); // Calculate the the light inScattered in direction // -vec(v) for the point at height r (vec(y) following Bruneton and Neyret's paper diff --git a/modules/atmosphere/shaders/deltaS_calc_fs.glsl b/modules/atmosphere/shaders/deltaS_calc_fs.glsl index b5d7de5e79..ca97f15ff9 100644 --- a/modules/atmosphere/shaders/deltaS_calc_fs.glsl +++ b/modules/atmosphere/shaders/deltaS_calc_fs.glsl @@ -28,6 +28,10 @@ out vec4 renderTarget; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; uniform int layer; uniform sampler3D deltaSRTexture; uniform sampler3D deltaSMTexture; diff --git a/modules/atmosphere/shaders/deltaS_sup_calc_fs.glsl b/modules/atmosphere/shaders/deltaS_sup_calc_fs.glsl index ebe63b5071..7580c790be 100644 --- a/modules/atmosphere/shaders/deltaS_sup_calc_fs.glsl +++ b/modules/atmosphere/shaders/deltaS_sup_calc_fs.glsl @@ -28,6 +28,10 @@ out vec4 renderTarget; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; uniform int layer; uniform sampler3D deltaSTexture; @@ -35,10 +39,9 @@ void main() { vec2 p = gl_FragCoord.xy - vec2(0.5); float nu = -1.0 + floor(p.x / float(SAMPLES_MU_S)) / (float(SAMPLES_NU) - 1.0) * 2.0; - vec3 uvw = vec3( - gl_FragCoord.xy, - float(layer) + 0.5) / vec3(ivec3(SAMPLES_MU_S * SAMPLES_NU, SAMPLES_MU, SAMPLES_R) - ); + vec3 uvw = + vec3(gl_FragCoord.xy, float(layer) + 0.5) / + vec3(ivec3(SAMPLES_MU_S * SAMPLES_NU, SAMPLES_MU, SAMPLES_R)); // See Bruneton and Neyret paper, "Angular Precision" paragraph to understanding why we // are dividing the S[L*] by the Rayleigh phase function. diff --git a/modules/atmosphere/shaders/inScattering_calc_fs.glsl b/modules/atmosphere/shaders/inScattering_calc_fs.glsl index 8443cb1109..94c7aabcac 100644 --- a/modules/atmosphere/shaders/inScattering_calc_fs.glsl +++ b/modules/atmosphere/shaders/inScattering_calc_fs.glsl @@ -29,6 +29,18 @@ layout(location = 0) out vec4 renderTarget1; layout(location = 1) out vec4 renderTarget2; +uniform float Rg; +uniform float Rt; +uniform float HR; +uniform vec3 betaRayleigh; +uniform float HO; +uniform float HM; +uniform vec3 betaMieScattering; +uniform bool ozoneLayerEnabled; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; +uniform sampler2D transmittanceTexture; uniform float r; uniform vec4 dhdH; @@ -60,7 +72,9 @@ void integrand(float r, float mu, float muSun, float nu, float y, out vec3 S_R, if (muSun_i >= -sqrt(1.0 - Rg * Rg / (ri * ri))) { // It's the transmittance from the point y (ri) to the top of atmosphere in direction // of the sun (muSun_i) and the transmittance from the observer at x (r) to y (ri). - vec3 transmittanceY = transmittance(r, mu, y) * transmittance(ri, muSun_i); + vec3 transmittanceY = + transmittance(transmittanceTexture, r, mu, y, Rg, Rt) * + transmittance(transmittanceTexture, ri, muSun_i, Rg, Rt); // exp(-h/H)*T(x,v) if (ozoneLayerEnabled) { S_R = (exp(-(ri - Rg) / HO) + exp(-(ri - Rg) / HR)) * transmittanceY; @@ -83,7 +97,7 @@ void inscatter(float r, float mu, float muSun, float nu, out vec3 S_R, out vec3 S_R = vec3(0.0); S_M = vec3(0.0); - float rayDist = rayDistance(r, mu); + float rayDist = rayDistance(r, mu, Rt, Rg); float dy = rayDist / float(INSCATTER_INTEGRAL_SAMPLES); vec3 S_Ri; vec3 S_Mi; @@ -103,13 +117,10 @@ void inscatter(float r, float mu, float muSun, float nu, out vec3 S_R, out vec3 } void main() { - vec3 S_R; // First Order Rayleigh InScattering - vec3 S_M; // First Order Mie InScattering - // From the layer interpolation (see C++ code for layer to r) and the textures // parameters (uv), we unmapping mu, muSun and nu. float mu, muSun, nu; - unmappingMuMuSunNu(r, dhdH, mu, muSun, nu); + unmappingMuMuSunNu(r, dhdH, SAMPLES_MU, Rg, Rt, SAMPLES_MU_S, SAMPLES_NU, mu, muSun, nu); // Here we calculate the single inScattered light. Because this is a single // inscattering, the light that arrives at a point y in the path from the eye to the @@ -122,6 +133,8 @@ void main() { // S[L0] = P_R*S_R[L0] + P_M*S_M[L0] // In order to save memory, we just store the red component of S_M[L0], and later we use // the proportionality rule to calcule the other components. + vec3 S_R; // First Order Rayleigh InScattering + vec3 S_M; // First Order Mie InScattering inscatter(r, mu, muSun, nu, S_R, S_M); renderTarget1 = vec4(S_R, 1.0); renderTarget2 = vec4(S_M, 1.0); diff --git a/modules/atmosphere/shaders/inScattering_sup_calc_fs.glsl b/modules/atmosphere/shaders/inScattering_sup_calc_fs.glsl index fd128b3b16..c84ffff774 100644 --- a/modules/atmosphere/shaders/inScattering_sup_calc_fs.glsl +++ b/modules/atmosphere/shaders/inScattering_sup_calc_fs.glsl @@ -28,6 +28,13 @@ out vec4 renderTarget; +uniform float Rg; +uniform float Rt; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; +uniform sampler2D transmittanceTexture; uniform float r; uniform vec4 dhdH; uniform sampler3D deltaJTexture; @@ -35,7 +42,7 @@ uniform sampler3D deltaJTexture; // The integrand here is the f(y) of the trapezoidal rule: vec3 integrand(float r, float mu, float muSun, float nu, float dist) { // We can calculate r_i by the cosine law: r_i^2=dist^2 + r^2 - 2*r*dist*cos(PI-theta) - float r_i = sqrt(r * r + dist * dist + 2.0f * r * dist * mu); + float r_i = sqrt(r * r + dist * dist + 2.0 * r * dist * mu); // r_i can be found using the dot product: // vec(y_i) dot vec(dist) = cos(theta_i) * ||vec(y_i)|| * ||vec(dist)|| // But vec(y_i) = vec(x) + vec(dist), also: vec(x) dot vec(dist) = cos(theta) = mu @@ -46,12 +53,15 @@ vec3 integrand(float r, float mu, float muSun, float nu, float dist) { // But vec(y_i) = vec(x) + vec(dist), and vec(x) dot vec(s) = muSun, cos(sigma_i + theta_i) = nu float muSun_i = (r * muSun + dist * nu) / r_i; // The irradiance attenuated from point r until y (y-x = dist) - return transmittance(r, mu, dist) * texture4D(deltaJTexture, r_i, mu_i, muSun_i, nu).rgb; + return + transmittance(transmittanceTexture, r, mu, dist, Rg, Rt) * + texture4D(deltaJTexture, r_i, mu_i, muSun_i, nu, Rg, SAMPLES_MU, Rt, SAMPLES_R, + SAMPLES_MU_S, SAMPLES_NU).rgb; } vec3 inscatter(float r, float mu, float muSun, float nu) { vec3 inScatteringRadiance = vec3(0.0); - float dy = rayDistance(r, mu) / float(INSCATTER_INTEGRAL_SAMPLES); + float dy = rayDistance(r, mu, Rt, Rg) / float(INSCATTER_INTEGRAL_SAMPLES); vec3 inScatteringRadiance_i = integrand(r, mu, muSun, nu, 0.0); // In order to solve the integral from equation (11) we use the trapezoidal rule: @@ -71,7 +81,7 @@ void main() { float muSun = 0.0; float nu = 0.0; // Unmapping the variables from texture texels coordinates to mapped coordinates - unmappingMuMuSunNu(r, dhdH, mu, muSun, nu); + unmappingMuMuSunNu(r, dhdH, SAMPLES_MU, Rg, Rt, SAMPLES_MU_S, SAMPLES_NU, mu, muSun, nu); // Write to texture deltaSR renderTarget = vec4(inscatter(r, mu, muSun, nu), 1.0); diff --git a/modules/atmosphere/shaders/irradiance_calc_fs.glsl b/modules/atmosphere/shaders/irradiance_calc_fs.glsl index 93fca5128e..4e31d608b1 100644 --- a/modules/atmosphere/shaders/irradiance_calc_fs.glsl +++ b/modules/atmosphere/shaders/irradiance_calc_fs.glsl @@ -28,16 +28,24 @@ out vec4 renderTableColor; -void main() { - // See Bruneton and Colliene to understand the mapping - float muSun = -0.2 + (gl_FragCoord.x - 0.5) / (float(OTHER_TEXTURES.x) - 1.0) * 1.2; - float r = Rg + (gl_FragCoord.y - 0.5) / (float(OTHER_TEXTURES.y) ) * RtMinusRg; +uniform float Rg; +uniform float Rt; +uniform ivec2 OTHER_TEXTURES; +uniform sampler2D transmittanceTexture; - // We are calculating the Irradiance for L0, i.e., only the radiance coming from Sun - // direction is accounted: +void main() { + // See Bruneton and Collienne to understand the mapping + float muSun = -0.2 + (gl_FragCoord.x - 0.5) / (float(OTHER_TEXTURES.x) - 1.0) * 1.2; + float r = Rg + (gl_FragCoord.y - 0.5) / (float(OTHER_TEXTURES.y)) * (Rt - Rg); + + // We are calculating the Irradiance for L0, i.e., only the radiance coming from the Sun + // direction is accounted for: // E[L0](x,s) = L0*dot(w,n) or 0 (if v!=s or the sun is occluded). - // Because we consider the Planet as a perfect sphere and we are considering only single + // Because we consider the planet as a perfect sphere and we are considering only single // scattering here, the dot product dot(w,n) is equal to dot(s,n) that is equal to // dot(s, r/||r||) = muSun. - renderTableColor = vec4(transmittance(r, muSun) * max(muSun, 0.0), 0.0); + renderTableColor = vec4( + transmittance(transmittanceTexture, r, muSun, Rg, Rt) * max(muSun, 0.0), + 0.0 + ); } diff --git a/modules/atmosphere/shaders/irradiance_final_fs.glsl b/modules/atmosphere/shaders/irradiance_final_fs.glsl index 5f9834ab2c..6640cc2f6a 100644 --- a/modules/atmosphere/shaders/irradiance_final_fs.glsl +++ b/modules/atmosphere/shaders/irradiance_final_fs.glsl @@ -24,10 +24,9 @@ #version __CONTEXT__ -#include "atmosphere_common.glsl" - out vec4 renderTableColor; +uniform ivec2 OTHER_TEXTURES; uniform sampler2D deltaETexture; void main() { diff --git a/modules/atmosphere/shaders/irradiance_sup_calc_fs.glsl b/modules/atmosphere/shaders/irradiance_sup_calc_fs.glsl index 06b31090b8..5c3f29452e 100644 --- a/modules/atmosphere/shaders/irradiance_sup_calc_fs.glsl +++ b/modules/atmosphere/shaders/irradiance_sup_calc_fs.glsl @@ -28,22 +28,32 @@ out vec4 renderTableColor; -uniform int firstIteraction; +uniform float Rg; +uniform float Rt; +uniform float mieG; +uniform ivec2 SKY; +uniform int SAMPLES_R; +uniform int SAMPLES_MU; +uniform int SAMPLES_MU_S; +uniform int SAMPLES_NU; +uniform int firstIteration; uniform sampler3D deltaSRTexture; uniform sampler3D deltaSMTexture; +const int IRRADIANCE_INTEGRAL_SAMPLES = 32; + // Spherical Coordinates Steps. phi e [0,2PI] and theta e [0, PI/2] const float stepPhi = (2.0 * M_PI) / float(IRRADIANCE_INTEGRAL_SAMPLES); const float stepTheta = M_PI / (2.0 * float(IRRADIANCE_INTEGRAL_SAMPLES)); void main() { - // See Bruneton and Colliene to understand the mapping. + // See Bruneton and Collienne to understand the mapping. float muSun = -0.2 + (gl_FragCoord.x - 0.5) / (float(SKY.x) - 1.0) * 1.2; - float r = Rg + (gl_FragCoord.y - 0.5) / (float(SKY.y) - 1.0) * RtMinusRg; + float r = Rg + (gl_FragCoord.y - 0.5) / (float(SKY.y) - 1.0) * (Rt - Rg); // We know that muSun = cos(sigma) = s.z/||s|| // But, ||s|| = 1, so s.z = muSun. Also, - // ||s|| = 1, so s.x = sin(sigma) = sqrt(1-muSun^2) and s.y = 0.0f + // ||s|| = 1, so s.x = sin(sigma) = sqrt(1-muSun^2) and s.y = 0.0 vec3 s = vec3(max(sqrt(1.0 - muSun * muSun), 0.0), 0.0, muSun); // In order to solve the integral from equation (15) we use the trapezoidal rule: @@ -60,30 +70,32 @@ void main() { vec3 w = vec3(cos(phi) * sin(theta), sin(phi) * sin(theta), cos(theta)); float nu = dot(s, w); - // The first iteraction is different from the others, that's because in the first - // iteraction all the light arriving are coming from the initial pre-computed - // single scattered light. We stored these values in the deltaS textures (Ray and - // Mie), and in order to avoid problems with the high angle dependency in the phase - // functions, we don't include the phase functions on those tables (that's why we - // calculate them now) - if (firstIteraction == 1) { + // The first iteration is different from the others as in the first iteration all + // the light arriving is coming from the initial pre-computed single scattered + // light. We stored these values in the deltaS textures (Ray and Mie), and in order + // to avoid problems with the high angle dependency in the phase functions, we don't + // include the phase functions on those tables (that's why we calculate them now) + if (firstIteration == 1) { float phaseRay = rayleighPhaseFunction(nu); float phaseMie = miePhaseFunction(nu, mieG); - vec3 singleRay = texture4D(deltaSRTexture, r, w.z, muSun, nu).rgb; - vec3 singleMie = texture4D(deltaSMTexture, r, w.z, muSun, nu).rgb; + vec3 singleRay = texture4D(deltaSRTexture, r, w.z, muSun, nu, Rg, SAMPLES_MU, Rt, + SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb; + vec3 singleMie = texture4D(deltaSMTexture, r, w.z, muSun, nu, Rg, SAMPLES_MU, Rt, + SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb; // w.z is the cosine(theta) = mu for vec(w) and also vec(w) dot vec(n(xo)) irradianceE += (singleRay * phaseRay + singleMie * phaseMie) * w.z * dw; } else { // On line 10 of the algorithm, the texture table deltaE is updated, so when we - // are not in the first iteraction, we are getting the updated result of deltaE + // are not in the first iteration, we are getting the updated result of deltaE // (not the single irradiance light but the accumulated (higher order) irradiance // light. w.z is the cosine(theta) = mu for vec(w) and also vec(w) dot vec(n(xo)) - irradianceE += texture4D(deltaSRTexture, r, w.z, muSun, nu).rgb * w.z * dw; + irradianceE += texture4D(deltaSRTexture, r, w.z, muSun, nu, Rg, SAMPLES_MU, Rt, + SAMPLES_R, SAMPLES_MU_S, SAMPLES_NU).rgb * w.z * dw; } } } - // Write the higher oder irradiance to texture deltaE + // Write the higher order irradiance to texture deltaE renderTableColor = vec4(irradianceE, 0.0); } diff --git a/modules/atmosphere/shaders/transmittance_calc_fs.glsl b/modules/atmosphere/shaders/transmittance_calc_fs.glsl index cf78347f29..a0dd54f6f2 100644 --- a/modules/atmosphere/shaders/transmittance_calc_fs.glsl +++ b/modules/atmosphere/shaders/transmittance_calc_fs.glsl @@ -28,6 +28,19 @@ out vec4 renderTableColor; +uniform float Rg; +uniform float Rt; +uniform float HR; +uniform vec3 betaRayleigh; +uniform float HO; +uniform vec3 betaOzoneExtinction; +uniform float HM; +uniform vec3 betaMieExtinction; +uniform bool ozoneLayerEnabled; +uniform ivec2 TRANSMITTANCE; + +const int TRANSMITTANCE_STEPS = 500; + // Optical depth by integration, from ray starting at point vec(x), i.e, height r and // angle mu (cosine of vec(v)) until top of atmosphere or planet's ground. // r := height of starting point vect(x) @@ -42,14 +55,14 @@ float opticalDepth(float r, float mu, float H) { // direction and starting and ending points. // cosine law for triangles: y_i^2 = a^2 + b^2 - 2abcos(alpha) - float cosZenithHorizon = -sqrt(1.0 - (Rg * Rg / r2)); + float cosZenithHorizon = -sqrt(1.0 - ((Rg * Rg) / r2)); if (mu < cosZenithHorizon) { return 1e9; } // Integrating using the Trapezoidal rule: // Integral(f(y)dy)(from a to b) = ((b-a)/2n_steps)*(Sum(f(y_i+1)+f(y_i))) - float b_a = rayDistance(r, mu); + float b_a = rayDistance(r, mu, Rt, Rg); float deltaStep = b_a / float(TRANSMITTANCE_STEPS); // cosine law float y_i = exp(-(r - Rg) / H); @@ -72,11 +85,11 @@ void main() { // In the paper u_r^2 = (r^2-Rg^2)/(Rt^2-Rg^2) // So, extracting r from u_r in the above equation: - float r = Rg + (u_r * u_r) * RtMinusRg; + float r = Rg + (u_r * u_r) * (Rt - Rg); // In the paper the Bruneton suggest mu = dot(v,x)/||x|| with ||v|| = 1.0 // Later he proposes u_mu = (1-exp(-3mu-0.6))/(1-exp(-3.6)) - // But the below one is better. See Colliene. + // But the below one is better. See Collienne. // One must remember that mu is defined from 0 to PI/2 + epsilon float muSun = -0.15 + tan(1.5 * u_mu) / tan(1.5) * 1.15; diff --git a/src/rendering/framebufferrenderer.cpp b/src/rendering/framebufferrenderer.cpp index 7c93764d9f..1a92b2f781 100644 --- a/src/rendering/framebufferrenderer.cpp +++ b/src/rendering/framebufferrenderer.cpp @@ -1067,7 +1067,6 @@ void FramebufferRenderer::updateDeferredcastData() { std::filesystem::path vsPath = caster->deferredcastVSPath(); std::filesystem::path fsPath = caster->deferredcastFSPath(); - std::filesystem::path deferredShaderPath = caster->deferredcastPath(); ghoul::Dictionary dict; dict.setValue("rendererData", _rendererData); @@ -1086,17 +1085,10 @@ void FramebufferRenderer::updateDeferredcastData() { _deferredcastPrograms[caster] = ghoul::opengl::ProgramObject::Build( "Deferred " + std::to_string(data.id) + " raycast", vsPath, - deferredShaderPath, + fsPath, dict ); - _deferredcastPrograms[caster]->setIgnoreSubroutineUniformLocationError( - ghoul::opengl::ProgramObject::IgnoreError::Yes - ); - _deferredcastPrograms[caster]->setIgnoreUniformLocationError( - ghoul::opengl::ProgramObject::IgnoreError::Yes - ); - caster->initializeCachedVariables(*_deferredcastPrograms[caster]); } catch (ghoul::RuntimeError& e) {