From 2d2e6d2073a1392852c2f3c9d8deb975028b00a2 Mon Sep 17 00:00:00 2001 From: BlackYps Date: Sun, 20 Oct 2024 00:20:48 +0200 Subject: [PATCH 1/7] Make exponential water absorption an option on all shaders --- effects/mesh.fx | 4 +-- effects/terrain.fx | 68 ++++++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/effects/mesh.fx b/effects/mesh.fx index 22171ff685..c3c70c00a9 100644 --- a/effects/mesh.fx +++ b/effects/mesh.fx @@ -378,7 +378,7 @@ struct SHIELDIMPACT_VERTEX /// /////////////////////////////////////// -bool IsExperimentalShader() { +bool MapUsesAdvancedWater() { // lightMultiplier is one of the few variables that is driven by the map, // but accessible by the mesh shader. return lightMultiplier > 2.1; @@ -602,7 +602,7 @@ float3 ApplyWaterColor(float depth, float3 viewDirection, float3 color, float3 e // disable the whole thing on land-only maps if (surfaceElevation > 0) { // we need this switch to make it consistent with the terrain shader coloration - if (IsExperimentalShader()) { + if (MapUsesAdvancedWater()) { // We need to multiply by 2 to match the terrain shader. float scaledDepth = (-depth / (surfaceElevation - abyssElevation)) * 2; float3 up = float3(0,1,0); diff --git a/effects/terrain.fx b/effects/terrain.fx index 44f65662a3..9f266ed8ac 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -354,6 +354,12 @@ bool IsExperimentalShader() { return UpperAlbedoTile.x < 1.0; } +bool MapUsesAdvancedWater() { + // LightingMultiplier is one of the few variables that is driven by the map, + // but accessible by the mesh shader. + return LightingMultiplier > 2.1; +} + // sample a texture that is another buffer the same size as the one // we are rendering into and with the viewport setup the same way. float4 SampleScreen(sampler inSampler, float4 inTex) @@ -388,35 +394,29 @@ float ComputeShadow( float4 vShadowCoord ) return tex2D( ShadowSampler, vShadowCoord ).g; } -// apply the water color -float3 ApplyWaterColor(float terrainHeight, float waterDepth, float3 color) +float3 ApplyWaterColor(float3 viewDirection, float terrainHeight, float waterDepth, float3 color) { if (waterDepth > 0) { // With this extra check we get rid of unwanted coloration on steep cliffs when zoomed in, // but we prevent that terrain tesselation swallows too much of the water when zoomed out float opacity = saturate(smoothstep(10, 200, CameraPosition.y - WaterElevation) + step(terrainHeight, WaterElevation)); - float4 waterColor = tex1D(WaterRampSampler, waterDepth); - color = lerp(color.xyz, waterColor.rgb, waterColor.a * opacity); - } - return color; -} - -float3 ApplyWaterColorExponentially(float3 viewDirection, float terrainHeight, float waterDepth, float3 color) -{ - if (waterDepth > 0) { - float opacity = saturate(smoothstep(10, 200, CameraPosition.y - WaterElevation) + step(terrainHeight, WaterElevation)); - float3 up = float3(0,1,0); - // 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, - // to simplify, we assume that the light enters vertically into the water. - // We need to multiply by 2 to reach 98% absorption as the waterDepth can't go over 1. - float waterAbsorption = 1 - saturate(exp(-waterDepth * 2 * (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); + if (MapUsesAdvancedWater()) { + float3 up = float3(0,1,0); + // 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, + // to simplify, we assume that the light enters vertically into the water. + // We need to multiply by 2 to reach 98% absorption as the waterDepth can't go over 1. + float waterAbsorption = 1 - saturate(exp(-waterDepth * 2 * (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); + } else { + float4 waterColor = tex1D(WaterRampSampler, waterDepth); + color = lerp(color, waterColor.rgb, waterColor.a * opacity); + } } return color; } @@ -444,11 +444,7 @@ float4 CalculateLighting( float3 inNormal, float3 worldTerrain, float3 inAlbedo, light = LightingMultiplier * light + ShadowFillColor * ( 1 - light ); color.rgb = light * inAlbedo; - if (IsExperimentalShader()) { - color.rgb = ApplyWaterColorExponentially(-viewDirection, worldTerrain.z, waterDepth, color); - } else { - color.rgb = ApplyWaterColor(worldTerrain.z, waterDepth, color); - } + color.rgb = ApplyWaterColor(-viewDirection, worldTerrain.z, waterDepth, color); color.a = 0.01f + (specular*SpecularColor.w); return color; @@ -959,7 +955,7 @@ float4 TerrainAlbedoXP( VS_OUTPUT pixel) : COLOR albedo.rgb = light * ( albedo.rgb + specular.rgb ); float waterDepth = tex2D(UtilitySamplerC,pixel.mTexWT*TerrainScale).g; - albedo.rgb = ApplyWaterColor(pixel.mTexWT.z, waterDepth, albedo.rgb); + albedo.rgb = ApplyWaterColor(-pixel.mViewDirection, pixel.mTexWT.z, waterDepth, albedo.rgb); return float4(albedo.rgb, 0.01f); } @@ -2022,7 +2018,7 @@ float4 TerrainPBRAlbedoPS ( VS_OUTPUT inV) : COLOR float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); - color = ApplyWaterColorExponentially(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); + color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); // SpecularColor.ba, LowerNormalTile, Stratum7AlbedoTile and Stratum7NormalTile are unused now @@ -2222,13 +2218,13 @@ float4 Terrain001AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // compute water ramp intensity float waterDepth = tex2Dproj(UtilitySamplerC, coordinates).g; - albedo.rgb = ApplyWaterColorExponentially(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, albedo.rgb); + albedo.rgb = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, albedo.rgb); return float4(albedo.rgb, 0.01f); } /* # Similar to TTerrainXP, but upperAlbedo is used for map-wide # - # textures and we use better water color calculations. # + # textures. # # It is designed to be a drop-in replacement for TTerrainXP. # */ technique Terrain001 < string usage = "composite"; @@ -2456,7 +2452,7 @@ float4 Terrain003AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // compute water ramp intensity float waterDepth = tex2D(UtilitySamplerC, coordinates).g; - albedo.rgb = ApplyWaterColorExponentially(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, albedo.rgb); + albedo.rgb = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, albedo.rgb); return float4(albedo.rgb, 0.01f); } @@ -2616,7 +2612,7 @@ float4 Terrain101AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); - color = ApplyWaterColorExponentially(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); + color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); } @@ -2801,7 +2797,7 @@ float4 Terrain301AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); - color = ApplyWaterColorExponentially(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); + color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); } From 49f9dc3d799d3b63261bd20d13040fa4ab3162bc Mon Sep 17 00:00:00 2001 From: BlackYps Date: Sun, 20 Oct 2024 00:58:12 +0200 Subject: [PATCH 2/7] Improve naming conventions --- effects/terrain.fx | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/effects/terrain.fx b/effects/terrain.fx index 9f266ed8ac..44fb29a16a 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -344,7 +344,7 @@ VS_OUT FixedFuncVS( VS_IN In ) return Out; } -bool IsExperimentalShader() { +bool ShaderUsesTerrainInfoTexture() { // The tile value basically says how often the texture gets repeated on the map. // A value less than one doesn't make sense under normal conditions, so it is // relatively save to use it as our switch. @@ -427,10 +427,10 @@ float4 CalculateLighting( float3 inNormal, float3 worldTerrain, float3 inAlbedo, float4 color = float4( 0, 0, 0, 0 ); float shadow = ( inShadows && ( 1 == ShadowsEnabled ) ) ? ComputeShadow(shadowCoords) : 1; - if (IsExperimentalShader()) { + if (ShaderUsesTerrainInfoTexture()) { float3 position = TerrainScale * worldTerrain; - float mapShadow = tex2D(UpperAlbedoSampler, position.xy).w; - shadow = shadow * mapShadow; + float terrainShadow = tex2D(UpperAlbedoSampler, position.xy).w; + shadow = shadow * terrainShadow; } // calculate some specular @@ -495,9 +495,9 @@ float3 PBR(VS_OUTPUT inV, float4 position, float3 albedo, float3 n, float roughn float shadow = 1; if (ShadowsEnabled == 1) { - float mapShadow = tex2D(UpperAlbedoSampler, position.xy).w; // 1 where sun is, 0 where shadow is + float terrainShadow = tex2D(UpperAlbedoSampler, position.xy).w; // 1 where sun is, 0 where shadow is shadow = tex2D(ShadowSampler, inV.mShadow.xy).g; // 1 where sun is, 0 where shadow is - shadow *= mapShadow; + shadow *= terrainShadow; } float facingSpecular = 0.04; @@ -835,7 +835,7 @@ float4 TerrainBasisPS( VS_OUTPUT inV ) : COLOR float4 TerrainBasisPSBiCubic( VS_OUTPUT inV ) : COLOR { float4 result; - if (IsExperimentalShader()) { + if (ShaderUsesTerrainInfoTexture()) { float4 position = TerrainScale * inV.mTexWT; result = (float4(1, 1, tex2D(UpperAlbedoSampler, position.xy).xy)); } else { @@ -2165,14 +2165,14 @@ float4 Terrain001AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // x = normals-x // y = normals-z - // z = unused + // z = water depth // w = shadows - float4 utility = tex2D(UpperAlbedoSampler, coordinates.xy); - float mapShadow = utility.w; + float4 terrainInfo = tex2D(UpperAlbedoSampler, coordinates.xy); + float terrainShadow = terrainInfo.w; // disable shadows when game settings tell us to if (0 == ShadowsEnabled) { - mapShadow = 1.0f; + terrainShadow = 1.0f; } // sample the albedo's @@ -2199,7 +2199,7 @@ float4 Terrain001AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // compute the shadows, combining the baked and dynamic shadows float shadow = tex2D(ShadowSampler, inV.mShadow.xy).g; // 1 where sun is, 0 where shadow is - shadow = shadow * mapShadow; + shadow = shadow * terrainShadow; // normalize the pre-computed normal float3 normal = normalize(2 * SampleScreen(NormalSampler,inV.mTexSS).xyz - 1); @@ -2391,14 +2391,14 @@ float4 Terrain003AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // x = normals-x // y = normals-z - // z = unused + // z = water depth // w = shadows - float4 utility01 = tex2D(UpperAlbedoSampler, coordinates.xy); - float mapShadow = utility01.w; + float4 terrainInfo = tex2D(UpperAlbedoSampler, coordinates.xy); + float terrainShadow = terrainInfo.w; // disable shadows when game settings tell us to if (0 == ShadowsEnabled) { - mapShadow = 1.0f; + terrainShadow = 1.0f; } // x = specular @@ -2433,7 +2433,7 @@ float4 Terrain003AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR // compute the shadows, combining the baked and dynamic shadows float shadow = tex2D(ShadowSampler, inV.mShadow.xy).g; - shadow = shadow * mapShadow; + shadow = shadow * terrainShadow; // normalize the pre-computed normal float3 normal = normalize(2 * SampleScreen(NormalSampler,inV.mTexSS).xyz - 1); From 537b838b4034cce880cd2e0c4378423cfe147854 Mon Sep 17 00:00:00 2001 From: BlackYps Date: Sun, 20 Oct 2024 01:13:55 +0200 Subject: [PATCH 3/7] Give decals ability to use pbr lighting --- effects/terrain.fx | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/effects/terrain.fx b/effects/terrain.fx index 44fb29a16a..b9d385ba28 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -348,12 +348,26 @@ bool ShaderUsesTerrainInfoTexture() { // The tile value basically says how often the texture gets repeated on the map. // A value less than one doesn't make sense under normal conditions, so it is // relatively save to use it as our switch. + // We use the upper layer slot to store the terrain info texture, so we don't need + // the tile value for anything else. - // in order to trigger this you can set the albedo scale to be bigger than the map - // size. Use the value 10000 to be safe for any map + // In order to trigger this you need to set the albedo scale to be bigger than the + // map size. Use the value 10000 to be safe for any map return UpperAlbedoTile.x < 1.0; } +bool ShaderUsesPbrRendering() { + // The tile value basically says how often the texture gets repeated on the map. + // A value less than one doesn't make sense under normal conditions, so it is + // relatively save to use it as our switch. + // We use the stratum 7 normal slot to store the roughness texture, so we don't need + // the tile value for anything else. + + // In order to trigger this you need to set the normal scale to be bigger than the + // map size. Use the value 10000 to be safe for any map + return Stratum7NormalTile.x < 1.0; +} + bool MapUsesAdvancedWater() { // LightingMultiplier is one of the few variables that is driven by the map, // but accessible by the mesh shader. @@ -1335,7 +1349,13 @@ float4 DecalsPS( VS_OUTPUT inV, uniform bool inShadows) : COLOR float waterDepth = tex2Dproj(UtilitySamplerC, inV.mTexWT * TerrainScale).g; - float3 color = CalculateLighting(normal, inV.mTexWT.xyz, decalAlbedo.xyz, decalSpec.r, waterDepth, inV.mShadow, inShadows).xyz; + float3 color; + // We want the decals to behave consistently with the rest of the ground + if (ShaderUsesPbrRendering()) { + color = PBR(inV, TerrainScale * inV.mTexWT, decalAlbedo.rgb, normal, 1-decalSpec.r, waterDepth); + } else { + color = CalculateLighting(normal, inV.mTexWT.xyz, decalAlbedo.xyz, decalSpec.r, waterDepth, inV.mShadow, inShadows).xyz; + } return float4(color.rgb, decalAlbedo.w * decalMask.w * DecalAlpha); } @@ -2021,7 +2041,7 @@ float4 TerrainPBRAlbedoPS ( VS_OUTPUT inV) : COLOR color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); - // SpecularColor.ba, LowerNormalTile, Stratum7AlbedoTile and Stratum7NormalTile are unused now + // SpecularColor.ba, LowerNormalTile and Stratum7AlbedoTile are unused now // Candidates for configurable values are the rotation matrix values } From ec60cf4785210f9c8343a50415f4c3b7f7a3835a Mon Sep 17 00:00:00 2001 From: BlackYps Date: Sun, 20 Oct 2024 01:20:41 +0200 Subject: [PATCH 4/7] Simplify pbr function call --- effects/terrain.fx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/effects/terrain.fx b/effects/terrain.fx index b9d385ba28..3c38bea6db 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -504,12 +504,12 @@ float GeometrySmith(float3 n, float nDotV, float3 l, float roughness) return gs1 * gs2; } -float3 PBR(VS_OUTPUT inV, float4 position, float3 albedo, float3 n, float roughness, float waterDepth) { +float3 PBR(VS_OUTPUT inV, float3 albedo, float3 n, float roughness, float waterDepth) { // See https://blog.selfshadow.com/publications/s2013-shading-course/ float shadow = 1; if (ShadowsEnabled == 1) { - float terrainShadow = tex2D(UpperAlbedoSampler, position.xy).w; // 1 where sun is, 0 where shadow is + float terrainShadow = tex2D(UpperAlbedoSampler, TerrainScale * inV.mTexWT).w; // 1 where sun is, 0 where shadow is shadow = tex2D(ShadowSampler, inV.mShadow.xy).g; // 1 where sun is, 0 where shadow is shadow *= terrainShadow; } @@ -1352,7 +1352,7 @@ float4 DecalsPS( VS_OUTPUT inV, uniform bool inShadows) : COLOR float3 color; // We want the decals to behave consistently with the rest of the ground if (ShaderUsesPbrRendering()) { - color = PBR(inV, TerrainScale * inV.mTexWT, decalAlbedo.rgb, normal, 1-decalSpec.r, waterDepth); + color = PBR(inV, decalAlbedo.rgb, normal, 1-decalSpec.r, waterDepth); } else { color = CalculateLighting(normal, inV.mTexWT.xyz, decalAlbedo.xyz, decalSpec.r, waterDepth, inV.mShadow, inShadows).xyz; } @@ -2037,7 +2037,7 @@ float4 TerrainPBRAlbedoPS ( VS_OUTPUT inV) : COLOR float roughness = saturate(albedo.a * mask1.w * 2 + 0.01); float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; - float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); + float3 color = PBR(inV, albedo, normal, roughness, waterDepth); color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); @@ -2631,7 +2631,7 @@ float4 Terrain101AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR float roughness = saturate(albedo.a * mask1.w * 2 + 0.01); float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; - float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); + float3 color = PBR(inV, albedo, normal, roughness, waterDepth); color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); @@ -2816,7 +2816,7 @@ float4 Terrain301AlbedoPS ( VS_OUTPUT inV, uniform bool halfRange ) : COLOR float roughness = saturate(albedo.a * mask1.w * 2 + 0.01); float waterDepth = tex2D(UpperAlbedoSampler, position.xy).b; - float3 color = PBR(inV, position, albedo, normal, roughness, waterDepth); + float3 color = PBR(inV, albedo, normal, roughness, waterDepth); color = ApplyWaterColor(-1 * inV.mViewDirection, inV.mTexWT.z, waterDepth, color); return float4(color, 0.01f); From dd3d8de31eb40b9e8412adaa63d144184b2936bc Mon Sep 17 00:00:00 2001 From: BlackYps Date: Sun, 20 Oct 2024 01:36:52 +0200 Subject: [PATCH 5/7] Adjust decal roughness --- effects/terrain.fx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/effects/terrain.fx b/effects/terrain.fx index 3c38bea6db..0a12e351d4 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -1352,7 +1352,7 @@ float4 DecalsPS( VS_OUTPUT inV, uniform bool inShadows) : COLOR float3 color; // We want the decals to behave consistently with the rest of the ground if (ShaderUsesPbrRendering()) { - color = PBR(inV, decalAlbedo.rgb, normal, 1-decalSpec.r, waterDepth); + color = PBR(inV, decalAlbedo.rgb, normal, 0.9 * (1-decalSpec.r), waterDepth); } else { color = CalculateLighting(normal, inV.mTexWT.xyz, decalAlbedo.xyz, decalSpec.r, waterDepth, inV.mShadow, inShadows).xyz; } From eec162932a6e7d86ca1a0adaff9853ab0908b62f Mon Sep 17 00:00:00 2001 From: BlackYps Date: Mon, 21 Oct 2024 10:53:58 +0200 Subject: [PATCH 6/7] Improve comment --- effects/terrain.fx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/effects/terrain.fx b/effects/terrain.fx index 0a12e351d4..e12b7f1893 100644 --- a/effects/terrain.fx +++ b/effects/terrain.fx @@ -352,7 +352,7 @@ bool ShaderUsesTerrainInfoTexture() { // the tile value for anything else. // In order to trigger this you need to set the albedo scale to be bigger than the - // map size. Use the value 10000 to be safe for any map + // map size in the editor. Use the value 10000 to be safe for any map return UpperAlbedoTile.x < 1.0; } @@ -364,7 +364,7 @@ bool ShaderUsesPbrRendering() { // the tile value for anything else. // In order to trigger this you need to set the normal scale to be bigger than the - // map size. Use the value 10000 to be safe for any map + // map size in the editor. Use the value 10000 to be safe for any map return Stratum7NormalTile.x < 1.0; } From 02d590f082000618d64cf2ae0a658b1b9bce3cd5 Mon Sep 17 00:00:00 2001 From: BlackYps Date: Mon, 21 Oct 2024 10:54:07 +0200 Subject: [PATCH 7/7] Add changelog snippet --- changelog/snippets/other.6485.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/snippets/other.6485.md diff --git a/changelog/snippets/other.6485.md b/changelog/snippets/other.6485.md new file mode 100644 index 0000000000..eeee1a325d --- /dev/null +++ b/changelog/snippets/other.6485.md @@ -0,0 +1,2 @@ +- (#6485) The new, better way of calculating the water absorption is now available for all terrain shaders. The only requirement is that the light multiplier is set to more than 2.1. Decals now use PBR light calculations if the terrain shader uses it, making them more consistent with the ground they are on. +- \ No newline at end of file