From bad19d99548e9099728f8272bae9f10decf516ca Mon Sep 17 00:00:00 2001 From: BlackYps <52536103+BlackYps@users.noreply.github.com> Date: Thu, 9 Nov 2023 19:37:20 +0100 Subject: [PATCH] Water improvements (#5654) --- effects/mesh.fx | 18 ++++++++++-------- effects/terrain.fx | 10 +++++----- effects/water2.fx | 14 ++++++-------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/effects/mesh.fx b/effects/mesh.fx index be7062a0be..ffac15548d 100644 --- a/effects/mesh.fx +++ b/effects/mesh.fx @@ -601,24 +601,26 @@ float3 ComputeNormal( sampler2D source, float2 uv, float3x3 rotationMatrix) float3 ApplyWaterColor(float depth, float3 viewDirection, float3 color, float3 emission = float3(0, 0, 0)) { // disable the whole thing on land-only maps if (surfaceElevation > 0) { - float4 waterColor = tex1D(WaterRampSampler, -depth / (surfaceElevation - abyssElevation)); // we need this switch to make it consistent with the terrain shader coloration if (IsExperimentalShader()) { + float scaledDepth = (-depth / (surfaceElevation - abyssElevation)); float3 up = float3(0,1,0); - // To simplify, we assume that the light enters vertically into the water, // this is the length that the light travels underwater back to the camera float oneOverCosV = 1 / max(dot(up, normalize(viewDirection)), 0.0001); - // light gets absorbed exponentially - float waterAbsorption = saturate(exp(-waterColor.w * (1 + oneOverCosV))); + // Light gets absorbed exponentially. + // To simplify, we assume that the light enters vertically into the water. + float waterAbsorption = 1 - saturate(exp(-scaledDepth * (1 + oneOverCosV))); // when the mesh emits light, then the path from the surface to the mesh doesn't apply - float emissionAbsorption = saturate(exp(-waterColor.w * oneOverCosV)); + float emissionTransmitted = saturate(exp(-scaledDepth * oneOverCosV)); // darken the color first to simulate the light absorption on the way in and out - color *= waterAbsorption; + color *= 1 - waterAbsorption; // lerp in the watercolor to simulate the scattered light from the dirty water - color = lerp(waterColor.rgb, color, waterAbsorption); + float4 waterColor = tex1D(WaterRampSampler, waterAbsorption); + color = lerp(color, waterColor.rgb, waterAbsorption); // similarly tune down the emission light - color += emission * emissionAbsorption; + color += emission * emissionTransmitted; } else { + float4 waterColor = tex1D(WaterRampSampler, -depth / (surfaceElevation - abyssElevation)); color = lerp(color, waterColor.rgb, waterColor.w); color += emission; } diff --git a/effects/terrain.fx b/effects/terrain.fx index bd519bb755..1ac27e5feb 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -405,16 +405,16 @@ float3 ApplyWaterColorExponentially(float3 viewDirection, float terrainHeight, f { if (waterDepth > 0) { float opacity = saturate(smoothstep(10, 200, CameraPosition.y - WaterElevation) + step(terrainHeight, WaterElevation)); - float4 waterColor = tex1D(WaterRampSampler, waterDepth); float3 up = float3(0,1,0); - // To simplify, we assume that the light enters vertically into the water, // this is the length that the light travels underwater back to the camera - float oneOverCosV = 1 / max(abs(dot(up, normalize(viewDirection))), 0.0001); - // light gets absorbed exponentially - float waterAbsorption = 1 - saturate(exp(-waterColor.w * (1 + oneOverCosV))); + float oneOverCosV = 1 / max(dot(up, normalize(viewDirection)), 0.0001); + // Light gets absorbed exponentially, + // to simplify, we assume that the light enters vertically into the water. + float waterAbsorption = 1 - saturate(exp(-waterDepth * (1 + oneOverCosV))); // darken the color first to simulate the light absorption on the way in and out color *= 1 - waterAbsorption * opacity; // lerp in the watercolor to simulate the scattered light from the dirty water + float4 waterColor = tex1D(WaterRampSampler, waterAbsorption); color = lerp(color, waterColor.rgb, waterAbsorption * opacity); } return color; diff --git a/effects/water2.fx b/effects/water2.fx index 7a6eef8d1f..a18e413297 100644 --- a/effects/water2.fx +++ b/effects/water2.fx @@ -24,9 +24,9 @@ float3 waveCrestColor = float3( 1, 1, 1); float4x4 WorldToView; float4x4 Projection; -// red: wave normal strength -// green: water depth -// blue: ??? +// red: originally wave normal strength, now used for shadow +// green: water depth (autogenerated by the engine) +// blue: shoreline (autogenerated by the engine) // alpha: foam reduction texture UtilityTextureC; @@ -350,10 +350,6 @@ float4 HighFidelityPS( VS_OUTPUT inV, // scale, bias and normalize float3 N = 2.0 * sum.xyz - 4.0; N = normalize(N.xzy); - - // flatness - float3 up = float3(0,1,0); - N = lerp(up, N, waterTexture.r); float3 R = reflect(-viewVector, N); @@ -396,10 +392,12 @@ float4 HighFidelityPS( VS_OUTPUT inV, sunReflection = sunReflection * fresnel; // the sun shouldn't be visible where a unit reflection is sunReflection *= (1 - saturate(reflectedPixels.a * 2)); + // we can control this value to have terrain cast a shadow on the water surface + sunReflection *= waterTexture.r; refractedPixels.xyz += sunReflection; // Lerp in the wave crests - refractedPixels.xyz = lerp(refractedPixels.xyz, waveCrestColor, (1 - waterTexture.a) * waveCrest); + refractedPixels.xyz = lerp(refractedPixels.xyz, waveCrestColor, (1 - waterTexture.a) * (1 - waterTexture.b) * waveCrest); // return the pixels masked out by the water mask float4 returnPixels = refractedPixels;