From 10ff8988020e5b7e17627b8925fac95f332667f9 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Thu, 5 Jun 2025 21:56:08 -0400 Subject: [PATCH 01/19] Resolve the vast majority of SSR's problems in one go. --- indra/newview/app_settings/settings.xml | 6 +- .../class3/deferred/reflectionProbeF.glsl | 2 +- .../class3/deferred/screenSpaceReflUtil.glsl | 429 +++++++++++------- indra/newview/pipeline.cpp | 2 +- 4 files changed, 273 insertions(+), 166 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 8cfe4f3d975..e6abcf30193 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7594,7 +7594,7 @@ Type S32 Value - 25 + 24 RenderScreenSpaceReflectionRayStep @@ -7605,7 +7605,7 @@ Type F32 Value - 0.1 + 1.0 RenderScreenSpaceReflectionDistanceBias @@ -7649,7 +7649,7 @@ Type S32 Value - 4 + 2 RenderBumpmapMinDistanceSquared diff --git a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl index 136b3dd9668..5e27cd8c809 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/reflectionProbeF.glsl @@ -750,7 +750,7 @@ void doProbeSample(inout vec3 ambenv, inout vec3 glossenv, glossenv = sampleProbes(pos, normalize(refnormpersp), lod); #if defined(SSR) - if (cube_snapshot != 1 && glossiness >= 0.9) + if (cube_snapshot != 1) { vec4 ssr = vec4(0); if (transparent) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index e8901c7ba26..65b34a8a5eb 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -75,113 +75,21 @@ float getLinearDepth(vec2 tc) return -pos.z; } -bool traceScreenRay(vec3 position, vec3 reflection, out vec4 hitColor, out float hitDepth, float depth, sampler2D textureFrame) -{ - // transform position and reflection into same coordinate frame as the sceneMap and sceneDepth - reflection += position; - position = (inv_modelview_delta * vec4(position, 1)).xyz; - reflection = (inv_modelview_delta * vec4(reflection, 1)).xyz; - reflection -= position; - - depth = -position.z; - - vec3 step = rayStep * reflection; - vec3 marchingPosition = position + step; - float delta; - float depthFromScreen; - vec2 screenPosition; - bool hit = false; - hitColor = vec4(0); - - int i = 0; - if (depth > depthRejectBias) - { - for (; i < iterationCount && !hit; i++) - { - screenPosition = generateProjectedPosition(marchingPosition); - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - hit = false; - break; - } - depthFromScreen = getLinearDepth(screenPosition); - delta = abs(marchingPosition.z) - depthFromScreen; - - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - break; - } - - if (abs(delta) < distanceBias) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4( 0.5+ sign(delta)/2,0.3,0.5- sign(delta)/2, 0); - hitColor = texture(sceneMap, screenPosition) * color; - hitDepth = depthFromScreen; - hit = true; - break; - } - if (isBinarySearchEnabled && delta > 0) - { - break; - } - if (isAdaptiveStepEnabled) - { - float directionSign = sign(abs(marchingPosition.z) - depthFromScreen); - //this is sort of adapting step, should prevent lining reflection by doing sort of iterative converging - //some implementation doing it by binary search, but I found this idea more cheaty and way easier to implement - step = step * (1.0 - rayStep * max(directionSign, 0.0)); - marchingPosition += step * (-directionSign); - } - else - { - marchingPosition += step; - } - - if (isExponentialStepEnabled) - { - step *= adaptiveStepMultiplier; - } - } - if(isBinarySearchEnabled) - { - for(; i < iterationCount && !hit; i++) - { - step *= 0.5; - marchingPosition = marchingPosition - step * sign(delta); - - screenPosition = generateProjectedPosition(marchingPosition); - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - hit = false; - break; - } - depthFromScreen = getLinearDepth(screenPosition); - delta = abs(marchingPosition.z) - depthFromScreen; - - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - break; - } - - if (abs(delta) < distanceBias && depthFromScreen != (depth - distanceBias)) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4( 0.5+ sign(delta)/2,0.3,0.5- sign(delta)/2, 0); - hitColor = texture(sceneMap, screenPosition) * color; - hitDepth = depthFromScreen; - hit = true; - break; - } - } - } - } - - return hit; +// Add this function to your shader code to calculate edge fade based on screen position +float calculateEdgeFade(vec2 screenPos) { + // Start fading when we're this close to the edge (0.9 = start fading at 10% from edge) + const float edgeFadeStart = 0.9; + + // Convert 0-1 screen position to -1 to 1 range and take absolute value + // This gives us how far we are from the center (0 = center, 1 = edge) + vec2 distFromCenter = abs(screenPos * 2.0 - 1.0); + + // Calculate smooth fade for each axis + vec2 fade = smoothstep(edgeFadeStart, 1.0, distFromCenter); + + // Use the maximum fade value between X and Y axes + // (we want to fade if we're close to ANY edge) + return 1.0 - max(fade.x, fade.y); } uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( @@ -319,76 +227,275 @@ vec3 getPoissonSample(int i) { return POISSON3D_SAMPLES[i] * 2 - 1; } -float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness) -{ -#ifdef TRANSPARENT_SURFACE -collectedColor = vec4(1, 0, 1, 1); - return 0; -#endif - collectedColor = vec4(0); - int hits = 0; - - float depth = -viewPos.z; - - vec3 rayDirection = normalize(reflect(viewPos, normalize(n))); - - vec2 uv2 = tc * screen_res; - float c = (uv2.x + uv2.y) * 0.125; - float jitter = mod( c, 1.0); - - vec2 screenpos = 1 - abs(tc * 2 - 1); - float vignette = clamp((abs(screenpos.x) * abs(screenpos.y)) * 16,0, 1); - vignette *= clamp((dot(normalize(viewPos), n) * 0.5 + 0.5) * 5.5 - 0.8, 0, 1); +float calculateMipmapLevels(vec2 resolution) { + // Find the maximum dimension + float maxDimension = max(resolution.x, resolution.y); + + // Calculate log2 of max dimension + // (the number of times we can divide by 2 until we reach 1) + return floor(log2(maxDimension)) + 1.0; +} - float zFar = 128.0; - vignette *= clamp(1.0+(viewPos.z/zFar), 0.0, 1.0); +// Modified traceScreenRay function with improved fade +bool traceScreenRay(vec3 position, vec3 reflection, out vec4 hitColor, out float hitDepth, float depth, sampler2D source, out float edgeFade, float roughness) { + // Transform position and reflection into same coordinate frame as the sceneMap and sceneDepth + reflection += position; + position = (inv_modelview_delta * vec4(position, 1)).xyz; + reflection = (inv_modelview_delta * vec4(reflection, 1)).xyz; + reflection -= position; - vignette *= clamp(glossiness * 3 - 1.7, 0, 1); + depth = -position.z; - vec4 hitpoint; + vec3 step = rayStep * reflection; + vec3 marchingPosition = position + step; + float delta; + float depthFromScreen; + vec2 screenPosition; + bool hit = false; + hitColor = vec4(0); + edgeFade = 1.0; // Default to no fade + + int i = 0; + if (depth > depthRejectBias) + { + for (; i < iterationCount && !hit; i++) + { + screenPosition = generateProjectedPosition(marchingPosition); + + // Calculate edge fade when we hit screen boundaries + if (screenPosition.x > 1 || screenPosition.x < 0 || + screenPosition.y > 1 || screenPosition.y < 0) + { + // Clamp screen position to 0-1 range for fade calculation + vec2 clampedPos = clamp(screenPosition, 0.0, 1.0); + vec2 distFromCenter = abs(clampedPos * 2.0 - 1.0); + edgeFade = calculateEdgeFade(clampedPos); + + hitDepth = depthFromScreen; + hit = false; + break; + } + + depthFromScreen = getLinearDepth(screenPosition); + delta = abs(marchingPosition.z) - depthFromScreen; - glossiness = 1 - glossiness; + if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) + { + // Calculate a fade factor based on how close we are to screen edges + vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); + edgeFade = calculateEdgeFade(screenPosition); + break; + } - totalSamples = int(max(glossySampleCount, glossySampleCount * glossiness * vignette)); + if (abs(delta) < distanceBias) + { + vec4 color = vec4(1); + if(debugDraw) + color = vec4(0.5 + sign(delta)/2, 0.3, 0.5 - sign(delta)/2, 0); + + float mipLevel = calculateMipmapLevels(screen_res) * roughness; // Maps 0-1 roughness to 0-4 mip levels + vec2 screenCoord = screenPosition + (1 / screen_res * mipLevel ); + // Do a box sample. + hitColor = textureLod(source, screenCoord, mipLevel) * color; + hitColor += textureLod(source, -screenCoord, mipLevel) * color; + hitColor += textureLod(source, vec2(-screenCoord.x, screenCoord.y), mipLevel) * color; + hitColor += textureLod(source, vec2(screenCoord.x, -screenCoord.y), mipLevel) * color; + hitColor *= 0.25; + hitColor.a = 1; + hitDepth = depthFromScreen; + hit = true; + + // Calculate edge fade even for successful hits + vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); + edgeFade = calculateEdgeFade(screenPosition); + + // Add depth discontinuity detection for improved quality + float depthGradient = 0.0; + vec2 texelSize = 1.0 / screen_res; + float depthRight = getLinearDepth(screenPosition + vec2(texelSize.x, 0)); + float depthBottom = getLinearDepth(screenPosition + vec2(0, texelSize.y)); + depthGradient = max(abs(depthRight - depthFromScreen), abs(depthBottom - depthFromScreen)); + + break; + } + + // Rest of your existing tracing code with adaptive steps + if (isBinarySearchEnabled && delta > 0) + { + break; + } + if (isAdaptiveStepEnabled) + { + float directionSign = sign(abs(marchingPosition.z) - depthFromScreen); + step = step * (1.0 - rayStep * max(directionSign, 0.0)); + marchingPosition += step * (-directionSign); + } + else + { + marchingPosition += step; + } - totalSamples = max(totalSamples, 1); - if (glossiness < 0.35) - { - if (vignette > 0) + if (isExponentialStepEnabled) + { + step *= adaptiveStepMultiplier; + } + } + + // Binary search refinement with edge fade calculation + if(isBinarySearchEnabled) { - for (int i = 0; i < totalSamples; i++) + for(; i < iterationCount && !hit; i++) { - vec3 firstBasis = normalize(cross(getPoissonSample(i), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * glossiness); - - //float hitDepth; + step *= 0.5; + marchingPosition = marchingPosition - step * sign(delta); - bool hit = traceScreenRay(viewPos, normalize(reflectionDirectionRandomized), hitpoint, depth, depth, source); + screenPosition = generateProjectedPosition(marchingPosition); + if (screenPosition.x > 1 || screenPosition.x < 0 || + screenPosition.y > 1 || screenPosition.y < 0) + { + vec2 clampedPos = clamp(screenPosition, 0.0, 1.0); + vec2 distFromCenter = abs(clampedPos * 2.0 - 1.0); + edgeFade = calculateEdgeFade(screenPosition); + hitDepth = depthFromScreen; + hitColor.a = 0; + hit = false; + break; + } + + depthFromScreen = getLinearDepth(screenPosition); + delta = abs(marchingPosition.z) - depthFromScreen; - hitpoint.a = 0; + if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) + { + vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); + edgeFade = calculateEdgeFade(screenPosition); + break; + } - if (hit) + if (abs(delta) < distanceBias && depthFromScreen != (depth - distanceBias)) { - ++hits; - collectedColor += hitpoint; - collectedColor.a += 1; + vec4 color = vec4(1); + if(debugDraw) + color = vec4(0.5 + sign(delta)/2, 0.3, 0.5 - sign(delta)/2, 0); + + float mipLevel = calculateMipmapLevels(screen_res) * roughness; // Maps 0-1 roughness to 0-4 mip levels + vec2 screenCoord = screenPosition + (1 / screen_res * mipLevel); + // Do a box sample. + hitColor = textureLod(source, screenCoord, mipLevel) * color; + hitColor += textureLod(source, -screenCoord, mipLevel) * color; + hitColor += textureLod(source, vec2(-screenCoord.x, screenCoord.y), mipLevel) * color; + hitColor += textureLod(source, vec2(screenCoord.x, -screenCoord.y), mipLevel) * color; + hitColor *= 0.25; + hitDepth = depthFromScreen; + hitColor.a = 1; + hit = true; + + vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); + edgeFade = calculateEdgeFade(screenPosition); + + // Add depth discontinuity detection for improved quality in binary search + float depthGradient = 0.0; + vec2 texelSize = 1.0 / screen_res; + float depthRight = getLinearDepth(screenPosition + vec2(texelSize.x, 0)); + float depthBottom = getLinearDepth(screenPosition + vec2(0, texelSize.y)); + depthGradient = max(abs(depthRight - depthFromScreen), abs(depthBottom - depthFromScreen)); + + break; } } + } + } - if (hits > 0) - { - collectedColor /= hits; - } - else - { - collectedColor = vec4(0); + return hit; +} + +// Enhanced tapScreenSpaceReflection function +float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness) { +#ifdef TRANSPARENT_SURFACE + collectedColor = vec4(1, 0, 1, 1); + return 0; +#endif + collectedColor = vec4(0); + int hits = 0; + float cumulativeFade = 0.0; + float averageHitDepth = 0.0; + + float depth = -viewPos.z; + + // Flip normal if needed + if (dot(n, -viewPos) < 0.0) { + n = -n; + } + + vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); + + vec2 uv2 = tc * screen_res; + float c = (uv2.x + uv2.y) * 0.125; + float jitter = mod(c, 1.0); + + // Calculate a base edge fade for the current screen position + vec2 distFromCenter = abs(tc * 2.0 - 1.0); + float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); + + float zFar = 250.0; + // Distance-based vignette + float distanceVignette = clamp((1.0+(viewPos.z/zFar)) * 2, 0.0, 1.0); + + // Improved roughness mapping + float roughness = 1.0 - glossiness; + //roughness = roughness * roughness; // More physically accurate roughness mapping + + // Skip calculation entirely if we're right at the edge + if (baseEdgeFade > 0.01) { + // Calculate sample count based on roughness and edge fade + totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); + totalSamples = max(totalSamples, 1); + + for (int i = 0; i < totalSamples; i++) { + int poissonIndex = clamp(int(i + (random(tc) * (128 / totalSamples))), 0, 128); + vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); + vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); + vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * min((roughness * roughness * roughness * roughness), 0.001)); + + float hitDepth; + vec4 hitpoint; + float rayEdgeFade; + + bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpoint, hitDepth, depth, source, rayEdgeFade, roughness); + + if (hit) { + ++hits; + collectedColor.rgb += hitpoint.rgb; + collectedColor.a += 1.0; + cumulativeFade += rayEdgeFade; + averageHitDepth += hitDepth; } } + + if (hits > 0) { + collectedColor /= float(hits); + cumulativeFade /= float(hits); + averageHitDepth /= float(hits); + + } else { + collectedColor = vec4(0); + cumulativeFade = 0.0; + } + } else { + cumulativeFade = 0.0; } - float hitAlpha = hits; - hitAlpha /= totalSamples; - collectedColor.a = hitAlpha * vignette; - return hits; + + // Apply final edge fading + float finalEdgeFade = min(cumulativeFade * 1, 1) * distanceVignette; + + // Apply Fresnel effect + vec3 viewDir = normalize(-viewPos); + float NdotV = max(dot(normalize(n), viewDir), 0.0); + float fresnel = pow(1.0 - NdotV, 5.0); // Schlick's approximation + //collectedColor.rgb = vec3(1.0 / hits / 2); + // Combine fades and adjust alpha + collectedColor.a = finalEdgeFade; + + return float(hits); } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 493fcd5d45b..d480b6d00c6 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -900,7 +900,7 @@ bool LLPipeline::allocateScreenBufferInternal(U32 resX, U32 resY) if(RenderScreenSpaceReflections) { - mSceneMap.allocate(resX, resY, screenFormat, true); + mSceneMap.allocate(resX, resY, screenFormat, true, LLTexUnit::TT_TEXTURE, LLTexUnit::TMG_AUTO); } else { From 5aee7e66ace61ac1e7385868343d0f8d82820d55 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 6 Jun 2025 01:07:22 -0400 Subject: [PATCH 02/19] Modularize and document. --- .../class3/deferred/screenSpaceReflUtil.glsl | 689 ++++++++++++------ 1 file changed, 463 insertions(+), 226 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 65b34a8a5eb..b117ede0249 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -1,5 +1,9 @@ /** * @file class3/deferred/screenSpaceReflUtil.glsl + * @brief Utility functions for implementing Screen Space Reflections (SSR). + * + * This file contains the core logic for ray marching in screen space to find reflections, + * including adaptive step sizes, binary search refinement, and handling of glossy reflections. * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code @@ -23,76 +27,147 @@ * $/LicenseInfo$ */ +// --- Uniforms --- +// These are variables passed from the CPU to the shader. + +/** @brief Sampler for the previous frame's scene color. Potentially used for temporal reprojection or other effects. */ +uniform sampler2D prevSceneMap; +/** @brief Sampler for the current frame's scene color. This is what reflections will be sampled from. */ uniform sampler2D sceneMap; +/** @brief Sampler for the current frame's scene depth. Used to find intersections during ray marching. */ uniform sampler2D sceneDepth; +/** @brief Resolution of the screen in pixels (width, height). */ uniform vec2 screen_res; +/** @brief The current view's projection matrix. Transforms view space coordinates to clip space. */ uniform mat4 projection_matrix; -//uniform float zNear; -//uniform float zFar; +//uniform float zNear; // Near clipping plane distance (commented out, likely unused or implicitly handled) +/** @brief Far clipping plane distance. Used in depth calculations. */ +uniform float zFar; +/** @brief The inverse of the projection matrix. Transforms clip space coordinates back to view space. */ uniform mat4 inv_proj; -uniform mat4 modelview_delta; // should be transform from last camera space to current camera space +/** @brief Transformation matrix from the last frame's camera space to the current frame's camera space. Used for temporal reprojection or motion vector calculations. */ +uniform mat4 modelview_delta; +/** @brief Inverse of the modelview_delta matrix. Transforms current camera space back to last frame's camera space. */ uniform mat4 inv_modelview_delta; +// --- Forward declaration for a function defined elsewhere or later in the file --- +/** + * @brief Reconstructs view space position from screen coordinates and depth. + * @param pos_screen Screen space texture coordinates (0-1 range). + * @param depth Depth value sampled from the depth buffer (typically non-linear). + * @return vec4 The reconstructed position in view space. The .xyz components are position, .w is typically 1.0. + */ vec4 getPositionWithDepth(vec2 pos_screen, float depth); +/** + * @brief Generates a pseudo-random float value based on a 2D input vector. + * @param uv A 2D vector used as a seed for the random number generation. + * @return float A pseudo-random value in the range [0, 1). + */ float random (vec2 uv) { - return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123); //simple random function + // A common simple hash function to generate pseudo-random numbers in GLSL. + return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123); } // Based off of https://github.com/RoundedGlint585/ScreenSpaceReflection/ // A few tweaks here and there to suit our needs. +/** + * @brief Projects a 3D position (typically in view space) to 2D screen space coordinates. + * @param pos The 3D position to project (assumed to be in view space). + * @return vec2 The 2D screen space coordinates, in the range [0, 1] for x and y. + */ vec2 generateProjectedPosition(vec3 pos) { + // Project the 3D position to clip space. vec4 samplePosition = projection_matrix * vec4(pos, 1.f); + // Perform perspective divide and map to [0, 1] texture coordinate range. samplePosition.xy = (samplePosition.xy / samplePosition.w) * 0.5 + 0.5; return samplePosition.xy; } +// --- SSR Algorithm Configuration Flags --- +// These booleans control various optimizations and features of the SSR algorithm. + +/** @brief If true, a binary search step is performed to refine the hit point after an initial overshoot. */ bool isBinarySearchEnabled = true; +/** @brief If true, the ray marching step size is adapted based on the distance to the nearest surface. */ bool isAdaptiveStepEnabled = true; +/** @brief If true, the base ray marching step size increases exponentially with each step. */ bool isExponentialStepEnabled = true; +/** @brief If true, debug colors are used to visualize aspects of the ray marching process (e.g., delta values). */ bool debugDraw = false; +// --- SSR Algorithm Parameters (controlled by debug settings) --- + +/** @brief Maximum number of iterations for the ray marching loops. Debug setting: RenderScreenSpaceReflectionIterations */ uniform float iterationCount; +/** @brief Initial base step size for ray marching. Debug setting: RenderScreenSpaceReflectionRayStep */ uniform float rayStep; +/** @brief Threshold for considering a ray hit. If the distance between the ray and the scene surface is less than this, it's a hit. Debug setting: RenderScreenSpaceReflectionDistanceBias */ uniform float distanceBias; +/** @brief Bias to prevent self-reflection or reflections from surfaces very close to the ray origin. Rays originating from surfaces shallower than this bias are rejected. Debug setting: RenderScreenSpaceReflectionDepthRejectionBias */ uniform float depthRejectBias; +/** @brief Number of samples to take for glossy reflections. Higher values give smoother but more expensive reflections. Debug setting: RenderScreenSpaceReflectionGlossySamples */ uniform float glossySampleCount; +/** @brief Multiplier for the adaptive step size calculation. Debug setting: RenderScreenSpaceReflectionAdaptiveStepMultiplier */ uniform float adaptiveStepMultiplier; +/** @brief A time-varying sine value, likely used to introduce temporal variation in noise patterns (e.g., for Poisson disk sampling). */ uniform float noiseSine; +/** @brief A small constant value used for floating-point comparisons, typically to avoid issues with precision. Used in self-intersection checks. */ float epsilon = 0.1; +/** + * @brief Samples the scene depth texture and converts it to linear view space Z depth. + * @param tc Texture coordinates (screen space, 0-1 range) at which to sample the depth. + * @return float The linear depth value (typically negative, representing distance from camera in view space). + */ float getLinearDepth(vec2 tc) { + // Sample the raw depth value from the depth texture. float depth = texture(sceneDepth, tc).r; - + // Reconstruct the view space position using the sampled depth. vec4 pos = getPositionWithDepth(tc, depth); - - return -pos.z; + // Return the Z component of the view space position, which is the linear depth. + return -pos.z; // Negated as view space Z is typically negative. } -// Add this function to your shader code to calculate edge fade based on screen position +/** + * @brief Calculates a fade factor based on the proximity of a screen position to the screen edges. + * The fade increases as the position gets closer to any edge. + * @param screenPos The screen position in normalized device coordinates (0-1 range). + * @return float The edge fade factor, ranging from 1.0 (no fade, away from edges) to 0.0 (full fade, at an edge). + */ float calculateEdgeFade(vec2 screenPos) { - // Start fading when we're this close to the edge (0.9 = start fading at 10% from edge) + // Defines how close to the edge the fade effect starts. + // 0.9 means fading begins when the point is 10% away from the screen edge. const float edgeFadeStart = 0.9; - // Convert 0-1 screen position to -1 to 1 range and take absolute value - // This gives us how far we are from the center (0 = center, 1 = edge) + // Convert screen position from [0,1] to [-1,1] range and take absolute value. + // This gives distance from the center: 0 at center, 1 at edges. vec2 distFromCenter = abs(screenPos * 2.0 - 1.0); - // Calculate smooth fade for each axis + // Calculate a smooth fade for each axis using smoothstep. + // smoothstep(edgeFadeStart, 1.0, x) will be: + // - 0 if x < edgeFadeStart + // - 1 if x > 1.0 + // - A smooth transition between 0 and 1 if edgeFadeStart <= x <= 1.0 vec2 fade = smoothstep(edgeFadeStart, 1.0, distFromCenter); - // Use the maximum fade value between X and Y axes - // (we want to fade if we're close to ANY edge) + // Use the maximum fade value between X and Y axes. + // We want to fade if the point is close to ANY edge. + // If fade.x is high (close to X-edge) or fade.y is high (close to Y-edge), + // max(fade.x, fade.y) will be high. + // 1.0 - max(...) inverts this, so it's 1.0 (no fade) in the center and 0.0 (full fade) at edges. return 1.0 - max(fade.x, fade.y); } +/** @brief Predefined array of 128 3D Poisson disk sample offsets. Used for generating distributed samples, e.g., for glossy reflections. Values are in [0,1] range. */ uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( + // ... (array values as provided) ... vec3(0.5433144, 0.1122154, 0.2501391), vec3(0.6575254, 0.721409, 0.16286), vec3(0.02888453, 0.05170321, 0.7573566), @@ -223,279 +298,441 @@ uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( vec3(0.2698198, 0.0002266169, 0.3449324) ); +/** + * @brief Retrieves a Poisson disk sample from the predefined array and maps it to the [-1, 1] range. + * @param i The index of the sample to retrieve (should be within the bounds of POISSON3D_SAMPLES). + * @return vec3 The 3D sample vector, with components in the range [-1, 1]. + */ vec3 getPoissonSample(int i) { - return POISSON3D_SAMPLES[i] * 2 - 1; + // Samples are stored in [0,1], scale and shift to [-1,1]. + return POISSON3D_SAMPLES[i] * 2.0 - 1.0; } +/** + * @brief Calculates the approximate number of mipmap levels for a texture of a given resolution. + * This can be used to determine a suitable blur radius or LOD for texture sampling. + * @param resolution The 2D resolution of the texture (width, height). + * @return float The estimated number of mipmap levels. + */ float calculateMipmapLevels(vec2 resolution) { - // Find the maximum dimension + // Find the larger dimension of the texture. float maxDimension = max(resolution.x, resolution.y); - - // Calculate log2 of max dimension - // (the number of times we can divide by 2 until we reach 1) + // The number of mip levels is related to log2 of the largest dimension. + // Adding 1.0 accounts for the base level (mip 0). return floor(log2(maxDimension)) + 1.0; } -// Modified traceScreenRay function with improved fade -bool traceScreenRay(vec3 position, vec3 reflection, out vec4 hitColor, out float hitDepth, float depth, sampler2D source, out float edgeFade, float roughness) { - // Transform position and reflection into same coordinate frame as the sceneMap and sceneDepth - reflection += position; - position = (inv_modelview_delta * vec4(position, 1)).xyz; - reflection = (inv_modelview_delta * vec4(reflection, 1)).xyz; - reflection -= position; - - depth = -position.z; - - vec3 step = rayStep * reflection; - vec3 marchingPosition = position + step; - float delta; - float depthFromScreen; - vec2 screenPosition; - bool hit = false; - hitColor = vec4(0); - edgeFade = 1.0; // Default to no fade +/** + * @brief Processes a confirmed ray intersection, calculating hit color, depth, and edge fade. + * This function encapsulates the logic for sampling the source texture and applying debug visualization. + * @param screenPos Current screen position (texture coordinates) of the hit. + * @param hitScreenDepth Depth value from the scene's depth buffer at the hit point (linear view space Z). + * @param signedDelta Signed difference between the ray's current depth and the scene's depth. Used for debug coloring. + * @param texSource The texture (e.g., scene color) to sample for the reflection color. + * @param reflRoughness Roughness of the reflecting surface, used to adjust mip level for blurring. + * @param outHitColor Output: The calculated color of the reflection. + * @param outHitDepthValue Output: The depth of the reflection hit. + * @param outEdgeFactor Output: The edge fade factor calculated at the hit position. + */ +void processRayIntersection( + vec2 screenPos, + float hitScreenDepth, + float signedDelta, + sampler2D texSource, + float reflRoughness, + out vec4 outHitColor, + out float outHitDepthValue, + out float outEdgeFactor) +{ + vec4 color = vec4(1.0); // Default color multiplier. + if(debugDraw) { + // If debug drawing is enabled, colorize based on the sign of delta. + // Helps visualize if the ray hit in front of or behind the surface. + color = vec4(0.5 + sign(signedDelta) / 2.0, 0.3, 0.5 - sign(signedDelta) / 2.0, 0.0); + } + + // Calculate mip level for texture sampling based on screen resolution and roughness. + // Higher roughness leads to sampling from higher (blurrier) mip levels. + float mipLevel = calculateMipmapLevels(screen_res) * reflRoughness; - int i = 0; - if (depth > depthRejectBias) + // Sample the source texture at the hit position using the calculated mip level. + outHitColor = textureLod(texSource, screenPos, mipLevel) * color; + outHitColor.a = 1.0; // Ensure full opacity for the hit. + outHitDepthValue = hitScreenDepth; // Store the depth of the hit. + outEdgeFactor = calculateEdgeFade(screenPos); // Calculate edge fade at the hit position. +} + +/** + * @brief Checks if the ray's projected screen position is outside the [0,1] screen bounds. + * If off-screen, it calculates and updates the edge fade factor. + * @param screenPos The ray's current projected screen position (texture coordinates). + * @param currentEdgeFade Input/Output: The current edge fade value, which will be updated if the ray is off-screen. + * @return bool True if the ray is off-screen, false otherwise. + */ +bool checkAndUpdateOffScreen(vec2 screenPos, inout float currentEdgeFade) +{ + if (screenPos.x > 1.0 || screenPos.x < 0.0 || + screenPos.y > 1.0 || screenPos.y < 0.0) { - for (; i < iterationCount && !hit; i++) - { + // If off-screen, clamp the position to the edges and calculate edge fade. + vec2 clampedPos = clamp(screenPos, 0.0, 1.0); + currentEdgeFade = calculateEdgeFade(clampedPos); + return true; // Is off-screen + } + return false; // Is on-screen +} + +/** + * @brief Checks if the ray is intersecting too close to the surface it originated from (self-intersection). + * This helps prevent artifacts where a reflection ray immediately hits its own surface. + * @param reflectingSurfaceDepth The view space Z depth of the surface from which the ray originates. + * @param currentRayMarchDepth The view space Z depth of the scene surface at the ray's current projected position. + * @param depthEpsilon A small tolerance value for the depth comparison. + * @return bool True if a self-intersection is detected, false otherwise. + */ +bool checkSelfIntersection(float reflectingSurfaceDepth, float currentRayMarchDepth, float depthEpsilon) +{ + // Check if the ray's current depth is within epsilon distance of the original surface's depth. + return reflectingSurfaceDepth < currentRayMarchDepth + depthEpsilon && + reflectingSurfaceDepth > currentRayMarchDepth - depthEpsilon; +} + +/** + * @brief Advances the ray marching position based on adaptive and/or exponential stepping logic. + * @param deltaFromSurface Current difference between the ray's depth and the scene depth at the projected point. + * Negative means ray is in front of surface, positive means behind. + * @param currentMarchingPos Input/Output: The current 3D position of the ray in view space. Will be updated. + * @param currentBaseStepVec Input/Output: The base step vector. Its direction is the ray direction. Its magnitude + * can be scaled by exponential stepping and provides a reference for adaptive steps. + * @param minStepLenScalar The minimum step length ('rayStep' uniform), a scalar. + * @param useAdaptiveStepping Boolean flag to enable adaptive stepping. + * @param useExponentialStepping Boolean flag to enable exponential step scaling. + * @param expStepMultiplier Multiplier for exponential step scaling ('adaptiveStepMultiplier' uniform). + */ +void advanceRayMarch( + float deltaFromSurface, + inout vec3 currentMarchingPos, + inout vec3 currentBaseStepVec, + float minStepLenScalar, + bool useAdaptiveStepping, + bool useExponentialStepping, + float expStepMultiplier +) +{ + vec3 actualMarchingVector; + if (useAdaptiveStepping) { + if (deltaFromSurface < 0.0f) { // Ray is in front of the surface + vec3 stepDir = normalize(currentBaseStepVec); + if (abs(stepDir.z) > 0.0001f) { // Avoid division by zero if ray is mostly horizontal + // Project the Z-difference onto the ray's direction to estimate distance to intersection. + float distToPotentialIntersection = abs(deltaFromSurface) / abs(stepDir.z); + // Determine adaptive step length: + // - At least minStepLenScalar. + // - Try to step a fraction (e.g., 75%) towards potential intersection. + // - No more than the current length of currentBaseStepVec. + float adaptiveLength = clamp(distToPotentialIntersection * 0.75f, + minStepLenScalar, + length(currentBaseStepVec)); + actualMarchingVector = stepDir * adaptiveLength; + } else { // Ray is mostly horizontal, use a conservative step. + actualMarchingVector = stepDir * max(length(currentBaseStepVec) * 0.5f, minStepLenScalar); + } + } else { // deltaFromSurface >= 0.0f (Ray is at or behind the surface, but not yet a 'hit' or triggering binary search) + // This typically involves a retreat if the ray overshot. + float directionSign = sign(deltaFromSurface); // 0 if at surface, 1 if behind. + // Form a retreat vector. + vec3 baseStepForRetreat = currentBaseStepVec * (1.0f - minStepLenScalar * max(directionSign, 0.0f)); + actualMarchingVector = baseStepForRetreat * (-directionSign); // Retreat if delta > 0. + } + } else { // Not adaptive stepping, use the current base step vector. + actualMarchingVector = currentBaseStepVec; + } + + currentMarchingPos += actualMarchingVector; // Advance the ray position. + + if (useExponentialStepping) { + // If exponential stepping is enabled, increase the magnitude of the base step vector for subsequent iterations. + currentBaseStepVec *= expStepMultiplier; + } +} + +/** + * @brief Traces a single reflection ray through the scene using screen-space ray marching. + * It attempts to find an intersection with the scene geometry represented by a depth buffer. + * Features include adaptive step size, exponential step increase, binary search refinement, + * and edge fading. + * + * @param initialPosition The starting position of the ray in current view space. + * @param initialReflection The initial reflection vector (direction) in current view space. + * @param hitColor Output: If an intersection is found, this will be the color sampled from the 'source' texture at the hit point. Otherwise, it's (0,0,0,0). + * @param hitDepth Output: If an intersection is found, this will be the linear view space Z depth of the hit point. + * @param source The sampler2D (e.g., current frame's color buffer) to fetch reflection color from upon a hit. + * @param edgeFade Output: A factor (0-1) indicating how close the ray path or hit point is to the screen edges. 1 means no fade. + * @param roughness Surface roughness (0-1), used to adjust mip level for texture sampling to simulate blurriness. + * @return bool True if the ray hits a surface, false otherwise. + */ +bool traceScreenRay( + vec3 initialPosition, + vec3 initialReflection, + out vec4 hitColor, + out float hitDepth, + sampler2D source, + out float edgeFade, + float roughness) +{ + // Transform initialPosition and the target of initialReflection vector from current camera space to previous camera space. + // This is crucial if 'source' texture and 'sceneDepth' are from the previous frame's perspective, + // or if a consistent coordinate system is needed for ray marching against 'inv_modelview_delta'-transformed geometry. + vec3 reflectionTargetPoint = initialPosition + initialReflection; + vec3 currentPosition_transformed = (inv_modelview_delta * vec4(initialPosition, 1.0)).xyz; + vec3 reflectionTarget_transformed = (inv_modelview_delta * vec4(reflectionTargetPoint, 1.0)).xyz; + vec3 reflectionVector_transformed = reflectionTarget_transformed - currentPosition_transformed; + + // Depth of the reflecting surface in the transformed view space. + float reflectingSurfaceViewDepth = -currentPosition_transformed.z; + + // Initialize ray marching variables. + // 'baseStepVector' starts with 'rayStep' magnitude in the direction of the (transformed) reflection. + vec3 baseStepVector = rayStep * reflectionVector_transformed; + vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. + + float delta; // Difference between ray's Z depth and scene's Z depth at the projected screen position. + vec2 screenPosition; // Ray's current projected 2D screen position. + bool hit = false; // Flag to indicate if an intersection was found. + hitColor = vec4(0.0); // Initialize output hit color. + edgeFade = 1.0; // Initialize output edge fade. + float depthFromScreen = 0.0; // Linear depth sampled from the sceneDepth texture. + + int i = 0; // Iteration counter. + // Only trace if the reflecting surface itself isn't too shallow (depthRejectBias). + if (reflectingSurfaceViewDepth > depthRejectBias) { + // --- Main Ray Marching Loop --- + for (; i < int(iterationCount) && !hit; i++) { + // Project current 3D marching position to 2D screen space. screenPosition = generateProjectedPosition(marchingPosition); - // Calculate edge fade when we hit screen boundaries - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - // Clamp screen position to 0-1 range for fade calculation - vec2 clampedPos = clamp(screenPosition, 0.0, 1.0); - vec2 distFromCenter = abs(clampedPos * 2.0 - 1.0); - edgeFade = calculateEdgeFade(clampedPos); - - hitDepth = depthFromScreen; - hit = false; + // Check if ray is off-screen. If so, update edgeFade and break. + if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { + hit = false; // Ensure hit is false as ray went out of bounds. break; } + // Get linear depth of the scene at the ray's projected screen position. depthFromScreen = getLinearDepth(screenPosition); + // Calculate delta: difference between ray's absolute Z and scene's depth. + // Assumes marchingPosition.z and depthFromScreen are comparable (e.g., both positive view depths or both negative view Z). + // Original code uses abs(marchingPosition.z), implying positive view depth convention. delta = abs(marchingPosition.z) - depthFromScreen; - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - // Calculate a fade factor based on how close we are to screen edges - vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); - edgeFade = calculateEdgeFade(screenPosition); + // Check for self-intersection (ray hitting the surface it originated from). + if (checkSelfIntersection(reflectingSurfaceViewDepth, depthFromScreen, epsilon)) { + edgeFade = calculateEdgeFade(screenPosition); // Update edge fade even on self-intersection. + hit = false; // This is not a valid reflection hit. break; } - if (abs(delta) < distanceBias) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4(0.5 + sign(delta)/2, 0.3, 0.5 - sign(delta)/2, 0); - - float mipLevel = calculateMipmapLevels(screen_res) * roughness; // Maps 0-1 roughness to 0-4 mip levels - vec2 screenCoord = screenPosition + (1 / screen_res * mipLevel ); - // Do a box sample. - hitColor = textureLod(source, screenCoord, mipLevel) * color; - hitColor += textureLod(source, -screenCoord, mipLevel) * color; - hitColor += textureLod(source, vec2(-screenCoord.x, screenCoord.y), mipLevel) * color; - hitColor += textureLod(source, vec2(screenCoord.x, -screenCoord.y), mipLevel) * color; - hitColor *= 0.25; - hitColor.a = 1; - hitDepth = depthFromScreen; + // Check for intersection: if absolute delta is within distanceBias. + if (abs(delta) < distanceBias) { + processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, + hitColor, hitDepth, edgeFade); hit = true; - - // Calculate edge fade even for successful hits - vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); - edgeFade = calculateEdgeFade(screenPosition); - - // Add depth discontinuity detection for improved quality - float depthGradient = 0.0; - vec2 texelSize = 1.0 / screen_res; - float depthRight = getLinearDepth(screenPosition + vec2(texelSize.x, 0)); - float depthBottom = getLinearDepth(screenPosition + vec2(0, texelSize.y)); - depthGradient = max(abs(depthRight - depthFromScreen), abs(depthBottom - depthFromScreen)); - - break; + break; // Found a hit. } - // Rest of your existing tracing code with adaptive steps - if (isBinarySearchEnabled && delta > 0) - { + // If ray overshot (delta > 0) and binary search is enabled, break to start binary search. + if (isBinarySearchEnabled && delta > 0.0) { break; } - if (isAdaptiveStepEnabled) - { - float directionSign = sign(abs(marchingPosition.z) - depthFromScreen); - step = step * (1.0 - rayStep * max(directionSign, 0.0)); - marchingPosition += step * (-directionSign); - } - else - { - marchingPosition += step; - } - if (isExponentialStepEnabled) - { - step *= adaptiveStepMultiplier; - } + // Advance the ray: calculate next step and update marchingPosition & baseStepVector. + advanceRayMarch(delta, marchingPosition, baseStepVector, + rayStep, isAdaptiveStepEnabled, isExponentialStepEnabled, adaptiveStepMultiplier); } - // Binary search refinement with edge fade calculation - if(isBinarySearchEnabled) - { - for(; i < iterationCount && !hit; i++) - { - step *= 0.5; - marchingPosition = marchingPosition - step * sign(delta); + // --- Binary Search Refinement Loop --- + // Perform binary search if enabled, the main loop overshot (delta > 0), and no hit was found yet. + // 'delta' and 'baseStepVector' hold their values from the last iteration of the main loop. + if (isBinarySearchEnabled && delta > 0.0 && !hit) { + vec3 binarySearchBaseStep = baseStepVector; // Start with the step that caused the overshoot. + + // Continue iteration count from where the main loop left off. + for (; i < int(iterationCount) && !hit; i++) { + binarySearchBaseStep *= 0.5; // Halve the step size for refinement. + // Move back or forward along the ray based on the sign of the last known delta. + // If delta > 0 (overshot), sign(delta) is 1, so we subtract (move back). + marchingPosition = marchingPosition - binarySearchBaseStep * sign(delta); screenPosition = generateProjectedPosition(marchingPosition); - if (screenPosition.x > 1 || screenPosition.x < 0 || - screenPosition.y > 1 || screenPosition.y < 0) - { - vec2 clampedPos = clamp(screenPosition, 0.0, 1.0); - vec2 distFromCenter = abs(clampedPos * 2.0 - 1.0); - edgeFade = calculateEdgeFade(screenPosition); - hitDepth = depthFromScreen; - hitColor.a = 0; + + if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { + hitColor.a = 0.0; // Indicate no valid color if off-screen. hit = false; break; } depthFromScreen = getLinearDepth(screenPosition); - delta = abs(marchingPosition.z) - depthFromScreen; + // Recalculate delta for this binary search iteration. + float currentBinarySearchDelta = abs(marchingPosition.z) - depthFromScreen; - if (depth < depthFromScreen + epsilon && depth > depthFromScreen - epsilon) - { - vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); + if (checkSelfIntersection(reflectingSurfaceViewDepth, depthFromScreen, epsilon)) { edgeFade = calculateEdgeFade(screenPosition); + hit = false; break; } - if (abs(delta) < distanceBias && depthFromScreen != (depth - distanceBias)) - { - vec4 color = vec4(1); - if(debugDraw) - color = vec4(0.5 + sign(delta)/2, 0.3, 0.5 - sign(delta)/2, 0); - - float mipLevel = calculateMipmapLevels(screen_res) * roughness; // Maps 0-1 roughness to 0-4 mip levels - vec2 screenCoord = screenPosition + (1 / screen_res * mipLevel); - // Do a box sample. - hitColor = textureLod(source, screenCoord, mipLevel) * color; - hitColor += textureLod(source, -screenCoord, mipLevel) * color; - hitColor += textureLod(source, vec2(-screenCoord.x, screenCoord.y), mipLevel) * color; - hitColor += textureLod(source, vec2(screenCoord.x, -screenCoord.y), mipLevel) * color; - hitColor *= 0.25; - hitDepth = depthFromScreen; - hitColor.a = 1; + // Check for hit using the refined delta. + // The condition `depthFromScreen != (reflectingSurfaceViewDepth - distanceBias)` seems specific + // and might be trying to avoid certain self-reflection scenarios or floating point issues. + if (abs(currentBinarySearchDelta) < distanceBias && + depthFromScreen != (reflectingSurfaceViewDepth - distanceBias)) { + processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, + hitColor, hitDepth, edgeFade); hit = true; - - vec2 distFromCenter = abs(screenPosition * 2.0 - 1.0); - edgeFade = calculateEdgeFade(screenPosition); - - // Add depth discontinuity detection for improved quality in binary search - float depthGradient = 0.0; - vec2 texelSize = 1.0 / screen_res; - float depthRight = getLinearDepth(screenPosition + vec2(texelSize.x, 0)); - float depthBottom = getLinearDepth(screenPosition + vec2(0, texelSize.y)); - depthGradient = max(abs(depthRight - depthFromScreen), abs(depthBottom - depthFromScreen)); - break; } + delta = currentBinarySearchDelta; // Update delta for the next binary search refinement step. } } } - return hit; + return hit; // Return whether a valid intersection was found. } -// Enhanced tapScreenSpaceReflection function -float tapScreenSpaceReflection(int totalSamples, vec2 tc, vec3 viewPos, vec3 n, inout vec4 collectedColor, sampler2D source, float glossiness) { +/** + * @brief Main function to compute screen space reflections for a given screen pixel. + * It traces multiple rays for glossy reflections, accumulates results, and applies various fading effects. + * + * @param totalSamples The desired number of samples for glossy reflections (can be adjusted internally). + * @param tc The texture coordinates of the current pixel being processed (screen space, 0-1). + * @param viewPos The view space position of the current pixel/surface. + * @param n The view space normal of the current pixel/surface. + * @param collectedColor Output: The accumulated reflection color. Alpha component is used for final fade/intensity. + * @param source The sampler2D (e.g., sceneMap) from which to sample reflection colors. + * @param glossiness The glossiness of the surface (0.0 for rough, 1.0 for perfectly smooth/mirror). + * @return float The number of rays that successfully hit a surface. + */ +float tapScreenSpaceReflection( + int totalSamples, + vec2 tc, + vec3 viewPos, + vec3 n, + inout vec4 collectedColor, + sampler2D source, + float glossiness) +{ #ifdef TRANSPARENT_SURFACE + // Early out for transparent surfaces if this define is active. + // Sets a magenta color for debugging. collectedColor = vec4(1, 0, 1, 1); - return 0; + return 0; // No hits for transparent surfaces. #endif - collectedColor = vec4(0); - int hits = 0; - float cumulativeFade = 0.0; - float averageHitDepth = 0.0; - float depth = -viewPos.z; - - // Flip normal if needed - if (dot(n, -viewPos) < 0.0) { - n = -n; - } + // Convert glossiness to roughness. Squaring roughness provides a more perceptually linear response. + float roughness = 1.0 - glossiness; + roughness = roughness * roughness; - vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); + // Only proceed if the surface is not perfectly rough. + if (roughness < 1.0) { // Max roughness might be 1.0, so < 1.0 means some reflectivity. + collectedColor = vec4(0.0); // Initialize accumulated color. + int hits = 0; // Counter for successful ray hits. + float cumulativeFade = 0.0; // Accumulator for edge fade factors from successful rays. + float averageHitDepth = 0.0; // Accumulator for hit depths. - vec2 uv2 = tc * screen_res; - float c = (uv2.x + uv2.y) * 0.125; - float jitter = mod(c, 1.0); + float currentPixelViewDepth = -viewPos.z; // View space depth of the reflecting pixel. + + // Ensure the normal is facing the camera. + if (dot(n, -viewPos) < 0.0) { + n = -n; + } - // Calculate a base edge fade for the current screen position - vec2 distFromCenter = abs(tc * 2.0 - 1.0); - float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - - float zFar = 250.0; - // Distance-based vignette - float distanceVignette = clamp((1.0+(viewPos.z/zFar)) * 2, 0.0, 1.0); + // Calculate the perfect reflection direction. + vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - // Improved roughness mapping - float roughness = 1.0 - glossiness; - //roughness = roughness * roughness; // More physically accurate roughness mapping + // Calculate a base edge fade for the current pixel's screen position. + // This fade is applied regardless of where the reflection rays go. + vec2 distFromCenter = abs(tc * 2.0 - 1.0); + float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - // Skip calculation entirely if we're right at the edge - if (baseEdgeFade > 0.01) { - // Calculate sample count based on roughness and edge fade - totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); - totalSamples = max(totalSamples, 1); - - for (int i = 0; i < totalSamples; i++) { - int poissonIndex = clamp(int(i + (random(tc) * (128 / totalSamples))), 0, 128); - vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * min((roughness * roughness * roughness * roughness), 0.001)); - - float hitDepth; - vec4 hitpoint; - float rayEdgeFade; - - bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpoint, hitDepth, depth, source, rayEdgeFade, roughness); - - if (hit) { - ++hits; - collectedColor.rgb += hitpoint.rgb; - collectedColor.a += 1.0; - cumulativeFade += rayEdgeFade; - averageHitDepth += hitDepth; + // Attenuation based on viewing angle (Fresnel-like effect). + // angleFactor is close to 1 for direct view, close to 0 for grazing angles. + float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); + float angleFactorSq = angleFactor * angleFactor; // Square to make falloff more pronounced. + + // Attenuation based on distance from the camera. + float distFadeDiv = 128.0; // Denominator for distance fade calculation. + float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 4.0, 0.0, 1.0); + + // Combine angle and distance factors for fading. + // The commented-out 'combinedFade' logic suggests experimentation with how these factors interact. + // The final used 'combinedFade' is simply 'distanceFactor'. + float combinedFade = distanceFactor; + + + // Skip reflection calculation entirely if the pixel is too close to the screen edge. + if (baseEdgeFade > 0.001) { + // Adjust the number of samples based on roughness and base edge fade. + // Smoother surfaces or surfaces away from edges get more samples. + totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); + totalSamples = max(totalSamples, 1); // Ensure at least one sample. + + for (int i = 0; i < totalSamples; i++) { + // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. + // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. + float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. + int poissonIndex = int(mod(float(i) + temporalOffset + random(tc * noiseSine) * 64.0, 128.0)); + + // Create an orthonormal basis around the main ray direction. + vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); + vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); + // Generate random coefficients to offset the ray within the cone defined by roughness. + vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); + // Cubic roughness term to make glossy reflections spread more at higher roughness values. + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness * roughness)); + + float hitDepthVal; // Stores depth of the hit for this ray. + vec4 hitpointColor; // Stores color of the hit for this ray. + float rayEdgeFadeVal; // Stores edge fade for this ray. + + // Trace the individual (potentially perturbed) reflection ray. + // Roughness is multiplied by 2 here, potentially to increase blur for glossy rays. + bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0); + + if (hit) { + ++hits; + collectedColor.rgb += hitpointColor.rgb; // Accumulate color. + collectedColor.a += 1.0; // Using alpha to count hits for averaging, or for intensity. + cumulativeFade += rayEdgeFadeVal; // Accumulate edge fade. + averageHitDepth += hitDepthVal; // Accumulate hit depth. + } } - } - if (hits > 0) { - collectedColor /= float(hits); - cumulativeFade /= float(hits); - averageHitDepth /= float(hits); - - } else { - collectedColor = vec4(0); - cumulativeFade = 0.0; + if (hits > 0) { + // Average the accumulated values if any rays hit. + collectedColor /= float(hits); // Average color. Note: alpha also gets divided. + cumulativeFade /= float(hits); // Average edge fade. + averageHitDepth /= float(hits); // Average hit depth. + } else { + // No hits, result is black and no fade. + collectedColor = vec4(0.0); + cumulativeFade = 0.0; + } + } else { // Pixel is too close to the edge (baseEdgeFade is ~0). + cumulativeFade = 0.0; // No reflection, so no cumulative fade. } - } else { - cumulativeFade = 0.0; + + // Apply final fading to the reflection. + // 'cumulativeFade' is the average edge fade of successful rays. + float finalEdgeFade = min(cumulativeFade * 1.0, 1.0); // Clamp fade. + + // Apply the combined distance/angle fade. + finalEdgeFade *= combinedFade; + + // Set the alpha of the collected color to the final combined fade factor. + // This alpha value will likely be used to blend the reflection with the underlying surface color. + // Commented-out lines suggest alternative ways to use roughness or fade for the final alpha. + collectedColor.a = finalEdgeFade; + return float(hits); // Return the number of successful ray hits. } - - // Apply final edge fading - float finalEdgeFade = min(cumulativeFade * 1, 1) * distanceVignette; - - // Apply Fresnel effect - vec3 viewDir = normalize(-viewPos); - float NdotV = max(dot(normalize(n), viewDir), 0.0); - float fresnel = pow(1.0 - NdotV, 5.0); // Schlick's approximation - //collectedColor.rgb = vec3(1.0 / hits / 2); - // Combine fades and adjust alpha - collectedColor.a = finalEdgeFade; - - return float(hits); + + return 0; // Surface is perfectly rough, no reflections calculated. } From 6eb00fb3bf90c725f08e2a21e3d617e2a874121b Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Fri, 6 Jun 2025 12:12:10 -0400 Subject: [PATCH 03/19] Make sure to sample as many probes as we can on water specifically when SSR is enabled. --- .../class3/deferred/screenSpaceReflUtil.glsl | 57 ++++++++++--------- .../shaders/class3/environment/waterF.glsl | 5 +- indra/newview/llviewershadermgr.cpp | 7 +++ 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index b117ede0249..dd9b93f86a3 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -30,8 +30,6 @@ // --- Uniforms --- // These are variables passed from the CPU to the shader. -/** @brief Sampler for the previous frame's scene color. Potentially used for temporal reprojection or other effects. */ -uniform sampler2D prevSceneMap; /** @brief Sampler for the current frame's scene color. This is what reflections will be sampled from. */ uniform sampler2D sceneMap; /** @brief Sampler for the current frame's scene depth. Used to find intersections during ray marching. */ @@ -41,12 +39,9 @@ uniform sampler2D sceneDepth; uniform vec2 screen_res; /** @brief The current view's projection matrix. Transforms view space coordinates to clip space. */ uniform mat4 projection_matrix; -//uniform float zNear; // Near clipping plane distance (commented out, likely unused or implicitly handled) -/** @brief Far clipping plane distance. Used in depth calculations. */ -uniform float zFar; /** @brief The inverse of the projection matrix. Transforms clip space coordinates back to view space. */ uniform mat4 inv_proj; -/** @brief Transformation matrix from the last frame's camera space to the current frame's camera space. Used for temporal reprojection or motion vector calculations. */ +/** @brief Transformation matrix from the last frame's camera space to the current frame's camera space. Used for temporal reprojection.*/ uniform mat4 modelview_delta; /** @brief Inverse of the modelview_delta matrix. Transforms current camera space back to last frame's camera space. */ uniform mat4 inv_modelview_delta; @@ -88,9 +83,7 @@ vec2 generateProjectedPosition(vec3 pos) return samplePosition.xy; } -// --- SSR Algorithm Configuration Flags --- // These booleans control various optimizations and features of the SSR algorithm. - /** @brief If true, a binary search step is performed to refine the hit point after an initial overshoot. */ bool isBinarySearchEnabled = true; /** @brief If true, the ray marching step size is adapted based on the distance to the nearest surface. */ @@ -100,8 +93,6 @@ bool isExponentialStepEnabled = true; /** @brief If true, debug colors are used to visualize aspects of the ray marching process (e.g., delta values). */ bool debugDraw = false; -// --- SSR Algorithm Parameters (controlled by debug settings) --- - /** @brief Maximum number of iterations for the ray marching loops. Debug setting: RenderScreenSpaceReflectionIterations */ uniform float iterationCount; /** @brief Initial base step size for ray marching. Debug setting: RenderScreenSpaceReflectionRayStep */ @@ -114,16 +105,16 @@ uniform float depthRejectBias; uniform float glossySampleCount; /** @brief Multiplier for the adaptive step size calculation. Debug setting: RenderScreenSpaceReflectionAdaptiveStepMultiplier */ uniform float adaptiveStepMultiplier; -/** @brief A time-varying sine value, likely used to introduce temporal variation in noise patterns (e.g., for Poisson disk sampling). */ +/** @brief A time-varying sine value, used to introduce temporal variation with the poisson sphere sampling. */ uniform float noiseSine; /** @brief A small constant value used for floating-point comparisons, typically to avoid issues with precision. Used in self-intersection checks. */ -float epsilon = 0.1; +float epsilon = 0.000001; /** * @brief Samples the scene depth texture and converts it to linear view space Z depth. * @param tc Texture coordinates (screen space, 0-1 range) at which to sample the depth. - * @return float The linear depth value (typically negative, representing distance from camera in view space). + * @return float The linear depth value. */ float getLinearDepth(vec2 tc) { @@ -456,6 +447,8 @@ void advanceRayMarch( } } +const float MAX_Z_DEPTH = 512; + /** * @brief Traces a single reflection ray through the scene using screen-space ray marching. * It attempts to find an intersection with the scene geometry represented by a depth buffer. @@ -519,6 +512,14 @@ bool traceScreenRay( // Get linear depth of the scene at the ray's projected screen position. depthFromScreen = getLinearDepth(screenPosition); + + // We store a max Z depth, and just kind of assume that the ray is intersecting with the sky if we hit it. + if (depthFromScreen >= MAX_Z_DEPTH) + { + hit = false; + break; + } + // Calculate delta: difference between ray's absolute Z and scene's depth. // Assumes marchingPosition.z and depthFromScreen are comparable (e.g., both positive view depths or both negative view Z). // Original code uses abs(marchingPosition.z), implying positive view depth convention. @@ -563,7 +564,6 @@ bool traceScreenRay( marchingPosition = marchingPosition - binarySearchBaseStep * sign(delta); screenPosition = generateProjectedPosition(marchingPosition); - if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { hitColor.a = 0.0; // Indicate no valid color if off-screen. hit = false; @@ -571,6 +571,12 @@ bool traceScreenRay( } depthFromScreen = getLinearDepth(screenPosition); + + if (depthFromScreen >= MAX_Z_DEPTH) + { + hit = false; + break; + } // Recalculate delta for this binary search iteration. float currentBinarySearchDelta = abs(marchingPosition.z) - depthFromScreen; @@ -608,7 +614,7 @@ bool traceScreenRay( * @param n The view space normal of the current pixel/surface. * @param collectedColor Output: The accumulated reflection color. Alpha component is used for final fade/intensity. * @param source The sampler2D (e.g., sceneMap) from which to sample reflection colors. - * @param glossiness The glossiness of the surface (0.0 for rough, 1.0 for perfectly smooth/mirror). + * @param glossiness The glossiness of the surface (0.0 for rough, 1.0 for perfectly smooth/mirror). Gets converted to roughness internally. * @return float The number of rays that successfully hit a surface. */ float tapScreenSpaceReflection( @@ -629,8 +635,7 @@ float tapScreenSpaceReflection( // Convert glossiness to roughness. Squaring roughness provides a more perceptually linear response. float roughness = 1.0 - glossiness; - roughness = roughness * roughness; - + //roughness = min(0.05, roughness); // Only proceed if the surface is not perfectly rough. if (roughness < 1.0) { // Max roughness might be 1.0, so < 1.0 means some reflectivity. collectedColor = vec4(0.0); // Initialize accumulated color. @@ -659,14 +664,14 @@ float tapScreenSpaceReflection( float angleFactorSq = angleFactor * angleFactor; // Square to make falloff more pronounced. // Attenuation based on distance from the camera. - float distFadeDiv = 128.0; // Denominator for distance fade calculation. - float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 4.0, 0.0, 1.0); + float distFadeDiv = 512.0 * angleFactor; // Denominator for distance fade calculation. + float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); // Combine angle and distance factors for fading. - // The commented-out 'combinedFade' logic suggests experimentation with how these factors interact. - // The final used 'combinedFade' is simply 'distanceFactor'. + // Also some nearby distance stuff to hide some up close artifacts. float combinedFade = distanceFactor; - + combinedFade *= 1 - angleFactorSq; + combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); // Skip reflection calculation entirely if the pixel is too close to the screen edge. if (baseEdgeFade > 0.001) { @@ -674,7 +679,7 @@ float tapScreenSpaceReflection( // Smoother surfaces or surfaces away from edges get more samples. totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); totalSamples = max(totalSamples, 1); // Ensure at least one sample. - + for (int i = 0; i < totalSamples; i++) { // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. @@ -687,14 +692,12 @@ float tapScreenSpaceReflection( // Generate random coefficients to offset the ray within the cone defined by roughness. vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); // Cubic roughness term to make glossy reflections spread more at higher roughness values. - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness * roughness)); + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness)); float hitDepthVal; // Stores depth of the hit for this ray. vec4 hitpointColor; // Stores color of the hit for this ray. float rayEdgeFadeVal; // Stores edge fade for this ray. - // Trace the individual (potentially perturbed) reflection ray. - // Roughness is multiplied by 2 here, potentially to increase blur for glossy rays. bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0); if (hit) { @@ -728,8 +731,6 @@ float tapScreenSpaceReflection( finalEdgeFade *= combinedFade; // Set the alpha of the collected color to the final combined fade factor. - // This alpha value will likely be used to blend the reflection with the underlying surface color. - // Commented-out lines suggest alternative ways to use roughness or fade for the final alpha. collectedColor.a = finalEdgeFade; return float(hits); // Return the number of successful ray hits. } diff --git a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl index 349f3b9a67d..ff15be28d8e 100644 --- a/indra/newview/app_settings/shaders/class3/environment/waterF.glsl +++ b/indra/newview/app_settings/shaders/class3/environment/waterF.glsl @@ -24,8 +24,11 @@ */ // class3/environment/waterF.glsl - +#ifdef SSR +#define WATER_MINIMAL_PLUS 1 +#else #define WATER_MINIMAL 1 +#endif out vec4 frag_color; diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index a0a9906724e..408148e1cbb 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -914,6 +914,7 @@ bool LLViewerShaderMgr::loadShadersWater() if (success) { + bool ssr = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); // load water shader gWaterProgram.mName = "Water Shader"; gWaterProgram.mFeatures.calculatesAtmospherics = true; @@ -922,6 +923,7 @@ bool LLViewerShaderMgr::loadShadersWater() gWaterProgram.mFeatures.hasSrgb = true; gWaterProgram.mFeatures.hasReflectionProbes = true; gWaterProgram.mFeatures.hasTonemap = true; + gWaterProgram.mFeatures.hasScreenSpaceReflections = ssr; gWaterProgram.mFeatures.hasShadows = use_sun_shadow; gWaterProgram.mShaderFiles.clear(); gWaterProgram.mShaderFiles.push_back(make_pair("environment/waterV.glsl", GL_VERTEX_SHADER)); @@ -936,6 +938,11 @@ bool LLViewerShaderMgr::loadShadersWater() { gWaterProgram.addPermutation("HAS_SUN_SHADOW", "1"); } + + if (ssr) + { + gWaterProgram.addPermutation("SSR", "1"); + } gWaterProgram.mShaderGroup = LLGLSLShader::SG_WATER; gWaterProgram.mShaderLevel = mShaderLevel[SHADER_WATER]; From 9ff815bba61c6759ae1c4f591e65f9d248214cc7 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sat, 7 Jun 2025 09:34:40 -0400 Subject: [PATCH 04/19] Start getting multi-pass tracing setup. --- indra/llrender/llshadermgr.cpp | 2 + indra/llrender/llshadermgr.h | 2 + indra/newview/app_settings/settings.xml | 70 ++++- .../class3/deferred/screenSpaceReflUtil.glsl | 262 ++++++++++-------- indra/newview/pipeline.cpp | 74 +++-- 5 files changed, 257 insertions(+), 153 deletions(-) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index 4807c122263..edae28d4557 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1349,6 +1349,8 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("glossySampleCount"); mReservedUniforms.push_back("noiseSine"); mReservedUniforms.push_back("adaptiveStepMultiplier"); + mReservedUniforms.push_back("splitParamsStart");; + mReservedUniforms.push_back("splitParamsEnd"); mReservedUniforms.push_back("modelview_delta"); mReservedUniforms.push_back("inv_modelview_delta"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 46788841a5d..8ceb140dea0 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -189,6 +189,8 @@ class LLShaderMgr DEFERRED_SSR_GLOSSY_SAMPLES, // "glossySampleCount" DEFERRED_SSR_NOISE_SINE, // "noiseSine" DEFERRED_SSR_ADAPTIVE_STEP_MULT, // "adaptiveStepMultiplier" + DEFERRED_SSR_SPLIT_START, // "splitParamsStart" + DEFERRED_SSR_SPLIT_END, // "splitParamsEnd" MODELVIEW_DELTA_MATRIX, // "modelview_delta" INVERSE_MODELVIEW_DELTA_MATRIX, // "inv_modelview_delta" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index e6abcf30193..fe704a8733d 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7592,9 +7592,13 @@ Persist 1 Type - S32 + Vector3 Value - 24 + + 24 + 24 + 24 + RenderScreenSpaceReflectionRayStep @@ -7603,9 +7607,13 @@ Persist 1 Type - F32 + Vector3 Value - 1.0 + + 1 + 1 + 1 + RenderScreenSpaceReflectionDistanceBias @@ -7614,9 +7622,13 @@ Persist 1 Type - F32 + Vector3 Value - 0.015 + + 0.015 + 0.015 + 0.015 + RenderScreenSpaceReflectionDepthRejectBias @@ -7625,9 +7637,13 @@ Persist 1 Type - F32 + Vector3 Value - 0.001 + + 0.001 + 0.001 + 0.001 + RenderScreenSpaceReflectionAdaptiveStepMultiplier @@ -7636,9 +7652,43 @@ Persist 1 Type - F32 + Vector3 + Value + + 1.2 + 1.3 + 1.4 + + + RenderScreenSpaceReflectionSplitStart + + Comment + Starting splits for each SSR pass. + Persist + 1 + Type + Vector3 Value - 1.6 + + 0 + 25 + 100 + + + RenderScreenSpaceReflectionSplitEnd + + Comment + Ending splits for each SSR pass. + Persist + 1 + Type + Vector3 + Value + + 25 + 100 + 256 + RenderScreenSpaceReflectionGlossySamples diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index dd9b93f86a3..b0873753330 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -93,18 +93,17 @@ bool isExponentialStepEnabled = true; /** @brief If true, debug colors are used to visualize aspects of the ray marching process (e.g., delta values). */ bool debugDraw = false; -/** @brief Maximum number of iterations for the ray marching loops. Debug setting: RenderScreenSpaceReflectionIterations */ -uniform float iterationCount; -/** @brief Initial base step size for ray marching. Debug setting: RenderScreenSpaceReflectionRayStep */ -uniform float rayStep; -/** @brief Threshold for considering a ray hit. If the distance between the ray and the scene surface is less than this, it's a hit. Debug setting: RenderScreenSpaceReflectionDistanceBias */ -uniform float distanceBias; -/** @brief Bias to prevent self-reflection or reflections from surfaces very close to the ray origin. Rays originating from surfaces shallower than this bias are rejected. Debug setting: RenderScreenSpaceReflectionDepthRejectionBias */ -uniform float depthRejectBias; +// Near Pass Uniforms +uniform vec3 iterationCount; +uniform vec3 rayStep; +uniform vec3 distanceBias; +uniform vec3 depthRejectBias; +uniform vec3 adaptiveStepMultiplier; +uniform vec3 splitParamsStart; +uniform vec3 splitParamsEnd; + /** @brief Number of samples to take for glossy reflections. Higher values give smoother but more expensive reflections. Debug setting: RenderScreenSpaceReflectionGlossySamples */ uniform float glossySampleCount; -/** @brief Multiplier for the adaptive step size calculation. Debug setting: RenderScreenSpaceReflectionAdaptiveStepMultiplier */ -uniform float adaptiveStepMultiplier; /** @brief A time-varying sine value, used to introduce temporal variation with the poisson sphere sampling. */ uniform float noiseSine; @@ -471,7 +470,13 @@ bool traceScreenRay( out float hitDepth, sampler2D source, out float edgeFade, - float roughness) + float roughness, + int passIterationCount, + float passRayStep, + float passDistanceBias, + float passDepthRejectBias, + float passAdaptiveStepMultiplier +) { // Transform initialPosition and the target of initialReflection vector from current camera space to previous camera space. // This is crucial if 'source' texture and 'sceneDepth' are from the previous frame's perspective, @@ -486,7 +491,7 @@ bool traceScreenRay( // Initialize ray marching variables. // 'baseStepVector' starts with 'rayStep' magnitude in the direction of the (transformed) reflection. - vec3 baseStepVector = rayStep * reflectionVector_transformed; + vec3 baseStepVector = passRayStep * reflectionVector_transformed; vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. float delta; // Difference between ray's Z depth and scene's Z depth at the projected screen position. @@ -498,9 +503,9 @@ bool traceScreenRay( int i = 0; // Iteration counter. // Only trace if the reflecting surface itself isn't too shallow (depthRejectBias). - if (reflectingSurfaceViewDepth > depthRejectBias) { + if (reflectingSurfaceViewDepth > passDepthRejectBias) { // --- Main Ray Marching Loop --- - for (; i < int(iterationCount) && !hit; i++) { + for (; i < int(passIterationCount) && !hit; i++) { // Project current 3D marching position to 2D screen space. screenPosition = generateProjectedPosition(marchingPosition); @@ -533,7 +538,7 @@ bool traceScreenRay( } // Check for intersection: if absolute delta is within distanceBias. - if (abs(delta) < distanceBias) { + if (abs(delta) < passDistanceBias) { processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, hitColor, hitDepth, edgeFade); hit = true; @@ -547,7 +552,7 @@ bool traceScreenRay( // Advance the ray: calculate next step and update marchingPosition & baseStepVector. advanceRayMarch(delta, marchingPosition, baseStepVector, - rayStep, isAdaptiveStepEnabled, isExponentialStepEnabled, adaptiveStepMultiplier); + passRayStep, isAdaptiveStepEnabled, isExponentialStepEnabled, passAdaptiveStepMultiplier); } // --- Binary Search Refinement Loop --- @@ -557,7 +562,7 @@ bool traceScreenRay( vec3 binarySearchBaseStep = baseStepVector; // Start with the step that caused the overshoot. // Continue iteration count from where the main loop left off. - for (; i < int(iterationCount) && !hit; i++) { + for (; i < int(passIterationCount) && !hit; i++) { binarySearchBaseStep *= 0.5; // Halve the step size for refinement. // Move back or forward along the ray based on the sign of the last known delta. // If delta > 0 (overshot), sign(delta) is 1, so we subtract (move back). @@ -589,8 +594,8 @@ bool traceScreenRay( // Check for hit using the refined delta. // The condition `depthFromScreen != (reflectingSurfaceViewDepth - distanceBias)` seems specific // and might be trying to avoid certain self-reflection scenarios or floating point issues. - if (abs(currentBinarySearchDelta) < distanceBias && - depthFromScreen != (reflectingSurfaceViewDepth - distanceBias)) { + if (abs(currentBinarySearchDelta) < passDistanceBias && + depthFromScreen != (reflectingSurfaceViewDepth - passDistanceBias)) { processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, hitColor, hitDepth, edgeFade); hit = true; @@ -604,6 +609,127 @@ bool traceScreenRay( return hit; // Return whether a valid intersection was found. } +/** + * @brief Handles the actual tracing logic for screen space reflections, including multi-sample glossy reflections. + * + * @param viewPos The view space position of the current pixel/surface. + * @param normal The view space normal of the current pixel/surface. + * @param tc The texture coordinates of the current pixel being processed (screen space, 0-1). + * @param tracedColor Output: The accumulated reflection color. Alpha component is used for final fade/intensity. + * @param source The sampler2D (e.g., sceneMap) from which to sample reflection colors. + * @param roughness The roughness of the surface (0.0 for smooth, 1.0 for rough). + * @param iterations The number of ray marching iterations to perform. + * @param passRayStep The step size for ray marching. + * @param passDistanceBias The distance bias for intersection detection. + * @param passDepthRejectBias The depth rejection bias. + * @param passAdaptiveStepMultiplier The multiplier for adaptive stepping. + * @return bool True if at least one ray hit was successful, false otherwise. + */ +bool tracePass(vec3 viewPos, vec3 normal, vec2 tc, inout vec4 tracedColor, sampler2D source, + float roughness, int iterations, float passRayStep, float passDistanceBias, + float passDepthRejectBias, float passAdaptiveStepMultiplier) +{ + tracedColor = vec4(0.0); // Initialize accumulated color. + int hits = 0; // Counter for successful ray hits. + float cumulativeFade = 0.0; // Accumulator for edge fade factors from successful rays. + float averageHitDepth = 0.0; // Accumulator for hit depths. + + float currentPixelViewDepth = -viewPos.z; // View space depth of the reflecting pixel. + + // Ensure the normal is facing the camera. + vec3 n = normal; + if (dot(n, -viewPos) < 0.0) { + n = -n; + } + + // Calculate the perfect reflection direction. + vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); + + // Calculate a base edge fade for the current pixel's screen position. + // This fade is applied regardless of where the reflection rays go. + vec2 distFromCenter = abs(tc * 2.0 - 1.0); + float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); + + // Attenuation based on viewing angle (Fresnel-like effect). + // angleFactor is close to 1 for direct view, close to 0 for grazing angles. + float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); + float angleFactorSq = angleFactor * angleFactor; // Square to make falloff more pronounced. + + // Attenuation based on distance from the camera. + float distFadeDiv = 512.0 * angleFactor; // Denominator for distance fade calculation. + float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); + + // Combine angle and distance factors for fading. + // Also some nearby distance stuff to hide some up close artifacts. + float combinedFade = distanceFactor; + combinedFade *= 1 - angleFactorSq; + combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); + + // Skip reflection calculation entirely if the pixel is too close to the screen edge. + if (baseEdgeFade > 0.001) { + // Adjust the number of samples based on roughness and base edge fade. + // Smoother surfaces or surfaces away from edges get more samples. + int totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); + totalSamples = max(totalSamples, 1); // Ensure at least one sample. + + for (int i = 0; i < totalSamples; i++) { + // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. + // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. + float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. + int poissonIndex = int(mod(float(i) + temporalOffset + random(tc * noiseSine) * 64.0, 128.0)); + + // Create an orthonormal basis around the main ray direction. + vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); + vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); + // Generate random coefficients to offset the ray within the cone defined by roughness. + vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); + // Cubic roughness term to make glossy reflections spread more at higher roughness values. + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness)); + + float hitDepthVal; // Stores depth of the hit for this ray. + vec4 hitpointColor; // Stores color of the hit for this ray. + float rayEdgeFadeVal; // Stores edge fade for this ray. + + bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, + hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0, + iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier); + + if (hit) { + ++hits; + tracedColor.rgb += hitpointColor.rgb; // Accumulate color. + tracedColor.a += 1.0; // Using alpha to count hits for averaging, or for intensity. + cumulativeFade += rayEdgeFadeVal; // Accumulate edge fade. + averageHitDepth += hitDepthVal; // Accumulate hit depth. + } + } + + if (hits > 0) { + // Average the accumulated values if any rays hit. + tracedColor /= float(hits); // Average color. Note: alpha also gets divided. + cumulativeFade /= float(hits); // Average edge fade. + averageHitDepth /= float(hits); // Average hit depth. + } else { + // No hits, result is black and no fade. + tracedColor = vec4(0.0); + cumulativeFade = 0.0; + } + } else { // Pixel is too close to the edge (baseEdgeFade is ~0). + cumulativeFade = 0.0; // No reflection, so no cumulative fade. + } + + // Apply final fading to the reflection. + // 'cumulativeFade' is the average edge fade of successful rays. + float finalEdgeFade = min(cumulativeFade * 1.0, 1.0); // Clamp fade. + + // Apply the combined distance/angle fade. + finalEdgeFade *= combinedFade; + + // Set the alpha of the collected color to the final combined fade factor. + tracedColor.a = finalEdgeFade; + + return hits > 0; // Return true if at least one ray hit was successful. +} + /** * @brief Main function to compute screen space reflections for a given screen pixel. * It traces multiple rays for glossy reflections, accumulates results, and applies various fading effects. @@ -638,101 +764,11 @@ float tapScreenSpaceReflection( //roughness = min(0.05, roughness); // Only proceed if the surface is not perfectly rough. if (roughness < 1.0) { // Max roughness might be 1.0, so < 1.0 means some reflectivity. - collectedColor = vec4(0.0); // Initialize accumulated color. - int hits = 0; // Counter for successful ray hits. - float cumulativeFade = 0.0; // Accumulator for edge fade factors from successful rays. - float averageHitDepth = 0.0; // Accumulator for hit depths. - - float currentPixelViewDepth = -viewPos.z; // View space depth of the reflecting pixel. - - // Ensure the normal is facing the camera. - if (dot(n, -viewPos) < 0.0) { - n = -n; - } - - // Calculate the perfect reflection direction. - vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - - // Calculate a base edge fade for the current pixel's screen position. - // This fade is applied regardless of where the reflection rays go. - vec2 distFromCenter = abs(tc * 2.0 - 1.0); - float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - - // Attenuation based on viewing angle (Fresnel-like effect). - // angleFactor is close to 1 for direct view, close to 0 for grazing angles. - float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); - float angleFactorSq = angleFactor * angleFactor; // Square to make falloff more pronounced. - - // Attenuation based on distance from the camera. - float distFadeDiv = 512.0 * angleFactor; // Denominator for distance fade calculation. - float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); - - // Combine angle and distance factors for fading. - // Also some nearby distance stuff to hide some up close artifacts. - float combinedFade = distanceFactor; - combinedFade *= 1 - angleFactorSq; - combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); - - // Skip reflection calculation entirely if the pixel is too close to the screen edge. - if (baseEdgeFade > 0.001) { - // Adjust the number of samples based on roughness and base edge fade. - // Smoother surfaces or surfaces away from edges get more samples. - totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); - totalSamples = max(totalSamples, 1); // Ensure at least one sample. - - for (int i = 0; i < totalSamples; i++) { - // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. - // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. - float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. - int poissonIndex = int(mod(float(i) + temporalOffset + random(tc * noiseSine) * 64.0, 128.0)); - - // Create an orthonormal basis around the main ray direction. - vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - // Generate random coefficients to offset the ray within the cone defined by roughness. - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - // Cubic roughness term to make glossy reflections spread more at higher roughness values. - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness)); - - float hitDepthVal; // Stores depth of the hit for this ray. - vec4 hitpointColor; // Stores color of the hit for this ray. - float rayEdgeFadeVal; // Stores edge fade for this ray. - - bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0); - - if (hit) { - ++hits; - collectedColor.rgb += hitpointColor.rgb; // Accumulate color. - collectedColor.a += 1.0; // Using alpha to count hits for averaging, or for intensity. - cumulativeFade += rayEdgeFadeVal; // Accumulate edge fade. - averageHitDepth += hitDepthVal; // Accumulate hit depth. - } - } - - if (hits > 0) { - // Average the accumulated values if any rays hit. - collectedColor /= float(hits); // Average color. Note: alpha also gets divided. - cumulativeFade /= float(hits); // Average edge fade. - averageHitDepth /= float(hits); // Average hit depth. - } else { - // No hits, result is black and no fade. - collectedColor = vec4(0.0); - cumulativeFade = 0.0; - } - } else { // Pixel is too close to the edge (baseEdgeFade is ~0). - cumulativeFade = 0.0; // No reflection, so no cumulative fade. - } - - // Apply final fading to the reflection. - // 'cumulativeFade' is the average edge fade of successful rays. - float finalEdgeFade = min(cumulativeFade * 1.0, 1.0); // Clamp fade. - - // Apply the combined distance/angle fade. - finalEdgeFade *= combinedFade; + bool hasHits = tracePass(viewPos, n, tc, collectedColor, source, roughness, + int(iterationCount.x), rayStep.x, distanceBias.x, + depthRejectBias.x, adaptiveStepMultiplier.x); - // Set the alpha of the collected color to the final combined fade factor. - collectedColor.a = finalEdgeFade; - return float(hits); // Return the number of successful ray hits. + return hasHits ? 1.0 : 0.0; // Return 1.0 if we had hits, 0.0 otherwise. } return 0; // Surface is perfectly rough, no reflections calculated. diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index d480b6d00c6..de114bda235 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -286,6 +286,14 @@ static LLStaticHashedString sKern("kern"); static LLStaticHashedString sKernScale("kern_scale"); static LLStaticHashedString sSmaaRTMetrics("SMAA_RT_METRICS"); +static LLStaticHashedString sTraceIterations("iterationCount"); +static LLStaticHashedString sTraceRayStep("trayStep"); +static LLStaticHashedString sTraceDistanceBias("distanceBias"); +static LLStaticHashedString sTraceDepthRejectBias("depthRejectBias"); +static LLStaticHashedString sTraceStepMultiplier("adaptiveStepMultiplier"); +static LLStaticHashedString sTraceSplitParamsStart("splitParamsStart"); +static LLStaticHashedString sTraceSplitParamsEnd("splitParamsEnd"); + //---------------------------------------- void drawBox(const LLVector4a& c, const LLVector4a& r); @@ -1120,11 +1128,6 @@ void LLPipeline::refreshCachedSettings() CameraDoFResScale = gSavedSettings.getF32("CameraDoFResScale"); RenderAutoHideSurfaceAreaLimit = gSavedSettings.getF32("RenderAutoHideSurfaceAreaLimit"); RenderScreenSpaceReflections = gSavedSettings.getBOOL("RenderScreenSpaceReflections"); - RenderScreenSpaceReflectionIterations = gSavedSettings.getS32("RenderScreenSpaceReflectionIterations"); - RenderScreenSpaceReflectionRayStep = gSavedSettings.getF32("RenderScreenSpaceReflectionRayStep"); - RenderScreenSpaceReflectionDistanceBias = gSavedSettings.getF32("RenderScreenSpaceReflectionDistanceBias"); - RenderScreenSpaceReflectionDepthRejectBias = gSavedSettings.getF32("RenderScreenSpaceReflectionDepthRejectBias"); - RenderScreenSpaceReflectionAdaptiveStepMultiplier = gSavedSettings.getF32("RenderScreenSpaceReflectionAdaptiveStepMultiplier"); RenderScreenSpaceReflectionGlossySamples = gSavedSettings.getS32("RenderScreenSpaceReflectionGlossySamples"); RenderBufferVisualization = gSavedSettings.getS32("RenderBufferVisualization"); RenderMirrors = gSavedSettings.getBOOL("RenderMirrors"); @@ -9318,34 +9321,45 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) setEnvMat(shader); } - // reflection probe shaders generally sample the scene map as well for SSR - channel = shader.enableTexture(LLShaderMgr::SCENE_MAP); - if (channel > -1) - { - gGL.getTexUnit(channel)->bind(&mSceneMap); - } - - - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_ITR_COUNT, (GLfloat)RenderScreenSpaceReflectionIterations); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_DIST_BIAS, RenderScreenSpaceReflectionDistanceBias); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_RAY_STEP, RenderScreenSpaceReflectionRayStep); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_GLOSSY_SAMPLES, (GLfloat)RenderScreenSpaceReflectionGlossySamples); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_REJECT_BIAS, RenderScreenSpaceReflectionDepthRejectBias); - mPoissonOffset++; - - if (mPoissonOffset > 128 - RenderScreenSpaceReflectionGlossySamples) - mPoissonOffset = 0; - - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_NOISE_SINE, (GLfloat)mPoissonOffset); - shader.uniform1f(LLShaderMgr::DEFERRED_SSR_ADAPTIVE_STEP_MULT, RenderScreenSpaceReflectionAdaptiveStepMultiplier); - channel = shader.enableTexture(LLShaderMgr::SCENE_DEPTH); - if (channel > -1) + if (RenderScreenSpaceReflections) { - gGL.getTexUnit(channel)->bind(&mSceneMap, true); + // reflection probe shaders generally sample the scene map as well for SSR + channel = shader.enableTexture(LLShaderMgr::SCENE_MAP); + if (channel > -1) + { + gGL.getTexUnit(channel)->bind(&mSceneMap); + } + + static LLCachedControl traceIterations(gSavedSettings, "RenderScreenSpaceReflectionIterations"); + static LLCachedControl traceSteps(gSavedSettings, "RenderScreenSpaceReflectionRayStep"); + static LLCachedControl traceDistanceBias(gSavedSettings, "RenderScreenSpaceReflectionDistanceBias"); + static LLCachedControl traceRejectBias(gSavedSettings, "RenderScreenSpaceReflectionDepthRejectBias"); + static LLCachedControl traceStepMultiplier(gSavedSettings, "RenderScreenSpaceReflectionAdaptiveStepMultiplier"); + static LLCachedControl traceSplitStart(gSavedSettings, "RenderScreenSpaceReflectionSplitStart"); + static LLCachedControl traceSplitEnd(gSavedSettings, "RenderScreenSpaceReflectionSplitEnd"); + + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_ITR_COUNT, 1, traceIterations().mV); + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_DIST_BIAS, 1, traceDistanceBias().mV); + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_RAY_STEP, 1, traceSteps().mV); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_GLOSSY_SAMPLES, (GLfloat)RenderScreenSpaceReflectionGlossySamples); + shader.uniform3fv(sTraceDepthRejectBias, 1, traceRejectBias().mV); + mPoissonOffset++; + + if (mPoissonOffset > 128 - RenderScreenSpaceReflectionGlossySamples) + mPoissonOffset = 0; + + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_NOISE_SINE, (GLfloat)mPoissonOffset); + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_ADAPTIVE_STEP_MULT, 1, traceStepMultiplier().mV); + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_START, 1, traceSplitStart().mV); + shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_END, 1, traceSplitEnd().mV); + + channel = shader.enableTexture(LLShaderMgr::SCENE_DEPTH); + if (channel > -1) + { + gGL.getTexUnit(channel)->bind(&mSceneMap, true); + } } - - } void LLPipeline::unbindReflectionProbes(LLGLSLShader& shader) From 3fb627aa6fba7d2635ac8c44f8073d3182d385b7 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sat, 7 Jun 2025 11:32:01 -0400 Subject: [PATCH 05/19] Insert fifth element reference here --- indra/newview/app_settings/settings.xml | 14 +- .../class3/deferred/screenSpaceReflUtil.glsl | 282 +++++++++++------- 2 files changed, 176 insertions(+), 120 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index fe704a8733d..93fafff7563 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7595,9 +7595,9 @@ Vector3 Value - 24 - 24 - 24 + 48 + 16 + 8 RenderScreenSpaceReflectionRayStep @@ -7625,7 +7625,7 @@ Vector3 Value - 0.015 + 0.003 0.015 0.015 @@ -7655,8 +7655,8 @@ Vector3 Value + 1.155 1.2 - 1.3 1.4 @@ -7671,7 +7671,7 @@ Value 0 - 25 + 15 100 @@ -7685,7 +7685,7 @@ Vector3 Value - 25 + 15 100 256 diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index b0873753330..6f2dfda6241 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -158,7 +158,7 @@ float calculateEdgeFade(vec2 screenPos) { /** @brief Predefined array of 128 3D Poisson disk sample offsets. Used for generating distributed samples, e.g., for glossy reflections. Values are in [0,1] range. */ uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( // ... (array values as provided) ... - vec3(0.5433144, 0.1122154, 0.2501391), + vec3(0.0581234, 0.1254093, 0.0214785), vec3(0.6575254, 0.721409, 0.16286), vec3(0.02888453, 0.05170321, 0.7573566), vec3(0.06635678, 0.8286457, 0.07157445), @@ -295,7 +295,7 @@ uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( */ vec3 getPoissonSample(int i) { // Samples are stored in [0,1], scale and shift to [-1,1]. - return POISSON3D_SAMPLES[i] * 2.0 - 1.0; + return POISSON3D_SAMPLES[i] * 2.0 - 1.0 / 2; } /** @@ -448,6 +448,20 @@ void advanceRayMarch( const float MAX_Z_DEPTH = 512; +/** + * @brief Checks if a hit point is within the specified distance range from the reflector. + * @param reflectorDepth The depth of the reflecting surface. + * @param hitDepth The depth of the hit point. + * @param rangeStart The start of the valid range (minimum distance from reflector). + * @param rangeEnd The end of the valid range (maximum distance from reflector). + * @return bool True if the hit is within the valid range, false otherwise. + */ +bool isWithinReflectionRange(float reflectorDepth, float hitDepth, float rangeStart, float rangeEnd) +{ + float distanceFromReflector = abs(hitDepth - reflectorDepth); + return distanceFromReflector >= rangeStart && distanceFromReflector <= rangeEnd; +} + /** * @brief Traces a single reflection ray through the scene using screen-space ray marching. * It attempts to find an intersection with the scene geometry represented by a depth buffer. @@ -475,7 +489,8 @@ bool traceScreenRay( float passRayStep, float passDistanceBias, float passDepthRejectBias, - float passAdaptiveStepMultiplier + float passAdaptiveStepMultiplier, + vec2 distanceParams ) { // Transform initialPosition and the target of initialReflection vector from current camera space to previous camera space. @@ -489,6 +504,10 @@ bool traceScreenRay( // Depth of the reflecting surface in the transformed view space. float reflectingSurfaceViewDepth = -currentPosition_transformed.z; + // Extract range parameters + float rangeStart = distanceParams.x; + float rangeEnd = distanceParams.y; + // Initialize ray marching variables. // 'baseStepVector' starts with 'rayStep' magnitude in the direction of the (transformed) reflection. vec3 baseStepVector = passRayStep * reflectionVector_transformed; @@ -539,10 +558,12 @@ bool traceScreenRay( // Check for intersection: if absolute delta is within distanceBias. if (abs(delta) < passDistanceBias) { - processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, - hitColor, hitDepth, edgeFade); - hit = true; - break; // Found a hit. + if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { + processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, + hitColor, hitDepth, edgeFade); + hit = true; + break; // Found a hit. + } } // If ray overshot (delta > 0) and binary search is enabled, break to start binary search. @@ -596,10 +617,12 @@ bool traceScreenRay( // and might be trying to avoid certain self-reflection scenarios or floating point issues. if (abs(currentBinarySearchDelta) < passDistanceBias && depthFromScreen != (reflectingSurfaceViewDepth - passDistanceBias)) { - processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, - hitColor, hitDepth, edgeFade); - hit = true; - break; + if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { + processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, + hitColor, hitDepth, edgeFade); + hit = true; + break; + } } delta = currentBinarySearchDelta; // Update delta for the next binary search refinement step. } @@ -613,7 +636,7 @@ bool traceScreenRay( * @brief Handles the actual tracing logic for screen space reflections, including multi-sample glossy reflections. * * @param viewPos The view space position of the current pixel/surface. - * @param normal The view space normal of the current pixel/surface. + * @param rayDirection The perfect reflection direction (already calculated). * @param tc The texture coordinates of the current pixel being processed (screen space, 0-1). * @param tracedColor Output: The accumulated reflection color. Alpha component is used for final fade/intensity. * @param source The sampler2D (e.g., sceneMap) from which to sample reflection colors. @@ -625,107 +648,62 @@ bool traceScreenRay( * @param passAdaptiveStepMultiplier The multiplier for adaptive stepping. * @return bool True if at least one ray hit was successful, false otherwise. */ -bool tracePass(vec3 viewPos, vec3 normal, vec2 tc, inout vec4 tracedColor, sampler2D source, +bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, sampler2D source, float roughness, int iterations, float passRayStep, float passDistanceBias, - float passDepthRejectBias, float passAdaptiveStepMultiplier) + float passDepthRejectBias, float passAdaptiveStepMultiplier, vec2 distanceParams) { tracedColor = vec4(0.0); // Initialize accumulated color. int hits = 0; // Counter for successful ray hits. float cumulativeFade = 0.0; // Accumulator for edge fade factors from successful rays. float averageHitDepth = 0.0; // Accumulator for hit depths. - float currentPixelViewDepth = -viewPos.z; // View space depth of the reflecting pixel. + // Adjust the number of samples based on roughness. + // Smoother surfaces get more samples. + int totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness))); + totalSamples = max(totalSamples, 1); // Ensure at least one sample. - // Ensure the normal is facing the camera. - vec3 n = normal; - if (dot(n, -viewPos) < 0.0) { - n = -n; - } - - // Calculate the perfect reflection direction. - vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - - // Calculate a base edge fade for the current pixel's screen position. - // This fade is applied regardless of where the reflection rays go. - vec2 distFromCenter = abs(tc * 2.0 - 1.0); - float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - - // Attenuation based on viewing angle (Fresnel-like effect). - // angleFactor is close to 1 for direct view, close to 0 for grazing angles. - float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); - float angleFactorSq = angleFactor * angleFactor; // Square to make falloff more pronounced. - - // Attenuation based on distance from the camera. - float distFadeDiv = 512.0 * angleFactor; // Denominator for distance fade calculation. - float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); - - // Combine angle and distance factors for fading. - // Also some nearby distance stuff to hide some up close artifacts. - float combinedFade = distanceFactor; - combinedFade *= 1 - angleFactorSq; - combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); - - // Skip reflection calculation entirely if the pixel is too close to the screen edge. - if (baseEdgeFade > 0.001) { - // Adjust the number of samples based on roughness and base edge fade. - // Smoother surfaces or surfaces away from edges get more samples. - int totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness) * baseEdgeFade)); - totalSamples = max(totalSamples, 1); // Ensure at least one sample. + for (int i = 0; i < totalSamples; i++) { + // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. + // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. + float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. + int poissonIndex = int(mod(float(i) + random(tc * noiseSine) * 64.0, 128.0)); - for (int i = 0; i < totalSamples; i++) { - // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. - // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. - float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. - int poissonIndex = int(mod(float(i) + temporalOffset + random(tc * noiseSine) * 64.0, 128.0)); - - // Create an orthonormal basis around the main ray direction. - vec3 firstBasis = normalize(cross(getPoissonSample(poissonIndex), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - // Generate random coefficients to offset the ray within the cone defined by roughness. - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - // Cubic roughness term to make glossy reflections spread more at higher roughness values. - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness)); - - float hitDepthVal; // Stores depth of the hit for this ray. - vec4 hitpointColor; // Stores color of the hit for this ray. - float rayEdgeFadeVal; // Stores edge fade for this ray. - - bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, - hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0, - iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier); - - if (hit) { - ++hits; - tracedColor.rgb += hitpointColor.rgb; // Accumulate color. - tracedColor.a += 1.0; // Using alpha to count hits for averaging, or for intensity. - cumulativeFade += rayEdgeFadeVal; // Accumulate edge fade. - averageHitDepth += hitDepthVal; // Accumulate hit depth. - } - } - - if (hits > 0) { - // Average the accumulated values if any rays hit. - tracedColor /= float(hits); // Average color. Note: alpha also gets divided. - cumulativeFade /= float(hits); // Average edge fade. - averageHitDepth /= float(hits); // Average hit depth. - } else { - // No hits, result is black and no fade. - tracedColor = vec4(0.0); - cumulativeFade = 0.0; + // Create an orthonormal basis around the main ray direction. + vec3 firstBasis = normalize(cross(getPoissonSample(i), rayDirection)); + vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); + // Generate random coefficients to offset the ray within the cone defined by roughness. + vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); + // Cubic roughness term to make glossy reflections spread more at higher roughness values. + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness ) / 4); + + float hitDepthVal; // Stores depth of the hit for this ray. + vec4 hitpointColor; // Stores color of the hit for this ray. + float rayEdgeFadeVal; // Stores edge fade for this ray. + + bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, + hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0, + iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier, distanceParams); + + if (hit) { + ++hits; + tracedColor.rgb += hitpointColor.rgb; // Accumulate color. + tracedColor.a += 1.0; // Using alpha to count hits for averaging, or for intensity. + cumulativeFade += rayEdgeFadeVal; // Accumulate edge fade. + averageHitDepth += hitDepthVal; // Accumulate hit depth. } - } else { // Pixel is too close to the edge (baseEdgeFade is ~0). - cumulativeFade = 0.0; // No reflection, so no cumulative fade. } - - // Apply final fading to the reflection. - // 'cumulativeFade' is the average edge fade of successful rays. - float finalEdgeFade = min(cumulativeFade * 1.0, 1.0); // Clamp fade. - // Apply the combined distance/angle fade. - finalEdgeFade *= combinedFade; - - // Set the alpha of the collected color to the final combined fade factor. - tracedColor.a = finalEdgeFade; + if (hits > 0) { + // Average the accumulated values if any rays hit. + tracedColor /= float(hits); // Average color. Note: alpha also gets divided. + cumulativeFade /= float(hits); // Average edge fade. + averageHitDepth /= float(hits); // Average hit depth. + // Store the average edge fade in the alpha channel for the caller to use. + tracedColor.a = cumulativeFade; + } else { + // No hits, result is black and no fade. + tracedColor = vec4(0.0); + } return hits > 0; // Return true if at least one ray hit was successful. } @@ -753,23 +731,101 @@ float tapScreenSpaceReflection( float glossiness) { #ifdef TRANSPARENT_SURFACE - // Early out for transparent surfaces if this define is active. - // Sets a magenta color for debugging. collectedColor = vec4(1, 0, 1, 1); - return 0; // No hits for transparent surfaces. + return 0; #endif - // Convert glossiness to roughness. Squaring roughness provides a more perceptually linear response. float roughness = 1.0 - glossiness; - //roughness = min(0.05, roughness); - // Only proceed if the surface is not perfectly rough. - if (roughness < 1.0) { // Max roughness might be 1.0, so < 1.0 means some reflectivity. - bool hasHits = tracePass(viewPos, n, tc, collectedColor, source, roughness, - int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x); + + if (roughness < 1.0) { + float currentPixelViewDepth = -viewPos.z; + + vec2 distFromCenter = abs(tc * 2.0 - 1.0); + float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - return hasHits ? 1.0 : 0.0; // Return 1.0 if we had hits, 0.0 otherwise. + if (baseEdgeFade > 0.001) { + vec3 correctedNormal = n; + if (dot(correctedNormal, -viewPos) < 0.0) { + correctedNormal = -correctedNormal; + } + + vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(correctedNormal))); + + float angleFactor = abs(dot(normalize(-viewPos), normalize(correctedNormal))); + float angleFactorSq = angleFactor * angleFactor; + + float distFadeDiv = 512.0 * angleFactor; + float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); + + float combinedFade = 1;// distanceFactor; + combinedFade *= 1 - angleFactorSq; + //combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); + + vec4 nearColor = vec4(0.0); + vec4 midColor = vec4(0.0); + vec4 farColor = vec4(0.0); + bool hasNearHits = false; + bool hasMidHits = false; + bool hasFarHits = false; + + // Near pass + vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); + hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, + int(iterationCount.x), rayStep.x, distanceBias.x, + depthRejectBias.x, adaptiveStepMultiplier.x, distanceParams); + + // Mid pass + distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); + hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, + int(iterationCount.y), rayStep.y, distanceBias.y, + depthRejectBias.y, adaptiveStepMultiplier.y, distanceParams); + + // Far pass + distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); + hasFarHits = tracePass(viewPos, rayDirection, tc, farColor, source, roughness, + int(iterationCount.z), rayStep.z, distanceBias.z, + depthRejectBias.z, adaptiveStepMultiplier.z, distanceParams); + + // Combine results from all three passes + collectedColor = vec4(0.0); + float totalWeight = 0.0; + + // Use a priority system: prefer near hits, then mid, then far + if (hasNearHits) { + collectedColor = nearColor; + totalWeight = 1.0; + } else if (hasMidHits) { + collectedColor = midColor; + totalWeight = 1.0; + } else if (hasFarHits) { + collectedColor = farColor; + totalWeight = 1.0; + } + + // Alternative: blend all hits with distance-based weighting + // if (hasNearHits || hasMidHits || hasFarHits) { + // float nearWeight = hasNearHits ? 1.0 : 0.0; + // float midWeight = hasMidHits ? 0.7 : 0.0; + // float farWeight = hasFarHits ? 0.4 : 0.0; + // totalWeight = nearWeight + midWeight + farWeight; + // + // if (totalWeight > 0.0) { + // collectedColor = (nearColor * nearWeight + midColor * midWeight + farColor * farWeight) / totalWeight; + // } + + if (totalWeight > 0.0) { + float finalEdgeFade = collectedColor.a * combinedFade * baseEdgeFade; + collectedColor.a = finalEdgeFade; + return 1.0; + } else { + collectedColor = vec4(0.0); + return 0.0; + } + } else { + collectedColor = vec4(0.0); + return 0.0; + } } - return 0; // Surface is perfectly rough, no reflections calculated. + return 0; } From ab80b657a161552d7ac0122362da4e5146c8cc03 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sat, 7 Jun 2025 13:16:19 -0400 Subject: [PATCH 06/19] Just do a depth based exponential scale for the step sizing. Seems to fix most things. Tweak and tune per pass. --- indra/newview/app_settings/settings.xml | 2 +- .../class3/deferred/screenSpaceReflUtil.glsl | 100 +++++++++++------- 2 files changed, 63 insertions(+), 39 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 93fafff7563..2dbcb0a87d1 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7610,7 +7610,7 @@ Vector3 Value - 1 + 0.110 1 1 diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 6f2dfda6241..ada527529aa 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -410,32 +410,44 @@ void advanceRayMarch( ) { vec3 actualMarchingVector; + + // Calculate a minimum step size based on the current position's depth + // This prevents steps that are smaller than the depth buffer's precision + float currentDepth = abs(currentMarchingPos.z); + float depthBasedMinStep = max(minStepLenScalar, currentDepth * 0.001); // 0.1% of current depth + if (useAdaptiveStepping) { if (deltaFromSurface < 0.0f) { // Ray is in front of the surface vec3 stepDir = normalize(currentBaseStepVec); if (abs(stepDir.z) > 0.0001f) { // Avoid division by zero if ray is mostly horizontal // Project the Z-difference onto the ray's direction to estimate distance to intersection. float distToPotentialIntersection = abs(deltaFromSurface) / abs(stepDir.z); - // Determine adaptive step length: - // - At least minStepLenScalar. - // - Try to step a fraction (e.g., 75%) towards potential intersection. - // - No more than the current length of currentBaseStepVec. + // Determine adaptive step length with enhanced minimum: float adaptiveLength = clamp(distToPotentialIntersection * 0.75f, - minStepLenScalar, + depthBasedMinStep, length(currentBaseStepVec)); actualMarchingVector = stepDir * adaptiveLength; } else { // Ray is mostly horizontal, use a conservative step. - actualMarchingVector = stepDir * max(length(currentBaseStepVec) * 0.5f, minStepLenScalar); + float stepLength = max(length(currentBaseStepVec) * 0.5f, depthBasedMinStep); + actualMarchingVector = stepDir * stepLength; + } + } else { // deltaFromSurface >= 0.0f (Ray is at or behind the surface) + float directionSign = sign(deltaFromSurface); + // Ensure retreat step is also not too small + vec3 baseStepForRetreat = currentBaseStepVec * max(0.5f, 1.0f - minStepLenScalar * max(directionSign, 0.0f)); + actualMarchingVector = baseStepForRetreat * (-directionSign); + + // Ensure minimum retreat distance + if (length(actualMarchingVector) < depthBasedMinStep) { + actualMarchingVector = normalize(actualMarchingVector) * depthBasedMinStep; } - } else { // deltaFromSurface >= 0.0f (Ray is at or behind the surface, but not yet a 'hit' or triggering binary search) - // This typically involves a retreat if the ray overshot. - float directionSign = sign(deltaFromSurface); // 0 if at surface, 1 if behind. - // Form a retreat vector. - vec3 baseStepForRetreat = currentBaseStepVec * (1.0f - minStepLenScalar * max(directionSign, 0.0f)); - actualMarchingVector = baseStepForRetreat * (-directionSign); // Retreat if delta > 0. } } else { // Not adaptive stepping, use the current base step vector. actualMarchingVector = currentBaseStepVec; + // But still enforce minimum step size + if (length(actualMarchingVector) < depthBasedMinStep) { + actualMarchingVector = normalize(actualMarchingVector) * depthBasedMinStep; + } } currentMarchingPos += actualMarchingVector; // Advance the ray position. @@ -490,12 +502,11 @@ bool traceScreenRay( float passDistanceBias, float passDepthRejectBias, float passAdaptiveStepMultiplier, + float passDepthScaleExponent, vec2 distanceParams ) { // Transform initialPosition and the target of initialReflection vector from current camera space to previous camera space. - // This is crucial if 'source' texture and 'sceneDepth' are from the previous frame's perspective, - // or if a consistent coordinate system is needed for ray marching against 'inv_modelview_delta'-transformed geometry. vec3 reflectionTargetPoint = initialPosition + initialReflection; vec3 currentPosition_transformed = (inv_modelview_delta * vec4(initialPosition, 1.0)).xyz; vec3 reflectionTarget_transformed = (inv_modelview_delta * vec4(reflectionTargetPoint, 1.0)).xyz; @@ -508,9 +519,10 @@ bool traceScreenRay( float rangeStart = distanceParams.x; float rangeEnd = distanceParams.y; - // Initialize ray marching variables. - // 'baseStepVector' starts with 'rayStep' magnitude in the direction of the (transformed) reflection. - vec3 baseStepVector = passRayStep * reflectionVector_transformed; + // Initialize ray marching variables - NO SCALING + vec3 normalizedReflection = normalize(reflectionVector_transformed); + float depthScale = pow(reflectingSurfaceViewDepth, passDepthScaleExponent); + vec3 baseStepVector = passRayStep * max(1.0, depthScale) * normalizedReflection; vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. float delta; // Difference between ray's Z depth and scene's Z depth at the projected screen position. @@ -543,10 +555,15 @@ bool traceScreenRay( hit = false; break; } + + // Early termination if ray has traveled too far from the reflector + float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); + if (rayTravelDistance > rangeEnd) { + hit = false; + break; + } // Calculate delta: difference between ray's absolute Z and scene's depth. - // Assumes marchingPosition.z and depthFromScreen are comparable (e.g., both positive view depths or both negative view Z). - // Original code uses abs(marchingPosition.z), implying positive view depth convention. delta = abs(marchingPosition.z) - depthFromScreen; // Check for self-intersection (ray hitting the surface it originated from). @@ -578,7 +595,6 @@ bool traceScreenRay( // --- Binary Search Refinement Loop --- // Perform binary search if enabled, the main loop overshot (delta > 0), and no hit was found yet. - // 'delta' and 'baseStepVector' hold their values from the last iteration of the main loop. if (isBinarySearchEnabled && delta > 0.0 && !hit) { vec3 binarySearchBaseStep = baseStepVector; // Start with the step that caused the overshoot. @@ -586,7 +602,6 @@ bool traceScreenRay( for (; i < int(passIterationCount) && !hit; i++) { binarySearchBaseStep *= 0.5; // Halve the step size for refinement. // Move back or forward along the ray based on the sign of the last known delta. - // If delta > 0 (overshot), sign(delta) is 1, so we subtract (move back). marchingPosition = marchingPosition - binarySearchBaseStep * sign(delta); screenPosition = generateProjectedPosition(marchingPosition); @@ -603,6 +618,14 @@ bool traceScreenRay( hit = false; break; } + + // Check if we've traveled too far + float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); + if (rayTravelDistance > rangeEnd) { + hit = false; + break; + } + // Recalculate delta for this binary search iteration. float currentBinarySearchDelta = abs(marchingPosition.z) - depthFromScreen; @@ -613,8 +636,6 @@ bool traceScreenRay( } // Check for hit using the refined delta. - // The condition `depthFromScreen != (reflectingSurfaceViewDepth - distanceBias)` seems specific - // and might be trying to avoid certain self-reflection scenarios or floating point issues. if (abs(currentBinarySearchDelta) < passDistanceBias && depthFromScreen != (reflectingSurfaceViewDepth - passDistanceBias)) { if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { @@ -650,7 +671,7 @@ bool traceScreenRay( */ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, sampler2D source, float roughness, int iterations, float passRayStep, float passDistanceBias, - float passDepthRejectBias, float passAdaptiveStepMultiplier, vec2 distanceParams) + float passDepthRejectBias, float passAdaptiveStepMultiplier, float passDepthScaleExponent, vec2 distanceParams) { tracedColor = vec4(0.0); // Initialize accumulated color. int hits = 0; // Counter for successful ray hits. @@ -682,7 +703,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0, - iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier, distanceParams); + iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier, passDepthScaleExponent, distanceParams); if (hit) { ++hits; @@ -772,19 +793,19 @@ float tapScreenSpaceReflection( vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x, distanceParams); + depthRejectBias.x, adaptiveStepMultiplier.x, 1.1, distanceParams); // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, int(iterationCount.y), rayStep.y, distanceBias.y, - depthRejectBias.y, adaptiveStepMultiplier.y, distanceParams); + depthRejectBias.y, adaptiveStepMultiplier.y, 0.8, distanceParams); // Far pass distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); hasFarHits = tracePass(viewPos, rayDirection, tc, farColor, source, roughness, int(iterationCount.z), rayStep.z, distanceBias.z, - depthRejectBias.z, adaptiveStepMultiplier.z, distanceParams); + depthRejectBias.z, adaptiveStepMultiplier.z, 0.5, distanceParams); // Combine results from all three passes collectedColor = vec4(0.0); @@ -801,17 +822,20 @@ float tapScreenSpaceReflection( collectedColor = farColor; totalWeight = 1.0; } - + + /* // Alternative: blend all hits with distance-based weighting - // if (hasNearHits || hasMidHits || hasFarHits) { - // float nearWeight = hasNearHits ? 1.0 : 0.0; - // float midWeight = hasMidHits ? 0.7 : 0.0; - // float farWeight = hasFarHits ? 0.4 : 0.0; - // totalWeight = nearWeight + midWeight + farWeight; - // - // if (totalWeight > 0.0) { - // collectedColor = (nearColor * nearWeight + midColor * midWeight + farColor * farWeight) / totalWeight; - // } + if (hasNearHits || hasMidHits || hasFarHits) { + float nearWeight = hasNearHits ? 1.0 : 0.0; + float midWeight = hasMidHits ? 0.7 : 0.0; + float farWeight = hasFarHits ? 0.4 : 0.0; + totalWeight = nearWeight + midWeight + farWeight; + + if (totalWeight > 0.0) { + collectedColor = (nearColor * nearWeight + midColor * midWeight + farColor * farWeight) / totalWeight; + } + } + */ if (totalWeight > 0.0) { float finalEdgeFade = collectedColor.a * combinedFade * baseEdgeFade; From 9bdfe553850e5aea27e0e8f385af15d2ec8dc09b Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sat, 7 Jun 2025 16:56:22 -0400 Subject: [PATCH 07/19] Get some sane defaults setup. --- indra/newview/app_settings/settings.xml | 20 ++++++++--------- .../class3/deferred/screenSpaceReflUtil.glsl | 22 ++++++++----------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 2dbcb0a87d1..721d156a627 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7595,7 +7595,7 @@ Vector3 Value - 48 + 30 16 8 @@ -7610,8 +7610,8 @@ Vector3 Value - 0.110 - 1 + 0.035 + 0.75 1 @@ -7626,8 +7626,8 @@ Value 0.003 - 0.015 - 0.015 + 1 + 2 RenderScreenSpaceReflectionDepthRejectBias @@ -7655,9 +7655,9 @@ Vector3 Value - 1.155 - 1.2 - 1.4 + 1.197 + 1.3 + 1.6 RenderScreenSpaceReflectionSplitStart @@ -7671,7 +7671,7 @@ Value 0 - 15 + 7 100 @@ -7685,7 +7685,7 @@ Vector3 Value - 15 + 10 100 256 diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index ada527529aa..28fd2da593e 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -695,7 +695,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, // Generate random coefficients to offset the ray within the cone defined by roughness. vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); // Cubic roughness term to make glossy reflections spread more at higher roughness values. - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness ) / 4); + vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness )); float hitDepthVal; // Stores depth of the hit for this ray. vec4 hitpointColor; // Stores color of the hit for this ray. @@ -758,29 +758,25 @@ float tapScreenSpaceReflection( float roughness = 1.0 - glossiness; - if (roughness < 1.0) { + if (roughness < 0.3) { + float remappedRoughness = clamp((roughness - 0.2) / (0.3 - 0.2), 0.0, 1.0); + float roughnessIntensityFade = 1.0 - remappedRoughness; + + float roughnessFade = roughnessIntensityFade; float currentPixelViewDepth = -viewPos.z; vec2 distFromCenter = abs(tc * 2.0 - 1.0); float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); if (baseEdgeFade > 0.001) { - vec3 correctedNormal = n; - if (dot(correctedNormal, -viewPos) < 0.0) { - correctedNormal = -correctedNormal; - } - vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(correctedNormal))); + vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - float angleFactor = abs(dot(normalize(-viewPos), normalize(correctedNormal))); + float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); float angleFactorSq = angleFactor * angleFactor; - float distFadeDiv = 512.0 * angleFactor; - float distanceFactor = clamp((1.0 + (viewPos.z / distFadeDiv)) * 2, 0.0, 1.0); - - float combinedFade = 1;// distanceFactor; + float combinedFade = roughnessFade;// distanceFactor; combinedFade *= 1 - angleFactorSq; - //combinedFade *= min(1, max(0,dot(viewPos, viewPos) * 0.5 + 0.5 - 1)); vec4 nearColor = vec4(0.0); vec4 midColor = vec4(0.0); From e0b167d9e80bb20b4be0016058f94ab80d1d6014 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 05:54:32 -0400 Subject: [PATCH 08/19] Many more tweaks + some additional depth fade stuff for super far off things. --- indra/newview/app_settings/settings.xml | 22 ++++++------- .../class3/deferred/screenSpaceReflUtil.glsl | 32 ++++++++++++++++--- 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 721d156a627..80b9716eebb 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7597,7 +7597,7 @@ 30 16 - 8 + 16 RenderScreenSpaceReflectionRayStep @@ -7625,9 +7625,9 @@ Vector3 Value - 0.003 - 1 - 2 + 0.01 + 0.4 + 10 RenderScreenSpaceReflectionDepthRejectBias @@ -7656,8 +7656,8 @@ Value 1.197 - 1.3 - 1.6 + 1.5 + 2 RenderScreenSpaceReflectionSplitStart @@ -7671,8 +7671,8 @@ Value 0 - 7 - 100 + 5 + 50 RenderScreenSpaceReflectionSplitEnd @@ -7685,9 +7685,9 @@ Vector3 Value - 10 - 100 - 256 + 25 + 150 + 1000 RenderScreenSpaceReflectionGlossySamples diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 28fd2da593e..060fb049960 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -102,6 +102,11 @@ uniform vec3 adaptiveStepMultiplier; uniform vec3 splitParamsStart; uniform vec3 splitParamsEnd; +float blurStepY = 0.01; +float blurStepX = 0.01; +float blurIterationX = 2; +float blurIterationY = 4; + /** @brief Number of samples to take for glossy reflections. Higher values give smoother but more expensive reflections. Debug setting: RenderScreenSpaceReflectionGlossySamples */ uniform float glossySampleCount; /** @brief A time-varying sine value, used to introduce temporal variation with the poisson sphere sampling. */ @@ -332,7 +337,8 @@ void processRayIntersection( float reflRoughness, out vec4 outHitColor, out float outHitDepthValue, - out float outEdgeFactor) + out float outEdgeFactor +) { vec4 color = vec4(1.0); // Default color multiplier. if(debugDraw) { @@ -458,7 +464,7 @@ void advanceRayMarch( } } -const float MAX_Z_DEPTH = 512; +const float MAX_Z_DEPTH = 350; /** * @brief Checks if a hit point is within the specified distance range from the reflector. @@ -531,7 +537,7 @@ bool traceScreenRay( hitColor = vec4(0.0); // Initialize output hit color. edgeFade = 1.0; // Initialize output edge fade. float depthFromScreen = 0.0; // Linear depth sampled from the sceneDepth texture. - + float furthestValidDepth = 0.0; // The furthest valid depth encountered during ray marching. int i = 0; // Iteration counter. // Only trace if the reflecting surface itself isn't too shallow (depthRejectBias). if (reflectingSurfaceViewDepth > passDepthRejectBias) { @@ -578,6 +584,9 @@ bool traceScreenRay( if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, hitColor, hitDepth, edgeFade); + if (hitDepth > furthestValidDepth) { + furthestValidDepth = hitDepth; // Update the furthest valid depth. + } hit = true; break; // Found a hit. } @@ -641,6 +650,10 @@ bool traceScreenRay( if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, hitColor, hitDepth, edgeFade); + + if (hitDepth > furthestValidDepth) { + furthestValidDepth = hitDepth; // Update the furthest valid depth. + } hit = true; break; } @@ -650,6 +663,16 @@ bool traceScreenRay( } } + // Do a bit of distance fading if we have a hit. Use it to fade out far off objects that just don't look right. + if (hit) { + float zFadeStart = MAX_Z_DEPTH * 0.75; + float zFade = 1.0 - smoothstep(zFadeStart, MAX_Z_DEPTH, furthestValidDepth); + + // Combine both fades (multiply for stronger effect) + edgeFade *= zFade; + + } + return hit; // Return whether a valid intersection was found. } @@ -682,7 +705,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, // Smoother surfaces get more samples. int totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness))); totalSamples = max(totalSamples, 1); // Ensure at least one sample. - + vec2 blurOffset = vec2(0); for (int i = 0; i < totalSamples; i++) { // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. @@ -694,6 +717,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); // Generate random coefficients to offset the ray within the cone defined by roughness. vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); + coeffs += blurOffset; // Cubic roughness term to make glossy reflections spread more at higher roughness values. vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness )); From a0b1d4da02d1c1e601db8bb596619ac0694ccf1b Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 06:31:12 -0400 Subject: [PATCH 09/19] Update to the feature table to enable SSR on Ultra settings. --- indra/newview/featuretable.txt | 4 ++-- indra/newview/featuretable_mac.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt index 1090dd8ffb9..b3baaf38764 100644 --- a/indra/newview/featuretable.txt +++ b/indra/newview/featuretable.txt @@ -1,4 +1,4 @@ -version 74 +version 75 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -370,7 +370,7 @@ RenderFSAAType 1 2 RenderFSAASamples 1 3 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflections 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 0 RenderHeroProbeResolution 1 2048 diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt index c3e2dd0c41c..3524c7609b9 100644 --- a/indra/newview/featuretable_mac.txt +++ b/indra/newview/featuretable_mac.txt @@ -1,4 +1,4 @@ -version 73 +version 74 // The version number above should be incremented IF AND ONLY IF some // change has been made that is sufficiently important to justify // resetting the graphics preferences of all users to the recommended @@ -370,7 +370,7 @@ RenderFSAAType 1 2 RenderFSAASamples 1 3 RenderReflectionsEnabled 1 1 RenderReflectionProbeDetail 1 1 -RenderScreenSpaceReflections 1 0 +RenderScreenSpaceReflections 1 1 RenderReflectionProbeLevel 1 3 RenderMirrors 1 0 RenderHeroProbeResolution 1 1024 From 2a675e5df1c356daf614a23ffbd56881aeca6413 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 07:01:49 -0400 Subject: [PATCH 10/19] Add a few early outs to help reduce unnecessary traces. --- .../class3/deferred/screenSpaceReflUtil.glsl | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 060fb049960..382d010e790 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -515,18 +515,44 @@ bool traceScreenRay( // Transform initialPosition and the target of initialReflection vector from current camera space to previous camera space. vec3 reflectionTargetPoint = initialPosition + initialReflection; vec3 currentPosition_transformed = (inv_modelview_delta * vec4(initialPosition, 1.0)).xyz; + + vec2 initialScreenPos = generateProjectedPosition(currentPosition_transformed); + if (initialScreenPos.x < 0.0 || initialScreenPos.x > 1.0 || + initialScreenPos.y < 0.0 || initialScreenPos.y > 1.0) { + hitColor = vec4(0.0); + edgeFade = 0.0; + hitDepth = 0.0; + return false; + } + vec3 reflectionTarget_transformed = (inv_modelview_delta * vec4(reflectionTargetPoint, 1.0)).xyz; vec3 reflectionVector_transformed = reflectionTarget_transformed - currentPosition_transformed; // Depth of the reflecting surface in the transformed view space. float reflectingSurfaceViewDepth = -currentPosition_transformed.z; + if (reflectingSurfaceViewDepth > MAX_Z_DEPTH) { + // Do a sanity check: if the reflecting surface is too far away, skip ray tracing. + hitColor = vec4(0.0); + edgeFade = 0.0; + hitDepth = 0.0; + return false; + } + // Extract range parameters float rangeStart = distanceParams.x; float rangeEnd = distanceParams.y; // Initialize ray marching variables - NO SCALING vec3 normalizedReflection = normalize(reflectionVector_transformed); + + if (normalizedReflection.z >= 0.0) { + hitColor = vec4(0.0); + edgeFade = 0.0; + hitDepth = 0.0; + return false; + } + float depthScale = pow(reflectingSurfaceViewDepth, passDepthScaleExponent); vec3 baseStepVector = passRayStep * max(1.0, depthScale) * normalizedReflection; vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. @@ -783,6 +809,13 @@ float tapScreenSpaceReflection( float roughness = 1.0 - glossiness; if (roughness < 0.3) { + + float viewDotNormal = dot(normalize(-viewPos), normalize(n)); + if (viewDotNormal <= 0.0) { + collectedColor = vec4(0.0); + return 0.0; + } + float remappedRoughness = clamp((roughness - 0.2) / (0.3 - 0.2), 0.0, 1.0); float roughnessIntensityFade = 1.0 - remappedRoughness; @@ -793,10 +826,15 @@ float tapScreenSpaceReflection( float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); if (baseEdgeFade > 0.001) { - vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - - float angleFactor = abs(dot(normalize(-viewPos), normalize(n))); + + float reflViewAngle = dot(normalize(rayDirection), normalize(viewPos)); + if (reflViewAngle < 0.001) { // Threshold can be tuned + collectedColor = vec4(0.0); + return 0.0; + } + + float angleFactor = viewDotNormal; float angleFactorSq = angleFactor * angleFactor; float combinedFade = roughnessFade;// distanceFactor; From 1dc2a2395f43bb43b789e9068d7d1091fe8d2d6f Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:17:44 -0400 Subject: [PATCH 11/19] Use a GGX based blur instead. --- .../class3/deferred/screenSpaceReflUtil.glsl | 217 +++--------------- 1 file changed, 34 insertions(+), 183 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 382d010e790..d5fb4e4e1cc 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -160,149 +160,6 @@ float calculateEdgeFade(vec2 screenPos) { return 1.0 - max(fade.x, fade.y); } -/** @brief Predefined array of 128 3D Poisson disk sample offsets. Used for generating distributed samples, e.g., for glossy reflections. Values are in [0,1] range. */ -uniform vec3 POISSON3D_SAMPLES[128] = vec3[128]( - // ... (array values as provided) ... - vec3(0.0581234, 0.1254093, 0.0214785), - vec3(0.6575254, 0.721409, 0.16286), - vec3(0.02888453, 0.05170321, 0.7573566), - vec3(0.06635678, 0.8286457, 0.07157445), - vec3(0.8957489, 0.4005505, 0.7916042), - vec3(0.3423355, 0.5053263, 0.9193521), - vec3(0.9694794, 0.9461077, 0.5406441), - vec3(0.9975473, 0.02789414, 0.7320132), - vec3(0.07781899, 0.3862341, 0.918594), - vec3(0.4439073, 0.9686955, 0.4055861), - vec3(0.9657035, 0.6624081, 0.7082613), - vec3(0.7712346, 0.07273269, 0.3292839), - vec3(0.2489169, 0.2550394, 0.1950516), - vec3(0.7249326, 0.9328285, 0.3352458), - vec3(0.6028461, 0.4424961, 0.5393377), - vec3(0.2879795, 0.7427881, 0.6619173), - vec3(0.3193627, 0.0486145, 0.08109283), - vec3(0.1233155, 0.602641, 0.4378719), - vec3(0.9800708, 0.211729, 0.6771586), - vec3(0.4894537, 0.3319927, 0.8087631), - vec3(0.4802743, 0.6358885, 0.814935), - vec3(0.2692913, 0.9911493, 0.9934899), - vec3(0.5648789, 0.8553897, 0.7784553), - vec3(0.8497344, 0.7870212, 0.02065313), - vec3(0.7503014, 0.2826185, 0.05412734), - vec3(0.8045461, 0.6167251, 0.9532926), - vec3(0.04225039, 0.2141281, 0.8678675), - vec3(0.07116079, 0.9971236, 0.3396397), - vec3(0.464099, 0.480959, 0.2775862), - vec3(0.6346927, 0.31871, 0.6588384), - vec3(0.449012, 0.8189669, 0.2736875), - vec3(0.452929, 0.2119148, 0.672004), - vec3(0.01506042, 0.7102436, 0.9800494), - vec3(0.1970513, 0.4713539, 0.4644522), - vec3(0.13715, 0.7253224, 0.5056525), - vec3(0.9006432, 0.5335414, 0.02206874), - vec3(0.9960898, 0.7961011, 0.01468861), - vec3(0.3386469, 0.6337739, 0.9310676), - vec3(0.1745718, 0.9114985, 0.1728188), - vec3(0.6342545, 0.5721557, 0.4553517), - vec3(0.1347412, 0.1137158, 0.7793725), - vec3(0.3574478, 0.3448052, 0.08741581), - vec3(0.7283059, 0.4753885, 0.2240275), - vec3(0.8293507, 0.9971212, 0.2747005), - vec3(0.6501846, 0.000688076, 0.7795712), - vec3(0.01149416, 0.4930083, 0.792608), - vec3(0.666189, 0.1875442, 0.7256873), - vec3(0.8538797, 0.2107637, 0.1547532), - vec3(0.5826825, 0.9750752, 0.9105834), - vec3(0.8914346, 0.08266425, 0.5484225), - vec3(0.4374518, 0.02987111, 0.7810078), - vec3(0.2287418, 0.1443802, 0.1176908), - vec3(0.2671157, 0.8929081, 0.8989366), - vec3(0.5425819, 0.5524959, 0.6963879), - vec3(0.3515188, 0.8304397, 0.0502702), - vec3(0.3354864, 0.2130747, 0.141169), - vec3(0.9729427, 0.3509927, 0.6098799), - vec3(0.7585629, 0.7115368, 0.9099342), - vec3(0.0140543, 0.6072157, 0.9436461), - vec3(0.9190664, 0.8497264, 0.1643751), - vec3(0.1538157, 0.3219983, 0.2984214), - vec3(0.8854713, 0.2968667, 0.8511457), - vec3(0.1910622, 0.03047311, 0.3571215), - vec3(0.2456353, 0.5568692, 0.3530164), - vec3(0.6927255, 0.8073994, 0.5808484), - vec3(0.8089353, 0.8969175, 0.3427134), - vec3(0.194477, 0.7985603, 0.8712182), - vec3(0.7256182, 0.5653068, 0.3985921), - vec3(0.9889427, 0.4584851, 0.8363391), - vec3(0.5718582, 0.2127113, 0.2950557), - vec3(0.5480209, 0.0193435, 0.2992659), - vec3(0.6598953, 0.09478426, 0.92187), - vec3(0.1385615, 0.2193868, 0.205245), - vec3(0.7623423, 0.1790726, 0.1508465), - vec3(0.7569032, 0.3773386, 0.4393887), - vec3(0.5842971, 0.6538072, 0.5224424), - vec3(0.9954313, 0.5763943, 0.9169143), - vec3(0.001311183, 0.340363, 0.1488652), - vec3(0.8167927, 0.4947158, 0.4454727), - vec3(0.3978434, 0.7106082, 0.002727509), - vec3(0.5459411, 0.7473233, 0.7062873), - vec3(0.4151598, 0.5614617, 0.4748358), - vec3(0.4440694, 0.1195122, 0.9624678), - vec3(0.1081301, 0.4813806, 0.07047641), - vec3(0.2402785, 0.3633997, 0.3898734), - vec3(0.2317942, 0.6488295, 0.4221864), - vec3(0.01145542, 0.9304277, 0.4105759), - vec3(0.3563728, 0.9228861, 0.3282344), - vec3(0.855314, 0.6949819, 0.3175117), - vec3(0.730832, 0.01478493, 0.5728671), - vec3(0.9304829, 0.02653277, 0.712552), - vec3(0.4132186, 0.4127623, 0.6084146), - vec3(0.7517329, 0.9978395, 0.1330464), - vec3(0.5210338, 0.4318751, 0.9721575), - vec3(0.02953994, 0.1375937, 0.9458942), - vec3(0.1835506, 0.9896691, 0.7919457), - vec3(0.3857062, 0.2682322, 0.1264563), - vec3(0.6319699, 0.8735335, 0.04390657), - vec3(0.5630485, 0.3339024, 0.993995), - vec3(0.90701, 0.1512893, 0.8970422), - vec3(0.3027443, 0.1144253, 0.1488708), - vec3(0.9149003, 0.7382028, 0.7914025), - vec3(0.07979286, 0.6892691, 0.2866171), - vec3(0.7743186, 0.8046008, 0.4399814), - vec3(0.3128662, 0.4362317, 0.6030678), - vec3(0.1133721, 0.01605821, 0.391872), - vec3(0.5185481, 0.9210006, 0.7889017), - vec3(0.8217013, 0.325305, 0.1668191), - vec3(0.8358996, 0.1449739, 0.3668382), - vec3(0.1778213, 0.5599256, 0.1327691), - vec3(0.06690693, 0.5508637, 0.07212365), - vec3(0.9750564, 0.284066, 0.5727578), - vec3(0.4350255, 0.8949825, 0.03574753), - vec3(0.8931149, 0.9177974, 0.8123496), - vec3(0.9055127, 0.989903, 0.813235), - vec3(0.2897243, 0.3123978, 0.5083504), - vec3(0.1519223, 0.3958645, 0.2640327), - vec3(0.6840154, 0.6463035, 0.2346607), - vec3(0.986473, 0.8714055, 0.3960275), - vec3(0.6819352, 0.4169535, 0.8379834), - vec3(0.9147297, 0.6144146, 0.7313942), - vec3(0.6554981, 0.5014008, 0.9748477), - vec3(0.9805915, 0.1318207, 0.2371372), - vec3(0.5980836, 0.06796348, 0.9941338), - vec3(0.6836596, 0.9917196, 0.2319056), - vec3(0.5276511, 0.2745509, 0.5422578), - vec3(0.829482, 0.03758276, 0.1240466), - vec3(0.2698198, 0.0002266169, 0.3449324) -); - -/** - * @brief Retrieves a Poisson disk sample from the predefined array and maps it to the [-1, 1] range. - * @param i The index of the sample to retrieve (should be within the bounds of POISSON3D_SAMPLES). - * @return vec3 The 3D sample vector, with components in the range [-1, 1]. - */ -vec3 getPoissonSample(int i) { - // Samples are stored in [0,1], scale and shift to [-1,1]. - return POISSON3D_SAMPLES[i] * 2.0 - 1.0 / 2; -} - /** * @brief Calculates the approximate number of mipmap levels for a texture of a given resolution. * This can be used to determine a suitable blur radius or LOD for texture sampling. @@ -464,7 +321,8 @@ void advanceRayMarch( } } -const float MAX_Z_DEPTH = 350; +const float MAX_Z_DEPTH = 256; +const float MAX_ROUGHNESS = 0.5; /** * @brief Checks if a hit point is within the specified distance range from the reflector. @@ -546,7 +404,7 @@ bool traceScreenRay( // Initialize ray marching variables - NO SCALING vec3 normalizedReflection = normalize(reflectionVector_transformed); - if (normalizedReflection.z >= 0.0) { + if (normalizedReflection.z >= 0.5) { hitColor = vec4(0.0); edgeFade = 0.0; hitDepth = 0.0; @@ -691,7 +549,7 @@ bool traceScreenRay( // Do a bit of distance fading if we have a hit. Use it to fade out far off objects that just don't look right. if (hit) { - float zFadeStart = MAX_Z_DEPTH * 0.75; + float zFadeStart = MAX_Z_DEPTH * 0.8; float zFade = 1.0 - smoothstep(zFadeStart, MAX_Z_DEPTH, furthestValidDepth); // Combine both fades (multiply for stronger effect) @@ -732,20 +590,33 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, int totalSamples = int(max(float(glossySampleCount), float(glossySampleCount) * (1.0 - roughness))); totalSamples = max(totalSamples, 1); // Ensure at least one sample. vec2 blurOffset = vec2(0); + int pixelSeed = int(mod(dot(tc * screen_res, vec2(37.0, 17.0)), 128.0)); for (int i = 0; i < totalSamples; i++) { - // Generate a perturbed reflection direction for glossy effects using Poisson disk samples. - // 'noiseSine' and 'random' calls add temporal and spatial variation to the sampling pattern. - float temporalOffset = fract(noiseSine * 0.1) * 127.0; // Temporal offset for Poisson index. - int poissonIndex = int(mod(float(i) + random(tc * noiseSine) * 64.0, 128.0)); - - // Create an orthonormal basis around the main ray direction. - vec3 firstBasis = normalize(cross(getPoissonSample(i), rayDirection)); - vec3 secondBasis = normalize(cross(rayDirection, firstBasis)); - // Generate random coefficients to offset the ray within the cone defined by roughness. - vec2 coeffs = vec2(random(tc + vec2(0, i)) + random(tc + vec2(i, 0))); - coeffs += blurOffset; - // Cubic roughness term to make glossy reflections spread more at higher roughness values. - vec3 reflectionDirectionRandomized = rayDirection + ((firstBasis * coeffs.x + secondBasis * coeffs.y) * (roughness * roughness )); + float temporalJitter = noiseSine; // Arbitrary multiplier for decorrelation + + float u1 = random(tc + vec2(i, 0.123) + temporalJitter); + float u2 = random(tc + vec2(0.456, i) + temporalJitter); + float jitter = random(tc + vec2(i * 0.777, 0.888) + temporalJitter); // Additional random jitter + + float alpha = max(roughness * roughness, 0.0001); // Already using alpha^2, clamp to avoid div by zero + float theta = atan(alpha * sqrt(u1) / sqrt(1.0 - u1)); + // Slightly jitter phi with a random offset, scaled by roughness for more effect on rough surfaces + float phi = 2.0 * 3.14159265 * (u2 + jitter * 0.15 * roughness); + + // Build tangent space + vec3 up = abs(rayDirection.y) < 0.999 ? vec3(0,1,0) : vec3(1,0,0); + vec3 tangent = normalize(cross(up, rayDirection)); + vec3 bitangent = cross(rayDirection, tangent); + + // GGX half-vector in tangent space + vec3 h = normalize( + sin(theta) * cos(phi) * tangent + + sin(theta) * sin(phi) * bitangent + + cos(theta) * rayDirection + ); + + // Reflect view vector about GGX half-vector to get the final sample direction + vec3 reflectionDirectionRandomized = normalize(reflect(-rayDirection, h)); float hitDepthVal; // Stores depth of the hit for this ray. vec4 hitpointColor; // Stores color of the hit for this ray. @@ -808,7 +679,7 @@ float tapScreenSpaceReflection( float roughness = 1.0 - glossiness; - if (roughness < 0.3) { + if (roughness < MAX_ROUGHNESS) { float viewDotNormal = dot(normalize(-viewPos), normalize(n)); if (viewDotNormal <= 0.0) { @@ -816,7 +687,7 @@ float tapScreenSpaceReflection( return 0.0; } - float remappedRoughness = clamp((roughness - 0.2) / (0.3 - 0.2), 0.0, 1.0); + float remappedRoughness = clamp((roughness - (MAX_ROUGHNESS * 0.6)) / (MAX_ROUGHNESS - (MAX_ROUGHNESS * 0.6)), 0.0, 1.0); float roughnessIntensityFade = 1.0 - remappedRoughness; float roughnessFade = roughnessIntensityFade; @@ -828,12 +699,6 @@ float tapScreenSpaceReflection( if (baseEdgeFade > 0.001) { vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); - float reflViewAngle = dot(normalize(rayDirection), normalize(viewPos)); - if (reflViewAngle < 0.001) { // Threshold can be tuned - collectedColor = vec4(0.0); - return 0.0; - } - float angleFactor = viewDotNormal; float angleFactorSq = angleFactor * angleFactor; @@ -851,13 +716,13 @@ float tapScreenSpaceReflection( vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x, 1.1, distanceParams); + depthRejectBias.x, adaptiveStepMultiplier.x, 1.2, distanceParams); // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, int(iterationCount.y), rayStep.y, distanceBias.y, - depthRejectBias.y, adaptiveStepMultiplier.y, 0.8, distanceParams); + depthRejectBias.y, adaptiveStepMultiplier.y, 0.9, distanceParams); // Far pass distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); @@ -880,20 +745,6 @@ float tapScreenSpaceReflection( collectedColor = farColor; totalWeight = 1.0; } - - /* - // Alternative: blend all hits with distance-based weighting - if (hasNearHits || hasMidHits || hasFarHits) { - float nearWeight = hasNearHits ? 1.0 : 0.0; - float midWeight = hasMidHits ? 0.7 : 0.0; - float farWeight = hasFarHits ? 0.4 : 0.0; - totalWeight = nearWeight + midWeight + farWeight; - - if (totalWeight > 0.0) { - collectedColor = (nearColor * nearWeight + midColor * midWeight + farColor * farWeight) / totalWeight; - } - } - */ if (totalWeight > 0.0) { float finalEdgeFade = collectedColor.a * combinedFade * baseEdgeFade; From 82b97236307aa1665a928e9a365359a811d793b0 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:33:27 -0400 Subject: [PATCH 12/19] Tweak the max roughness to better avoid striations when you move the camera around. --- .../shaders/class3/deferred/screenSpaceReflUtil.glsl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index d5fb4e4e1cc..be2664ee8e6 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -322,7 +322,7 @@ void advanceRayMarch( } const float MAX_Z_DEPTH = 256; -const float MAX_ROUGHNESS = 0.5; +const float MAX_ROUGHNESS = 0.45; /** * @brief Checks if a hit point is within the specified distance range from the reflector. @@ -712,23 +712,25 @@ float tapScreenSpaceReflection( bool hasMidHits = false; bool hasFarHits = false; + float stepRoughnesMultiplier = mix(1.0, 5.0, roughness); + // Near pass vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x, 1.2, distanceParams); + depthRejectBias.x, adaptiveStepMultiplier.x, 1.2 * stepRoughnesMultiplier, distanceParams); // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, int(iterationCount.y), rayStep.y, distanceBias.y, - depthRejectBias.y, adaptiveStepMultiplier.y, 0.9, distanceParams); + depthRejectBias.y, adaptiveStepMultiplier.y, 0.35 * stepRoughnesMultiplier, distanceParams); // Far pass distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); hasFarHits = tracePass(viewPos, rayDirection, tc, farColor, source, roughness, int(iterationCount.z), rayStep.z, distanceBias.z, - depthRejectBias.z, adaptiveStepMultiplier.z, 0.5, distanceParams); + depthRejectBias.z, adaptiveStepMultiplier.z, 0.1 * stepRoughnesMultiplier, distanceParams); // Combine results from all three passes collectedColor = vec4(0.0); From 4393d34e62a73bd5940811e3c6bba167f583d48d Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:37:38 -0400 Subject: [PATCH 13/19] Make the scale a bit better for high roughness values. --- .../shaders/class3/deferred/screenSpaceReflUtil.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index be2664ee8e6..925ab22dce7 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -718,7 +718,7 @@ float tapScreenSpaceReflection( vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x, 1.2 * stepRoughnesMultiplier, distanceParams); + depthRejectBias.x, adaptiveStepMultiplier.x, 0.6 * stepRoughnesMultiplier, distanceParams); // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); From cfae6dcf3d278f9512bd1768cd2687ff46f76ad5 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:48:35 -0400 Subject: [PATCH 14/19] Slight tweak to make SSR reflections more pronounced when replacing probe reflections. --- .../shaders/class3/deferred/screenSpaceReflUtil.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 925ab22dce7..70aba7de04a 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -703,7 +703,7 @@ float tapScreenSpaceReflection( float angleFactorSq = angleFactor * angleFactor; float combinedFade = roughnessFade;// distanceFactor; - combinedFade *= 1 - angleFactorSq; + combinedFade *= min(1, (1 -angleFactorSq) * 2); vec4 nearColor = vec4(0.0); vec4 midColor = vec4(0.0); From 6a289c6556dd5da29cd1c752229e53e11b365bac Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:50:02 -0400 Subject: [PATCH 15/19] Take care of trailing white spaces. --- .../class3/deferred/screenSpaceReflUtil.glsl | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 70aba7de04a..92a40d2e1d0 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -140,18 +140,18 @@ float calculateEdgeFade(vec2 screenPos) { // Defines how close to the edge the fade effect starts. // 0.9 means fading begins when the point is 10% away from the screen edge. const float edgeFadeStart = 0.9; - + // Convert screen position from [0,1] to [-1,1] range and take absolute value. // This gives distance from the center: 0 at center, 1 at edges. vec2 distFromCenter = abs(screenPos * 2.0 - 1.0); - + // Calculate a smooth fade for each axis using smoothstep. // smoothstep(edgeFadeStart, 1.0, x) will be: // - 0 if x < edgeFadeStart // - 1 if x > 1.0 // - A smooth transition between 0 and 1 if edgeFadeStart <= x <= 1.0 vec2 fade = smoothstep(edgeFadeStart, 1.0, distFromCenter); - + // Use the maximum fade value between X and Y axes. // We want to fade if the point is close to ANY edge. // If fade.x is high (close to X-edge) or fade.y is high (close to Y-edge), @@ -203,11 +203,11 @@ void processRayIntersection( // Helps visualize if the ray hit in front of or behind the surface. color = vec4(0.5 + sign(signedDelta) / 2.0, 0.3, 0.5 - sign(signedDelta) / 2.0, 0.0); } - + // Calculate mip level for texture sampling based on screen resolution and roughness. // Higher roughness leads to sampling from higher (blurrier) mip levels. float mipLevel = calculateMipmapLevels(screen_res) * reflRoughness; - + // Sample the source texture at the hit position using the calculated mip level. outHitColor = textureLod(texSource, screenPos, mipLevel) * color; outHitColor.a = 1.0; // Ensure full opacity for the hit. @@ -273,12 +273,12 @@ void advanceRayMarch( ) { vec3 actualMarchingVector; - + // Calculate a minimum step size based on the current position's depth // This prevents steps that are smaller than the depth buffer's precision float currentDepth = abs(currentMarchingPos.z); float depthBasedMinStep = max(minStepLenScalar, currentDepth * 0.001); // 0.1% of current depth - + if (useAdaptiveStepping) { if (deltaFromSurface < 0.0f) { // Ray is in front of the surface vec3 stepDir = normalize(currentBaseStepVec); @@ -299,7 +299,7 @@ void advanceRayMarch( // Ensure retreat step is also not too small vec3 baseStepForRetreat = currentBaseStepVec * max(0.5f, 1.0f - minStepLenScalar * max(directionSign, 0.0f)); actualMarchingVector = baseStepForRetreat * (-directionSign); - + // Ensure minimum retreat distance if (length(actualMarchingVector) < depthBasedMinStep) { actualMarchingVector = normalize(actualMarchingVector) * depthBasedMinStep; @@ -312,7 +312,7 @@ void advanceRayMarch( actualMarchingVector = normalize(actualMarchingVector) * depthBasedMinStep; } } - + currentMarchingPos += actualMarchingVector; // Advance the ray position. if (useExponentialStepping) { @@ -414,7 +414,7 @@ bool traceScreenRay( float depthScale = pow(reflectingSurfaceViewDepth, passDepthScaleExponent); vec3 baseStepVector = passRayStep * max(1.0, depthScale) * normalizedReflection; vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. - + float delta; // Difference between ray's Z depth and scene's Z depth at the projected screen position. vec2 screenPosition; // Ray's current projected 2D screen position. bool hit = false; // Flag to indicate if an intersection was found. @@ -429,13 +429,13 @@ bool traceScreenRay( for (; i < int(passIterationCount) && !hit; i++) { // Project current 3D marching position to 2D screen space. screenPosition = generateProjectedPosition(marchingPosition); - + // Check if ray is off-screen. If so, update edgeFade and break. if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { hit = false; // Ensure hit is false as ray went out of bounds. break; } - + // Get linear depth of the scene at the ray's projected screen position. depthFromScreen = getLinearDepth(screenPosition); @@ -445,7 +445,7 @@ bool traceScreenRay( hit = false; break; } - + // Early termination if ray has traveled too far from the reflector float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); if (rayTravelDistance > rangeEnd) { @@ -475,7 +475,7 @@ bool traceScreenRay( break; // Found a hit. } } - + // If ray overshot (delta > 0) and binary search is enabled, break to start binary search. if (isBinarySearchEnabled && delta > 0.0) { break; @@ -485,12 +485,12 @@ bool traceScreenRay( advanceRayMarch(delta, marchingPosition, baseStepVector, passRayStep, isAdaptiveStepEnabled, isExponentialStepEnabled, passAdaptiveStepMultiplier); } - + // --- Binary Search Refinement Loop --- // Perform binary search if enabled, the main loop overshot (delta > 0), and no hit was found yet. if (isBinarySearchEnabled && delta > 0.0 && !hit) { vec3 binarySearchBaseStep = baseStepVector; // Start with the step that caused the overshoot. - + // Continue iteration count from where the main loop left off. for (; i < int(passIterationCount) && !hit; i++) { binarySearchBaseStep *= 0.5; // Halve the step size for refinement. @@ -503,7 +503,7 @@ bool traceScreenRay( hit = false; break; } - + depthFromScreen = getLinearDepth(screenPosition); if (depthFromScreen >= MAX_Z_DEPTH) @@ -511,14 +511,14 @@ bool traceScreenRay( hit = false; break; } - + // Check if we've traveled too far float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); if (rayTravelDistance > rangeEnd) { hit = false; break; } - + // Recalculate delta for this binary search iteration. float currentBinarySearchDelta = abs(marchingPosition.z) - depthFromScreen; @@ -534,7 +534,7 @@ bool traceScreenRay( if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, hitColor, hitDepth, edgeFade); - + if (hitDepth > furthestValidDepth) { furthestValidDepth = hitDepth; // Update the furthest valid depth. } @@ -625,7 +625,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, bool hit = traceScreenRay(viewPos, reflectionDirectionRandomized, hitpointColor, hitDepthVal, source, rayEdgeFadeVal, roughness * 2.0, iterations, passRayStep, passDistanceBias, passDepthRejectBias, passAdaptiveStepMultiplier, passDepthScaleExponent, distanceParams); - + if (hit) { ++hits; tracedColor.rgb += hitpointColor.rgb; // Accumulate color. @@ -646,7 +646,7 @@ bool tracePass(vec3 viewPos, vec3 rayDirection, vec2 tc, inout vec4 tracedColor, // No hits, result is black and no fade. tracedColor = vec4(0.0); } - + return hits > 0; // Return true if at least one ray hit was successful. } @@ -678,7 +678,7 @@ float tapScreenSpaceReflection( #endif float roughness = 1.0 - glossiness; - + if (roughness < MAX_ROUGHNESS) { float viewDotNormal = dot(normalize(-viewPos), normalize(n)); @@ -692,26 +692,26 @@ float tapScreenSpaceReflection( float roughnessFade = roughnessIntensityFade; float currentPixelViewDepth = -viewPos.z; - + vec2 distFromCenter = abs(tc * 2.0 - 1.0); float baseEdgeFade = 1.0 - smoothstep(0.85, 1.0, max(distFromCenter.x, distFromCenter.y)); - + if (baseEdgeFade > 0.001) { vec3 rayDirection = normalize(reflect(normalize(viewPos), normalize(n))); float angleFactor = viewDotNormal; float angleFactorSq = angleFactor * angleFactor; - + float combinedFade = roughnessFade;// distanceFactor; combinedFade *= min(1, (1 -angleFactorSq) * 2); - + vec4 nearColor = vec4(0.0); vec4 midColor = vec4(0.0); vec4 farColor = vec4(0.0); bool hasNearHits = false; bool hasMidHits = false; bool hasFarHits = false; - + float stepRoughnesMultiplier = mix(1.0, 5.0, roughness); // Near pass @@ -719,23 +719,23 @@ float tapScreenSpaceReflection( hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, depthRejectBias.x, adaptiveStepMultiplier.x, 0.6 * stepRoughnesMultiplier, distanceParams); - + // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, int(iterationCount.y), rayStep.y, distanceBias.y, depthRejectBias.y, adaptiveStepMultiplier.y, 0.35 * stepRoughnesMultiplier, distanceParams); - + // Far pass distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); hasFarHits = tracePass(viewPos, rayDirection, tc, farColor, source, roughness, int(iterationCount.z), rayStep.z, distanceBias.z, depthRejectBias.z, adaptiveStepMultiplier.z, 0.1 * stepRoughnesMultiplier, distanceParams); - + // Combine results from all three passes collectedColor = vec4(0.0); float totalWeight = 0.0; - + // Use a priority system: prefer near hits, then mid, then far if (hasNearHits) { collectedColor = nearColor; @@ -747,7 +747,7 @@ float tapScreenSpaceReflection( collectedColor = farColor; totalWeight = 1.0; } - + if (totalWeight > 0.0) { float finalEdgeFade = collectedColor.a * combinedFade * baseEdgeFade; collectedColor.a = finalEdgeFade; From 06e5ad844bcf7b93fff1a1e78dd752eba20153e7 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 08:53:08 -0400 Subject: [PATCH 16/19] More trailing white spaces. --- indra/newview/llviewershadermgr.cpp | 2 +- indra/newview/pipeline.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/indra/newview/llviewershadermgr.cpp b/indra/newview/llviewershadermgr.cpp index 408148e1cbb..eb1cfa7453d 100644 --- a/indra/newview/llviewershadermgr.cpp +++ b/indra/newview/llviewershadermgr.cpp @@ -938,7 +938,7 @@ bool LLViewerShaderMgr::loadShadersWater() { gWaterProgram.addPermutation("HAS_SUN_SHADOW", "1"); } - + if (ssr) { gWaterProgram.addPermutation("SSR", "1"); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index de114bda235..40115159d1f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -9330,7 +9330,7 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) { gGL.getTexUnit(channel)->bind(&mSceneMap); } - + static LLCachedControl traceIterations(gSavedSettings, "RenderScreenSpaceReflectionIterations"); static LLCachedControl traceSteps(gSavedSettings, "RenderScreenSpaceReflectionRayStep"); static LLCachedControl traceDistanceBias(gSavedSettings, "RenderScreenSpaceReflectionDistanceBias"); From c8d2c4c04720f99277930f17f13ae7d4bc92a439 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Sun, 8 Jun 2025 17:42:51 -0400 Subject: [PATCH 17/19] More tuning to balance out some of the black striations in the distance. Also some improvements to the binary search to better handle underflows. --- .../class3/deferred/screenSpaceReflUtil.glsl | 133 +++++++----------- 1 file changed, 53 insertions(+), 80 deletions(-) diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 92a40d2e1d0..69d39886ef8 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -416,6 +416,10 @@ bool traceScreenRay( vec3 marchingPosition = currentPosition_transformed + baseStepVector; // First step from origin. float delta; // Difference between ray's Z depth and scene's Z depth at the projected screen position. + float prevDelta = 0.0; + vec3 prevPosition = marchingPosition; + bool crossedSurface = false; + vec2 screenPosition; // Ray's current projected 2D screen position. bool hit = false; // Flag to indicate if an intersection was found. hitColor = vec4(0.0); // Initialize output hit color. @@ -429,122 +433,91 @@ bool traceScreenRay( for (; i < int(passIterationCount) && !hit; i++) { // Project current 3D marching position to 2D screen space. screenPosition = generateProjectedPosition(marchingPosition); - - // Check if ray is off-screen. If so, update edgeFade and break. if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { - hit = false; // Ensure hit is false as ray went out of bounds. + hit = false; break; } - - // Get linear depth of the scene at the ray's projected screen position. depthFromScreen = getLinearDepth(screenPosition); - - // We store a max Z depth, and just kind of assume that the ray is intersecting with the sky if we hit it. - if (depthFromScreen >= MAX_Z_DEPTH) - { + if (depthFromScreen >= MAX_Z_DEPTH) { hit = false; break; } - - // Early termination if ray has traveled too far from the reflector float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); if (rayTravelDistance > rangeEnd) { hit = false; break; } - - // Calculate delta: difference between ray's absolute Z and scene's depth. delta = abs(marchingPosition.z) - depthFromScreen; - - // Check for self-intersection (ray hitting the surface it originated from). if (checkSelfIntersection(reflectingSurfaceViewDepth, depthFromScreen, epsilon)) { - edgeFade = calculateEdgeFade(screenPosition); // Update edge fade even on self-intersection. - hit = false; // This is not a valid reflection hit. + edgeFade = calculateEdgeFade(screenPosition); + hit = false; break; } - - // Check for intersection: if absolute delta is within distanceBias. if (abs(delta) < passDistanceBias) { if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { processRayIntersection(screenPosition, depthFromScreen, delta, source, roughness, hitColor, hitDepth, edgeFade); if (hitDepth > furthestValidDepth) { - furthestValidDepth = hitDepth; // Update the furthest valid depth. + furthestValidDepth = hitDepth; } hit = true; - break; // Found a hit. + break; } } - - // If ray overshot (delta > 0) and binary search is enabled, break to start binary search. - if (isBinarySearchEnabled && delta > 0.0) { + // --- Detect sign change for binary search --- + if (i > 0 && sign(prevDelta) != sign(delta)) { + crossedSurface = true; break; } - - // Advance the ray: calculate next step and update marchingPosition & baseStepVector. + prevDelta = delta; + prevPosition = marchingPosition; advanceRayMarch(delta, marchingPosition, baseStepVector, passRayStep, isAdaptiveStepEnabled, isExponentialStepEnabled, passAdaptiveStepMultiplier); + } // --- Binary Search Refinement Loop --- // Perform binary search if enabled, the main loop overshot (delta > 0), and no hit was found yet. - if (isBinarySearchEnabled && delta > 0.0 && !hit) { - vec3 binarySearchBaseStep = baseStepVector; // Start with the step that caused the overshoot. - - // Continue iteration count from where the main loop left off. - for (; i < int(passIterationCount) && !hit; i++) { - binarySearchBaseStep *= 0.5; // Halve the step size for refinement. - // Move back or forward along the ray based on the sign of the last known delta. - marchingPosition = marchingPosition - binarySearchBaseStep * sign(delta); - - screenPosition = generateProjectedPosition(marchingPosition); - if (checkAndUpdateOffScreen(screenPosition, edgeFade)) { - hitColor.a = 0.0; // Indicate no valid color if off-screen. - hit = false; - break; + if (isBinarySearchEnabled && crossedSurface && !hit) { + vec3 a = prevPosition; + vec3 b = marchingPosition; + float prevDeltaBinary = prevDelta; + for (int j = 0; j < 5; ++j) { // 5 steps is usually enough + vec3 mid = mix(a, b, 0.5); + vec2 midScreen = generateProjectedPosition(mid); + float midEdgeFade = edgeFade; + if (checkAndUpdateOffScreen(midScreen, midEdgeFade)) { + b = mid; + continue; } - - depthFromScreen = getLinearDepth(screenPosition); - - if (depthFromScreen >= MAX_Z_DEPTH) - { - hit = false; - break; + float midDepth = getLinearDepth(midScreen); + float midDelta = abs(mid.z) - midDepth; + if (checkSelfIntersection(reflectingSurfaceViewDepth, midDepth, epsilon)) { + a = mid; + prevDeltaBinary = midDelta; + continue; } - - // Check if we've traveled too far - float rayTravelDistance = abs(abs(marchingPosition.z) - reflectingSurfaceViewDepth); - if (rayTravelDistance > rangeEnd) { - hit = false; - break; + if (!isWithinReflectionRange(reflectingSurfaceViewDepth, midDepth, rangeStart, rangeEnd)) { + b = mid; + continue; } - - // Recalculate delta for this binary search iteration. - float currentBinarySearchDelta = abs(marchingPosition.z) - depthFromScreen; - - if (checkSelfIntersection(reflectingSurfaceViewDepth, depthFromScreen, epsilon)) { - edgeFade = calculateEdgeFade(screenPosition); - hit = false; + if (abs(midDelta) < passDistanceBias) { + processRayIntersection(midScreen, midDepth, midDelta, source, roughness, hitColor, hitDepth, midEdgeFade); + if (hitDepth > furthestValidDepth) { + furthestValidDepth = hitDepth; + } + edgeFade = midEdgeFade; + hit = true; break; } - - // Check for hit using the refined delta. - if (abs(currentBinarySearchDelta) < passDistanceBias && - depthFromScreen != (reflectingSurfaceViewDepth - passDistanceBias)) { - if (isWithinReflectionRange(reflectingSurfaceViewDepth, depthFromScreen, rangeStart, rangeEnd)) { - processRayIntersection(screenPosition, depthFromScreen, currentBinarySearchDelta, source, roughness, - hitColor, hitDepth, edgeFade); - - if (hitDepth > furthestValidDepth) { - furthestValidDepth = hitDepth; // Update the furthest valid depth. - } - hit = true; - break; - } + if (sign(midDelta) == sign(prevDeltaBinary)) { + a = mid; + prevDeltaBinary = midDelta; + } else { + b = mid; } - delta = currentBinarySearchDelta; // Update delta for the next binary search refinement step. } - } + } // End of binary search refinement loop } // Do a bit of distance fading if we have a hit. Use it to fade out far off objects that just don't look right. @@ -712,25 +685,25 @@ float tapScreenSpaceReflection( bool hasMidHits = false; bool hasFarHits = false; - float stepRoughnesMultiplier = mix(1.0, 5.0, roughness); + float stepRoughnesMultiplier = mix(0, 8.5, roughness); // Near pass vec2 distanceParams = vec2(splitParamsStart.x, splitParamsEnd.x); hasNearHits = tracePass(viewPos, rayDirection, tc, nearColor, source, roughness, int(iterationCount.x), rayStep.x, distanceBias.x, - depthRejectBias.x, adaptiveStepMultiplier.x, 0.6 * stepRoughnesMultiplier, distanceParams); + depthRejectBias.x, adaptiveStepMultiplier.x, mix(1, 20, roughness * roughness), distanceParams); // Mid pass distanceParams = vec2(splitParamsStart.y, splitParamsEnd.y); hasMidHits = tracePass(viewPos, rayDirection, tc, midColor, source, roughness, int(iterationCount.y), rayStep.y, distanceBias.y, - depthRejectBias.y, adaptiveStepMultiplier.y, 0.35 * stepRoughnesMultiplier, distanceParams); + depthRejectBias.y, adaptiveStepMultiplier.y, mix(0.8, 12, roughness * roughness), distanceParams); // Far pass distanceParams = vec2(splitParamsStart.z, splitParamsEnd.z); hasFarHits = tracePass(viewPos, rayDirection, tc, farColor, source, roughness, int(iterationCount.z), rayStep.z, distanceBias.z, - depthRejectBias.z, adaptiveStepMultiplier.z, 0.1 * stepRoughnesMultiplier, distanceParams); + depthRejectBias.z, adaptiveStepMultiplier.z, max(0.5, stepRoughnesMultiplier), distanceParams); // Combine results from all three passes collectedColor = vec4(0.0); From 129b6e4d200a2297e4c0500a975dfb338bdad8b9 Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Mon, 9 Jun 2025 08:04:05 -0400 Subject: [PATCH 18/19] Add support for adjustable max Z depth and roughness. --- indra/llrender/llshadermgr.cpp | 4 +++- indra/llrender/llshadermgr.h | 2 ++ indra/newview/app_settings/settings.xml | 22 +++++++++++++++++++ .../class3/deferred/screenSpaceReflUtil.glsl | 16 +++++++------- indra/newview/pipeline.cpp | 5 +++++ 5 files changed, 40 insertions(+), 9 deletions(-) diff --git a/indra/llrender/llshadermgr.cpp b/indra/llrender/llshadermgr.cpp index edae28d4557..5448d5a35fb 100644 --- a/indra/llrender/llshadermgr.cpp +++ b/indra/llrender/llshadermgr.cpp @@ -1349,8 +1349,10 @@ void LLShaderMgr::initAttribsAndUniforms() mReservedUniforms.push_back("glossySampleCount"); mReservedUniforms.push_back("noiseSine"); mReservedUniforms.push_back("adaptiveStepMultiplier"); - mReservedUniforms.push_back("splitParamsStart");; + mReservedUniforms.push_back("splitParamsStart"); mReservedUniforms.push_back("splitParamsEnd"); + mReservedUniforms.push_back("maxZDepth"); + mReservedUniforms.push_back("maxRoughness"); mReservedUniforms.push_back("modelview_delta"); mReservedUniforms.push_back("inv_modelview_delta"); diff --git a/indra/llrender/llshadermgr.h b/indra/llrender/llshadermgr.h index 8ceb140dea0..31ee663d656 100644 --- a/indra/llrender/llshadermgr.h +++ b/indra/llrender/llshadermgr.h @@ -191,6 +191,8 @@ class LLShaderMgr DEFERRED_SSR_ADAPTIVE_STEP_MULT, // "adaptiveStepMultiplier" DEFERRED_SSR_SPLIT_START, // "splitParamsStart" DEFERRED_SSR_SPLIT_END, // "splitParamsEnd" + DEFERRED_SSR_MAX_Z, // "maxZDepth" + DEFERRED_SSR_MAX_ROUGHNESS, // "maxRoughness" MODELVIEW_DELTA_MATRIX, // "modelview_delta" INVERSE_MODELVIEW_DELTA_MATRIX, // "inv_modelview_delta" diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 80b9716eebb..fff0280cb73 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7701,6 +7701,28 @@ Value 2 + RenderScreenSpaceReflectionMaxDepth + + Comment + Maximum depth from the camera to attempt a trace. + Persist + 1 + Type + F32 + Value + 256 + + RenderScreenSpaceReflectionMaxRoughness + + Comment + Maximum permitted roughness for screen space reflections. + Persist + 1 + Type + F32 + Value + 0.45 + RenderBumpmapMinDistanceSquared Comment diff --git a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl index 69d39886ef8..77b94e17e1a 100644 --- a/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl +++ b/indra/newview/app_settings/shaders/class3/deferred/screenSpaceReflUtil.glsl @@ -321,8 +321,8 @@ void advanceRayMarch( } } -const float MAX_Z_DEPTH = 256; -const float MAX_ROUGHNESS = 0.45; +uniform float maxZDepth; +uniform float maxRoughness; /** * @brief Checks if a hit point is within the specified distance range from the reflector. @@ -389,7 +389,7 @@ bool traceScreenRay( // Depth of the reflecting surface in the transformed view space. float reflectingSurfaceViewDepth = -currentPosition_transformed.z; - if (reflectingSurfaceViewDepth > MAX_Z_DEPTH) { + if (reflectingSurfaceViewDepth > maxZDepth) { // Do a sanity check: if the reflecting surface is too far away, skip ray tracing. hitColor = vec4(0.0); edgeFade = 0.0; @@ -438,7 +438,7 @@ bool traceScreenRay( break; } depthFromScreen = getLinearDepth(screenPosition); - if (depthFromScreen >= MAX_Z_DEPTH) { + if (depthFromScreen >= maxZDepth) { hit = false; break; } @@ -522,8 +522,8 @@ bool traceScreenRay( // Do a bit of distance fading if we have a hit. Use it to fade out far off objects that just don't look right. if (hit) { - float zFadeStart = MAX_Z_DEPTH * 0.8; - float zFade = 1.0 - smoothstep(zFadeStart, MAX_Z_DEPTH, furthestValidDepth); + float zFadeStart = maxZDepth * 0.8; + float zFade = 1.0 - smoothstep(zFadeStart, maxZDepth, furthestValidDepth); // Combine both fades (multiply for stronger effect) edgeFade *= zFade; @@ -652,7 +652,7 @@ float tapScreenSpaceReflection( float roughness = 1.0 - glossiness; - if (roughness < MAX_ROUGHNESS) { + if (roughness < maxRoughness) { float viewDotNormal = dot(normalize(-viewPos), normalize(n)); if (viewDotNormal <= 0.0) { @@ -660,7 +660,7 @@ float tapScreenSpaceReflection( return 0.0; } - float remappedRoughness = clamp((roughness - (MAX_ROUGHNESS * 0.6)) / (MAX_ROUGHNESS - (MAX_ROUGHNESS * 0.6)), 0.0, 1.0); + float remappedRoughness = clamp((roughness - (maxRoughness * 0.6)) / (maxRoughness - (maxRoughness * 0.6)), 0.0, 1.0); float roughnessIntensityFade = 1.0 - remappedRoughness; float roughnessFade = roughnessIntensityFade; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 40115159d1f..8d9ecc44616 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -9338,6 +9338,8 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) static LLCachedControl traceStepMultiplier(gSavedSettings, "RenderScreenSpaceReflectionAdaptiveStepMultiplier"); static LLCachedControl traceSplitStart(gSavedSettings, "RenderScreenSpaceReflectionSplitStart"); static LLCachedControl traceSplitEnd(gSavedSettings, "RenderScreenSpaceReflectionSplitEnd"); + static LLCachedControl traceMaxDepth(gSavedSettings, "RenderScreenSpaceReflectionMaxDepth"); + static LLCachedControl traceMaxRoughness(gSavedSettings, "RenderScreenSpaceReflectionMaxRoughness"); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_ITR_COUNT, 1, traceIterations().mV); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_DIST_BIAS, 1, traceDistanceBias().mV); @@ -9353,6 +9355,9 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_ADAPTIVE_STEP_MULT, 1, traceStepMultiplier().mV); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_START, 1, traceSplitStart().mV); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_END, 1, traceSplitEnd().mV); + + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_Z, traceMaxDepth()); + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_ROUGHNESS, traceMaxRoughness()); channel = shader.enableTexture(LLShaderMgr::SCENE_DEPTH); if (channel > -1) From 9b12d2de257c75c1b957a81b0913afd68bca47dc Mon Sep 17 00:00:00 2001 From: "Jonathan \"Geenz\" Goodman" Date: Mon, 9 Jun 2025 11:32:51 -0400 Subject: [PATCH 19/19] White space. --- indra/newview/pipeline.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 8d9ecc44616..0b07b0f076f 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -9355,7 +9355,7 @@ void LLPipeline::bindReflectionProbes(LLGLSLShader& shader) shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_ADAPTIVE_STEP_MULT, 1, traceStepMultiplier().mV); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_START, 1, traceSplitStart().mV); shader.uniform3fv(LLShaderMgr::DEFERRED_SSR_SPLIT_END, 1, traceSplitEnd().mV); - + shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_Z, traceMaxDepth()); shader.uniform1f(LLShaderMgr::DEFERRED_SSR_MAX_ROUGHNESS, traceMaxRoughness());