From ca8bdf86a6351693ffc9655842dc09fac16e4bd7 Mon Sep 17 00:00:00 2001 From: benpm Date: Wed, 9 Jul 2025 15:01:21 -0600 Subject: [PATCH] WIP: getting a more or less correct ring texture coordinate! --- .../shaders/globalrenderer_vs.glsl | 2 + .../shaders/localrenderer_vs.glsl | 2 + .../globebrowsing/shaders/renderer_fs.glsl | 64 +++++++------- modules/globebrowsing/src/renderableglobe.cpp | 85 +++++++++++++------ 4 files changed, 96 insertions(+), 57 deletions(-) diff --git a/modules/globebrowsing/shaders/globalrenderer_vs.glsl b/modules/globebrowsing/shaders/globalrenderer_vs.glsl index 685b3cfb3b..bef1f3f0cd 100644 --- a/modules/globebrowsing/shaders/globalrenderer_vs.glsl +++ b/modules/globebrowsing/shaders/globalrenderer_vs.glsl @@ -38,6 +38,7 @@ out vec3 ellipsoidNormalCameraSpace; out vec3 levelWeights; out vec3 positionCameraSpace; out vec3 positionWorldSpace; +out vec3 posObjSpace; #if USE_ACCURATE_NORMALS out vec3 ellipsoidTangentThetaCameraSpace; @@ -133,6 +134,7 @@ void main() { ellipsoidNormalCameraSpace = mat3(modelViewTransform) * pair.normal; positionCameraSpace = vec3(modelViewTransform * vec4(pair.position, 1.0)); positionWorldSpace = vec3(modelTransform * dvec4(pair.position, 1.0)); + posObjSpace = pair.position; #if SHADOW_MAPPING_ENABLED shadowCoords = vec4(shadowMatrix * dvec4(pair.position, 1.0)); diff --git a/modules/globebrowsing/shaders/localrenderer_vs.glsl b/modules/globebrowsing/shaders/localrenderer_vs.glsl index 23dc804cc9..56a5f365fe 100644 --- a/modules/globebrowsing/shaders/localrenderer_vs.glsl +++ b/modules/globebrowsing/shaders/localrenderer_vs.glsl @@ -38,6 +38,7 @@ out vec3 ellipsoidNormalCameraSpace; out vec3 levelWeights; out vec3 positionCameraSpace; out vec3 positionWorldSpace; +out vec3 posObjSpace; #if USE_ACCURATE_NORMALS out vec3 ellipsoidTangentThetaCameraSpace; @@ -117,6 +118,7 @@ void main() { ellipsoidNormalCameraSpace = patchNormalCameraSpace; positionCameraSpace = p; positionWorldSpace = vec3(inverseViewTransform * dvec4(p, 1.0)); + posObjSpace = vec3(inverseViewTransform * dvec4(p, 1.0)); #if SHADOW_MAPPING_ENABLED shadowCoords = vec4(shadowMatrix * dvec4(p, 1.0)); diff --git a/modules/globebrowsing/shaders/renderer_fs.glsl b/modules/globebrowsing/shaders/renderer_fs.glsl index 36744e2bfd..21e22434d2 100644 --- a/modules/globebrowsing/shaders/renderer_fs.glsl +++ b/modules/globebrowsing/shaders/renderer_fs.glsl @@ -52,6 +52,10 @@ uniform vec2 vertexResolution; #endif // SHOW_HEIGHT_RESOLUTION uniform vec3 lightDirectionCameraSpace; +uniform vec3 lightDirectionObjSpace; +uniform vec3 lightDirectionWorldSpace; +uniform mat4 modelViewTransform; +uniform float ringSize; #if PERFORM_SHADING uniform float orenNayarRoughness; @@ -70,10 +74,13 @@ uniform sampler1D ringTextureTransparency; uniform vec2 textureOffset; uniform vec3 sunPositionWorld; // NEW: world coordinates uniform vec3 camPositionWorld; // NEW: world coordinates + #endif // USE_RING_SHADOWS #endif // SHADOW_MAPPING_ENABLED +in vec3 posObjSpace; // Fragment position in object space + #if USE_ECLIPSE_SHADOWS #define NSEclipseShadowsMinusOne #{nEclipseShadows} @@ -277,49 +284,44 @@ Fragment getFragment() { #if USE_RING_SHADOWS // Calculate ring shadow by projecting ring texture directly onto surface // Assume ring lies in the XZ plane (Y=0) in object space - vec3 surfaceToSun = -normalize(sunPositionWorld - positionWorldSpace); // Use world coordinates + vec3 surfaceToSun = normalize(lightDirectionObjSpace); // Use world coordinates + vec3 p = posObjSpace; // Find intersection of light ray with ring plane (Y=0) // Ray equation: P = positionWorldSpace + t * surfaceToSun // For ring plane: P.y = 0, so t = -positionWorldSpace.y / surfaceToSun.y - if (abs(surfaceToSun.y) > 0.001) { // Avoid division by zero - float t = -positionWorldSpace.y / surfaceToSun.y; + if (abs(surfaceToSun.y) > 1e-8) { // Avoid division by zero + float t = -p.y / surfaceToSun.y; - if (t > 0.0) { // Ray intersects ring plane in front of surface - vec3 ringIntersection = positionWorldSpace + t * surfaceToSun; + vec3 ringIntersection = p + t * surfaceToSun; - // Calculate distance from ring center - float ringRadius = length(ringIntersection.xz); + // Calculate distance from ring center + float ringRadius = length(ringIntersection.xz); + + // Convert radius to texture coordinate based on ring texture mapping + // Radius \in [textureOffset.x, textureOffset.y] maps to texCoord \in [0,1] + float texCoord = ((ringRadius / ringSize) - textureOffset.x) / (textureOffset.y - textureOffset.x); + + if (texCoord >= 0.0 && texCoord <= 1.0) { + // Sample ring transparency texture + vec4 transparency = texture(ringTextureTransparency, texCoord); + // Determine which side of ring we're viewing from (matches ring shader logic) + float lerpFactor = dot(camPositionWorld, sunPositionWorld); - // Convert radius to texture coordinate based on ring texture mapping - // Radius \in [textureOffset.x, textureOffset.y] maps to texCoord \in [0,1] - float texCoord = (ringRadius - textureOffset.x) / (textureOffset.y - textureOffset.x); + // Sample appropriate ring texture based on viewing direction + vec4 ringColor = mix(texture(ringTextureFwrd, texCoord), texture(ringTextureBckwrd, texCoord), lerpFactor); - if (texCoord >= 0.0 && texCoord <= 1.0) { - // Sample ring transparency texture - vec4 transparency = texture(ringTextureTransparency, texCoord); - // Determine which side of ring we're viewing from (matches ring shader logic) - float lerpFactor = dot(camPositionWorld, sunPositionWorld); - - // Sample appropriate ring texture based on viewing direction - vec4 ringColor = mix(texture(ringTextureFwrd, texCoord), texture(ringTextureBckwrd, texCoord), lerpFactor); - - // Calculate shadow factor based on ring opacity - float ringOpacity = transparency.a; - shadow = 1.0 - ringOpacity; - shadow = clamp(shadow + 0.3, 0.0, 1.0); // Add ambient light similar to original - } - - if (t < 1000.0) { - shadow = 0.0; - } + // Calculate shadow factor based on ring opacity + float ringOpacity = transparency.a; + shadow = 1.0 - ringOpacity; + shadow = clamp(shadow + 0.3, 0.0, 1.0); // Add ambient light similar to original } + + frag.color.rgb = vec3(0.0, ringRadius / ringSize, 0.0); } + #endif // USE_RING_SHADOWS - if (shadow < 1.0) { - frag.color.rgb = vec3(0.0, 1.0, 0.0); - } #endif frag.color.a *= opacity; diff --git a/modules/globebrowsing/src/renderableglobe.cpp b/modules/globebrowsing/src/renderableglobe.cpp index 2e64f7156b..44ecfeaff0 100644 --- a/modules/globebrowsing/src/renderableglobe.cpp +++ b/modules/globebrowsing/src/renderableglobe.cpp @@ -1232,8 +1232,8 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, _globalRenderer.program->setIgnoreUniformLocationError(IgnoreError::No); } - const glm::mat4 modelViewTransform = glm::mat4(viewTransform * _cachedModelTransform); - const glm::mat4 modelViewProjectionTransform = + glm::mat4 modelViewTransform = glm::mat4(viewTransform * _cachedModelTransform); + glm::mat4 modelViewProjectionTransform = data.camera.sgctInternal.projectionMatrix() * modelViewTransform; // Upload the uniform variables @@ -1257,9 +1257,9 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, } using namespace layers; - const bool nightLayersActive = + bool nightLayersActive = !_layerManager.layerGroup(Group::ID::NightLayers).activeLayers().empty(); - const bool waterLayersActive = + bool waterLayersActive = !_layerManager.layerGroup(Group::ID::WaterMasks).activeLayers().empty(); if (nightLayersActive || waterLayersActive || _performShading) { @@ -1268,44 +1268,76 @@ void RenderableGlobe::renderChunks(const RenderData& data, RendererTasks&, const glm::vec3 directionToSunCameraSpace = glm::vec3(viewTransform * glm::dvec4(directionToSunWorldSpace, 0)); - // @TODO (abock, 2020-04-14); This is just a bandaid for issue #1136. The better - // way is to figure out with the uniform is optimized away. I assume that it is - // because the shader doesn't get recompiled when the last layer of the night - // or water is disabled; so the shader thinks it has to do the calculation, but - // there are actually no layers left + const glm::vec3 directionToSunObjSpace = glm::vec3(_cachedInverseModelTransform * + glm::dvec4(directionToSunWorldSpace, 0)); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _localRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); - _localRenderer.program->setUniform( + _globalRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); + _globalRenderer.program->setUniform( "lightDirectionCameraSpace", -glm::normalize(directionToSunCameraSpace) ); - _localRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); + _globalRenderer.program->setUniform( + "lightDirectionObjSpace", + -glm::normalize(directionToSunObjSpace) + ); + _globalRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); } - // Local shader - _localRenderer.program->setUniform( - "projectionTransform", - data.camera.sgctInternal.projectionMatrix() + modelViewTransform = glm::mat4(viewTransform * _cachedModelTransform); + modelViewProjectionTransform = + data.camera.sgctInternal.projectionMatrix() * modelViewTransform; + + + _globalRenderer.program->setUniform( + "lightDirectionWorldSpace", + glm::vec3(glm::normalize(directionToLightSource(data.modelTransform.translation, _lightSourceNode))) ); + // Upload the uniform variables + _localRenderer.program->setUniform( + "modelViewProjectionTransform", + modelViewProjectionTransform + ); + + _localRenderer.program->setUniform("modelViewTransform", modelViewTransform); + + if (_useAccurateNormals && hasHeightLayer) { + // Apply an extra scaling to the height if the object is scaled + _localRenderer.program->setUniform( + "heightScale", + static_cast( + glm::compMax(data.modelTransform.scale) * data.camera.scaling() + ) + ); + } + + using namespace layers; + nightLayersActive = + !_layerManager.layerGroup(Group::ID::NightLayers).activeLayers().empty(); + waterLayersActive = + !_layerManager.layerGroup(Group::ID::WaterMasks).activeLayers().empty(); + if (nightLayersActive || waterLayersActive || _performShading) { const glm::dvec3 directionToSunWorldSpace = directionToLightSource(data.modelTransform.translation, _lightSourceNode); const glm::vec3 directionToSunCameraSpace = glm::vec3(viewTransform * glm::dvec4(directionToSunWorldSpace, 0)); - // @TODO (abock, 2020-04-14); This is just a bandaid for issue #1136. The better - // way is to figure out with the uniform is optimized away. I assume that it is - // because the shader doesn't get recompiled when the last layer of the night - // or water is disabled; so the shader thinks it has to do the calculation, but - // there are actually no layers left + const glm::vec3 directionToSunObjSpace = glm::vec3(_cachedInverseModelTransform * + glm::dvec4(directionToSunWorldSpace, 0)); + using IgnoreError = ghoul::opengl::ProgramObject::IgnoreError; - _globalRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); - _globalRenderer.program->setUniform( + _localRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); + _localRenderer.program->setUniform( "lightDirectionCameraSpace", -glm::normalize(directionToSunCameraSpace) ); - _globalRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); + _localRenderer.program->setUniform( + "lightDirectionObjSpace", + -glm::normalize(directionToSunObjSpace) + ); + _localRenderer.program->setIgnoreUniformLocationError(IgnoreError::Yes); } int globalCount = 0; @@ -1517,6 +1549,7 @@ void RenderableGlobe::renderChunkGlobally(const Chunk& chunk, const RenderData& program.setUniform("textureOffset", _ringsComponent->textureOffset()); program.setUniform("sunPositionObj", _ringsComponent->sunPositionObj()); program.setUniform("camPositionObj", _ringsComponent->camPositionObj()); + program.setUniform("ringSize", (float)_ringsComponent->size()); } } else if (_shadowMappingProperties.shadowMapping && _shadowComponent) { @@ -1696,8 +1729,8 @@ void RenderableGlobe::renderChunkLocally(const Chunk& chunk, const RenderData& d } program.setUniform("textureOffset", _ringsComponent->textureOffset()); - program.setUniform("sunPositionObj", glm::vec3(0.0f)); - program.setUniform("camPositionWorld", data.camera.positionVec3()); // pass camera world position + program.setUniform("sunPositionObj", _ringsComponent->sunPositionObj()); + program.setUniform("camPositionObj", _ringsComponent->camPositionObj()); } } else if (_shadowMappingProperties.shadowMapping) {