From 3a2cac1c12ef8a2288e25966d663d8fa4c83e765 Mon Sep 17 00:00:00 2001 From: Dale Eidd Date: Wed, 14 Aug 2024 15:47:43 +1000 Subject: [PATCH] Fix #1156 Use infinity water depth Fixes incorrect depth values when away from zero, and greatly simplifies everything. Caution must be taken as infinity can cause issues. Water depth sampling had to be moved to the fragment shader, as it had artifacts possibly from interpolation. Additionally, improved fading out of shallow SSS towards the end of cascades. --- .../LodData/LodDataMgrSeaFloorDepth.cs | 6 ++-- .../Crest/Scripts/LodData/OceanDepthCache.cs | 4 +-- .../Settings/SimSettingsAnimatedWaves.cs | 4 +-- crest/Assets/Crest/Crest/Shaders/Ocean.shader | 36 ++++++++++++++----- .../Crest/Crest/Shaders/OceanConstants.hlsl | 3 +- .../Crest/Crest/Shaders/OceanHelpersNew.hlsl | 7 ++-- .../OceanInputs/AnimWavesSpectrum.shader | 2 +- .../Resources/AnimWavesGerstner.shader | 2 +- .../AnimWavesGerstnerGeometry.shader | 2 +- .../Underwater/UnderwaterCurtain.shader | 2 +- .../Underwater/UnderwaterEffectShared.hlsl | 2 +- 11 files changed, 43 insertions(+), 27 deletions(-) diff --git a/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgrSeaFloorDepth.cs b/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgrSeaFloorDepth.cs index d3fc56222..ce5bb4f50 100644 --- a/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgrSeaFloorDepth.cs +++ b/crest/Assets/Crest/Crest/Scripts/LodData/LodDataMgrSeaFloorDepth.cs @@ -17,13 +17,13 @@ namespace Crest public class LodDataMgrSeaFloorDepth : LodDataMgr { // NOTE: Must match CREST_OCEAN_DEPTH_BASELINE in OceanConstants.hlsl. - internal const float k_DepthBaseline = 1_000f; + internal const float k_DepthBaseline = Mathf.Infinity; public override string SimName => "SeaFloorDepth"; protected override GraphicsFormat RequestedTextureFormat => Settings._allowVaryingWaterLevel ? GraphicsFormat.R32G32_SFloat : GraphicsFormat.R16_SFloat; protected override bool NeedToReadWriteTextureData => false; - // We want the clear colour to be the min terrain height (-1000m) in X, and sea level offset 0m in Y. - readonly static Color s_nullColor = Color.red * -k_DepthBaseline; + // Lowest possible scene height in X, and sea level offset 0m in Y. + readonly static Color s_nullColor = new Color(-k_DepthBaseline, 0, 0, 0); static Texture2DArray s_nullTexture; protected override Texture2DArray NullTexture => s_nullTexture; diff --git a/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs b/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs index 5e80f6574..b6c709a2e 100644 --- a/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs +++ b/crest/Assets/Crest/Crest/Scripts/LodData/OceanDepthCache.cs @@ -212,9 +212,7 @@ bool InitObjects(bool updateComponents) _camDepthCache.transform.parent = transform; _camDepthCache.transform.localEulerAngles = 90f * Vector3.right; _camDepthCache.orthographic = true; - _camDepthCache.clearFlags = CameraClearFlags.SolidColor; - // Clear to 'very deep' - _camDepthCache.backgroundColor = Color.white * LodDataMgrSeaFloorDepth.k_DepthBaseline; + _camDepthCache.clearFlags = CameraClearFlags.Depth; _camDepthCache.enabled = false; _camDepthCache.allowMSAA = false; _camDepthCache.allowDynamicResolution = false; diff --git a/crest/Assets/Crest/Crest/Scripts/LodData/Settings/SimSettingsAnimatedWaves.cs b/crest/Assets/Crest/Crest/Scripts/LodData/Settings/SimSettingsAnimatedWaves.cs index eccf4fb18..c87376fab 100644 --- a/crest/Assets/Crest/Crest/Scripts/LodData/Settings/SimSettingsAnimatedWaves.cs +++ b/crest/Assets/Crest/Crest/Scripts/LodData/Settings/SimSettingsAnimatedWaves.cs @@ -35,8 +35,8 @@ public partial class SimSettingsAnimatedWaves : SimSettingsBase public float AttenuationInShallows => _attenuationInShallows; [Tooltip("Any water deeper than this will receive full wave strength. The lower the value, the less effective the depth cache will be at attenuating very large waves. Set to the maximum value (1,000) to disable.")] - [SerializeField, Range(1f, LodDataMgrSeaFloorDepth.k_DepthBaseline)] - float _shallowsMaxDepth = LodDataMgrSeaFloorDepth.k_DepthBaseline; + [SerializeField, Range(1f, 1000f)] + float _shallowsMaxDepth = 1000f; public float MaximumAttenuationDepth => _shallowsMaxDepth; public enum CollisionSources diff --git a/crest/Assets/Crest/Crest/Shaders/Ocean.shader b/crest/Assets/Crest/Crest/Shaders/Ocean.shader index 14de04d28..2159261ea 100644 --- a/crest/Assets/Crest/Crest/Shaders/Ocean.shader +++ b/crest/Assets/Crest/Crest/Shaders/Ocean.shader @@ -364,7 +364,6 @@ Shader "Crest/Ocean" // sample shape textures - always lerp between 2 LOD scales, so sample two textures o.flow_shadow = half4(0.0, 0.0, 0.0, 0.0); - o.lodAlpha_worldXZUndisplaced_oceanDepth.w = CREST_OCEAN_DEPTH_BASELINE; // Sample shape textures - always lerp between 2 LOD scales, so sample two textures // Calculate sample weights. params.z allows shape to be faded out (used on last lod to support pop-less scale transitions) @@ -410,11 +409,12 @@ Shader "Crest/Ocean" // Data that needs to be sampled at the displaced position half seaLevelOffset = 0.0; o.seaLevelDerivs = 0.0; + half seaDepth = 0.0; if (wt_smallerLod > 0.0001) { const float3 uv_slice_smallerLodDisp = WorldToUV(o.worldPos.xz, cascadeData0, _LD_SliceIndex); - SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_smallerLodDisp, wt_smallerLod, o.lodAlpha_worldXZUndisplaced_oceanDepth.w, seaLevelOffset, cascadeData0, o.seaLevelDerivs); + SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_smallerLodDisp, wt_smallerLod, seaDepth, seaLevelOffset, cascadeData0, o.seaLevelDerivs); #if _SHADOWS_ON // The minimum sampling weight is lower than others to fix shallow water colour popping. @@ -428,7 +428,7 @@ Shader "Crest/Ocean" { const float3 uv_slice_biggerLodDisp = WorldToUV(o.worldPos.xz, cascadeData1, _LD_SliceIndex + 1); - SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_biggerLodDisp, wt_biggerLod, o.lodAlpha_worldXZUndisplaced_oceanDepth.w, seaLevelOffset, cascadeData1, o.seaLevelDerivs); + SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_biggerLodDisp, wt_biggerLod, seaDepth, seaLevelOffset, cascadeData1, o.seaLevelDerivs); #if _SHADOWS_ON // The minimum sampling weight is lower than others to fix shallow water colour popping. @@ -526,10 +526,6 @@ Shader "Crest/Ocean" half3 screenPos = input.screenPosXYW; half2 uvDepth = screenPos.xy / screenPos.z; - #if _CLIPUNDERTERRAIN_ON - clip(input.lodAlpha_worldXZUndisplaced_oceanDepth.w + 2.0); - #endif - half3 view = normalize(_WorldSpaceCameraPos - input.worldPos); // water surface depth, and underlying scene opaque surface depth @@ -563,6 +559,8 @@ Shader "Crest/Ocean" #if _ALBEDO_ON half4 albedo = 0.0; #endif + + float seaDepth = 0.0; if (wt_smallerLod > 0.001) { const float3 uv_slice_smallerLod = WorldToUV(positionXZWSUndisplaced, cascadeData0, _LD_SliceIndex); @@ -575,6 +573,10 @@ Shader "Crest/Ocean" #if _ALBEDO_ON SampleAlbedo(_LD_TexArray_Albedo, uv_slice_smallerLod, wt_smallerLod, albedo); #endif + + #if defined(_SUBSURFACESHALLOWCOLOUR_ON) || defined(_CLIPUNDERTERRAIN_ON) + SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_smallerLod, wt_smallerLod, seaDepth); + #endif } if (wt_biggerLod > 0.001) { @@ -588,8 +590,24 @@ Shader "Crest/Ocean" #if _ALBEDO_ON SampleAlbedo(_LD_TexArray_Albedo, uv_slice_biggerLod, wt_biggerLod, albedo); #endif + + #if defined(_SUBSURFACESHALLOWCOLOUR_ON) || defined(_CLIPUNDERTERRAIN_ON) + SampleSeaDepth(_LD_TexArray_SeaFloorDepth, uv_slice_biggerLod, wt_biggerLod, seaDepth); + #endif } +#if _SUBSURFACESHALLOWCOLOUR_ON + seaDepth = min(_SubSurfaceDepthMax, seaDepth); + if (_LD_SliceIndex == ((uint)_SliceCount - 1)) + { + seaDepth = lerp(_SubSurfaceDepthMax, seaDepth, wt_smallerLod); + } +#endif + + #if _CLIPUNDERTERRAIN_ON + clip(seaDepth + 2.0); + #endif + #if _SUBSURFACESCATTERING_ON // Extents need the default SSS to avoid popping and not being noticeably different. if (_LD_SliceIndex == ((uint)_SliceCount - 1)) @@ -668,7 +686,7 @@ Shader "Crest/Ocean" // Compute color of ocean - in-scattered light + refracted scene half3 scatterCol = ScatterColour ( - input.lodAlpha_worldXZUndisplaced_oceanDepth.w, + seaDepth, shadow.x, sss, view, @@ -752,7 +770,7 @@ Shader "Crest/Ocean" // global shadow value. scatterCol = ScatterColour ( - input.lodAlpha_worldXZUndisplaced_oceanDepth.w, + seaDepth, UnderwaterShadowSSS(_WorldSpaceCameraPos.xz), sss, view, diff --git a/crest/Assets/Crest/Crest/Shaders/OceanConstants.hlsl b/crest/Assets/Crest/Crest/Shaders/OceanConstants.hlsl index 0148c9f96..c3b8bdda6 100644 --- a/crest/Assets/Crest/Crest/Shaders/OceanConstants.hlsl +++ b/crest/Assets/Crest/Crest/Shaders/OceanConstants.hlsl @@ -18,7 +18,8 @@ // NOTE: Must match k_DepthBaseline in LodDataMgrSeaFloorDepth.cs. // Bias ocean floor depth so that default (0) values in texture are not interpreted as shallow and generating foam everywhere -#define CREST_OCEAN_DEPTH_BASELINE 1000.0 +#define CREST_OCEAN_DEPTH_BASELINE 1.#INF +#define CREST_MAXIMUM_ATTENUATION_DEPTH 1000.0 // Soft shadows is red, hard shadows is green. #define CREST_SHADOW_INDEX_SOFT 0 diff --git a/crest/Assets/Crest/Crest/Shaders/OceanHelpersNew.hlsl b/crest/Assets/Crest/Crest/Shaders/OceanHelpersNew.hlsl index 50d493f99..3f998ad28 100644 --- a/crest/Assets/Crest/Crest/Shaders/OceanHelpersNew.hlsl +++ b/crest/Assets/Crest/Crest/Shaders/OceanHelpersNew.hlsl @@ -126,21 +126,20 @@ void SampleSeaDepth(in Texture2DArray i_oceanDepthSampler, in float3 i_uv_slice, { const half2 terrainHeight_seaLevelOffset = i_oceanDepthSampler.SampleLevel(LODData_linear_clamp_sampler, i_uv_slice.xyz, 0.0).xy; const half waterDepth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; - io_oceanDepth += i_wt * (waterDepth - CREST_OCEAN_DEPTH_BASELINE); + io_oceanDepth += i_wt * waterDepth; } void SampleSingleSeaDepth(Texture2DArray i_oceanDepthSampler, float3 i_uv_slice, inout half io_oceanDepth, inout half io_seaLevelOffset) { const half2 terrainHeight_seaLevelOffset = i_oceanDepthSampler.SampleLevel(LODData_linear_clamp_sampler, i_uv_slice, 0.0).xy; - const half waterDepth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; - io_oceanDepth = waterDepth - CREST_OCEAN_DEPTH_BASELINE; + io_oceanDepth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; io_seaLevelOffset = terrainHeight_seaLevelOffset.y; } void SampleSeaDepth(in Texture2DArray i_oceanDepthSampler, in float3 i_uv_slice, in float i_wt, inout half io_oceanDepth, inout half io_seaLevelOffset, const CascadeParams i_cascadeParams, inout float2 io_seaLevelDerivs) { const half2 terrainHeight_seaLevelOffset = i_oceanDepthSampler.SampleLevel( LODData_linear_clamp_sampler, i_uv_slice, 0.0 ).xy; - io_oceanDepth += i_wt * (_OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y - CREST_OCEAN_DEPTH_BASELINE); + io_oceanDepth += i_wt * (_OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y); io_seaLevelOffset += i_wt * terrainHeight_seaLevelOffset.y; { diff --git a/crest/Assets/Crest/Crest/Shaders/OceanInputs/AnimWavesSpectrum.shader b/crest/Assets/Crest/Crest/Shaders/OceanInputs/AnimWavesSpectrum.shader index 189797f53..0b32afd1b 100644 --- a/crest/Assets/Crest/Crest/Shaders/OceanInputs/AnimWavesSpectrum.shader +++ b/crest/Assets/Crest/Crest/Shaders/OceanInputs/AnimWavesSpectrum.shader @@ -47,7 +47,7 @@ Shader "Crest/Inputs/Shape Waves/Sample Spectrum" const half depth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; // Attenuate if depth is less than half of the average wavelength. half weight = saturate(2.0 * depth / _AverageWavelength); - if (_MaximumAttenuationDepth < CREST_OCEAN_DEPTH_BASELINE) + if (_MaximumAttenuationDepth < CREST_MAXIMUM_ATTENUATION_DEPTH) { weight = lerp(weight, 1.0, saturate(depth / _MaximumAttenuationDepth)); } diff --git a/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstner.shader b/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstner.shader index 8c91d1e71..55e12388f 100644 --- a/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstner.shader +++ b/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstner.shader @@ -77,7 +77,7 @@ Shader "Hidden/Crest/Inputs/Animated Waves/Gerstner Global" _LD_TexArray_SeaFloorDepth.SampleLevel(LODData_linear_clamp_sampler, float3(input.uv_uvWaves.xy, _LD_SliceIndex), 0.0).xy; const half depth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; half depth_wt = saturate(2.0 * depth / _AverageWavelength); - if (_MaximumAttenuationDepth < CREST_OCEAN_DEPTH_BASELINE) + if (_MaximumAttenuationDepth < CREST_MAXIMUM_ATTENUATION_DEPTH) { depth_wt = lerp(depth_wt, 1.0, saturate(depth / _MaximumAttenuationDepth)); } diff --git a/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstnerGeometry.shader b/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstnerGeometry.shader index 850de15ff..649fa64c3 100644 --- a/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstnerGeometry.shader +++ b/crest/Assets/Crest/Crest/Shaders/OceanInputs/Resources/AnimWavesGerstnerGeometry.shader @@ -113,7 +113,7 @@ Shader "Crest/Inputs/Animated Waves/Gerstner Geometry" const half2 terrainHeight_seaLevelOffset = _LD_TexArray_SeaFloorDepth.SampleLevel(LODData_linear_clamp_sampler, input.uv_slice, 0.0).xy; const half depth = _OceanCenterPosWorld.y - terrainHeight_seaLevelOffset.x + terrainHeight_seaLevelOffset.y; half depth_wt = saturate(2.0 * depth / _AverageWavelength); - if (_MaximumAttenuationDepth < CREST_OCEAN_DEPTH_BASELINE) + if (_MaximumAttenuationDepth < CREST_MAXIMUM_ATTENUATION_DEPTH) { depth_wt = lerp(depth_wt, 1.0, saturate(depth / _MaximumAttenuationDepth)); } diff --git a/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterCurtain.shader b/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterCurtain.shader index 4b0357185..4957e0a9e 100644 --- a/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterCurtain.shader +++ b/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterCurtain.shader @@ -199,7 +199,7 @@ Shader "Crest/Underwater Curtain" const half shadow = 1.0; const half sss = 0.0; - half seaFloorDepth = CREST_OCEAN_DEPTH_BASELINE; + half seaFloorDepth = 0.0; #if _SUBSURFACESHALLOWCOLOUR_ON { // compute scatter colour from cam pos. two scenarios this can be called: diff --git a/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterEffectShared.hlsl b/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterEffectShared.hlsl index d2d48ee5b..f879192eb 100644 --- a/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterEffectShared.hlsl +++ b/crest/Assets/Crest/Crest/Shaders/Underwater/UnderwaterEffectShared.hlsl @@ -197,7 +197,7 @@ half3 ApplyUnderwaterEffect } #endif // _SHADOWS_ON - half seaFloorDepth = CREST_OCEAN_DEPTH_BASELINE; + half seaFloorDepth = 0.0; #if _SUBSURFACESHALLOWCOLOUR_ON { // compute scatter colour from cam pos. two scenarios this can be called: