Files
2026-03-05 00:14:42 +08:00

347 lines
11 KiB
HLSL

// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_UNDERWATER_EFFECT_SHARED_INCLUDED
#define CREST_UNDERWATER_EFFECT_SHARED_INCLUDED
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Settings.Crest.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Constants.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/InputsDriven.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Helpers.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Depth.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Helpers.hlsl"
#if d_Crest_Portal
#include "Packages/com.waveharmonic.crest.portals/Runtime/Shaders/Library/Portals.hlsl"
#endif
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Cascade.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Texture.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Lighting.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Shadows.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/VolumeLighting.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Caustics.hlsl"
// These are set via call to CopyPropertiesFromMaterial() and must have the same
// names as the surface material parameters.
CBUFFER_START(CrestPerMaterial)
//
// Surface Shared
//
#ifndef d_Crest_WaterSurface
half4 _Crest_Absorption;
half4 _Crest_Scattering;
half _Crest_Anisotropy;
half _Crest_DirectTerm;
half _Crest_AmbientTerm;
half _Crest_ShadowsAffectsAmbientFactor;
bool _Crest_CausticsEnabled;
float _Crest_CausticsTextureScale;
float _Crest_CausticsScrollSpeed;
float _Crest_CausticsTextureAverage;
float _Crest_CausticsStrength;
float _Crest_CausticsFocalDepth;
float _Crest_CausticsDepthOfField;
float _Crest_CausticsDistortionStrength;
float _Crest_CausticsDistortionScale;
half _Crest_CausticsMotionBlur;
float4 _Crest_CausticsTexture_TexelSize;
float4 _Crest_CausticsDistortionTexture_TexelSize;
#endif // !d_Crest_WaterSurface
//
// Volume Only
//
// Out-scattering. Driven by the Water Renderer and Underwater Environmental Lighting.
float _Crest_VolumeExtinctionLength;
float _Crest_UnderwaterEnvironmentalLightingWeight;
// Also applied to transparent objects.
half _Crest_ExtinctionMultiplier;
half _Crest_SunBoost;
float _Crest_OutScatteringFactor;
float _Crest_OutScatteringExtinctionFactor;
half3 _Crest_AmbientLighting;
int _Crest_DataSliceOffset;
half _Crest_DitheringIntensity;
CBUFFER_END
TEXTURE2D_X(_Crest_WaterMaskDepthTexture);
#ifndef d_Crest_WaterSurface
TEXTURE2D_X(_Crest_WaterMaskTexture);
TEXTURE2D(_Crest_CausticsTexture);
SAMPLER(sampler_Crest_CausticsTexture);
TEXTURE2D(_Crest_CausticsDistortionTexture);
SAMPLER(sampler_Crest_CausticsDistortionTexture);
// NOTE: Cannot put this in namespace due to compiler bug. Fixed when using DXC.
static const m_Crest::TiledTexture _Crest_CausticsTiledTexture =
m_Crest::TiledTexture::Make(_Crest_CausticsTexture, sampler_Crest_CausticsTexture, _Crest_CausticsTexture_TexelSize, _Crest_CausticsTextureScale, _Crest_CausticsScrollSpeed);
static const m_Crest::TiledTexture _Crest_CausticsDistortionTiledTexture =
m_Crest::TiledTexture::Make(_Crest_CausticsDistortionTexture, sampler_Crest_CausticsDistortionTexture, _Crest_CausticsDistortionTexture_TexelSize, _Crest_CausticsDistortionScale, 1.0);
#endif // !d_Crest_WaterSurface
m_CrestNameSpace
// Get the out-scattering term.
half3 EvaluateOutScattering
(
const half3 i_Extinction,
const float3 i_PositionWS,
const half3 i_ViewWS,
const half i_Multiplier,
const float i_RawDepth,
const float i_WaterLevel
)
{
float3 positionWS = i_PositionWS;
#if !CREST_REFLECTION
// Project point onto sphere at the extinction length.
const float3 toSphere = -i_ViewWS * _Crest_VolumeExtinctionLength * i_Multiplier * _Crest_OutScatteringExtinctionFactor;
const float3 toScene = i_PositionWS - _WorldSpaceCameraPos.xyz;
positionWS = _WorldSpaceCameraPos.xyz + toSphere;
// Get closest position.
positionWS = dot(toScene, toScene) < dot(toSphere, toSphere) ? i_PositionWS : positionWS;
#endif
// Account for average extinction of light as it travels down through volume. Assume flat water as anything
// else would be expensive.
float waterDepth = max(0.0, (i_WaterLevel - positionWS.y));
#if CREST_REFLECTION
waterDepth *= 2.0;
if (i_RawDepth == 0.0) waterDepth = _Crest_VolumeExtinctionLength * i_Multiplier;
#else
// Full strength seems too extreme. Third strength seems reasonable.
waterDepth *= _Crest_OutScatteringFactor;
#endif
const float3 outScatteringTerm = exp(-i_Extinction * waterDepth);
// Transition between the Underwater Environmental Lighting (if present) and this. This will give us the
// benefit of both approaches.
return lerp(outScatteringTerm, 1.0, _Crest_UnderwaterEnvironmentalLightingWeight);
}
void GetWaterSurfaceAndUnderwaterData
(
const float4 positionCS,
const int2 positionSS,
const float rawMaskDepth,
const float mask,
inout float rawDepth,
inout bool isWaterSurface,
inout bool isUnderwater,
inout bool hasCaustics,
inout bool io_OutScatterScene,
inout bool io_ApplyLighting,
inout float sceneZ
)
{
const float rawSceneDepth = rawDepth;
hasCaustics = rawDepth != 0.0;
isWaterSurface = false;
isUnderwater = mask <= CREST_MASK_BELOW_SURFACE;
io_OutScatterScene = true;
io_ApplyLighting = true;
#if defined(d_Crest_PortalWithBackFace) || defined(d_Crest_FogBefore)
// Has back-face or is back-face.
Portal::EvaluateVolume(positionCS, positionSS, rawMaskDepth, rawSceneDepth, rawDepth, hasCaustics, isUnderwater, io_OutScatterScene, io_ApplyLighting);
#endif
// Merge water depth with scene depth.
if (rawDepth < rawMaskDepth)
{
isWaterSurface = true;
hasCaustics = false;
rawDepth = rawMaskDepth;
}
sceneZ = Utility::CrestLinearEyeDepth(rawDepth);
}
void ApplyWaterVolumeToUnderwaterFog(float4 positionCS, inout float fogDistance)
{
// TODO: could we use min here with near plane? less optimized
#if d_Crest_FogAfter
fogDistance -= Utility::CrestLinearEyeDepth(positionCS.z);
#else
// Subtract near plane.
fogDistance -= _ProjectionParams.y;
#endif
}
half3 ApplyUnderwaterEffect
(
half3 sceneColour,
const float rawDepth,
const float sceneZ,
const float fogDistance,
const half3 view,
const uint2 i_positionSS,
const float3 i_positionWS,
const bool hasCaustics,
const bool i_OutScatterScene,
const bool i_ApplyLighting,
const half i_multiplier
)
{
const bool isUnderwater = true;
float3 lightDirection; float3 lightColor;
PrimaryLight(i_positionWS, lightColor, lightDirection);
// Uniform effect calculated from camera position.
half3 volumeLight = 0.0;
half3 volumeOpacity = 1.0;
{
half3 absorption = _Crest_Absorption.xyz;
half3 scattering = _Crest_Scattering.xyz;
// We sample shadows at the camera position. Pass a user defined slice offset for smoothing out detail.
// Offset slice so that we dont get high freq detail. But never use last lod as this has crossfading.
int sliceIndex = clamp(_Crest_DataSliceOffset, 0, g_Crest_LodCount - 2);
if (g_Crest_SampleAbsorptionSimulation) absorption = Cascade::MakeAbsorption(sliceIndex).Sample(_WorldSpaceCameraPos.xz).xyz;
if (g_Crest_SampleScatteringSimulation) scattering = Cascade::MakeScattering(sliceIndex).Sample(_WorldSpaceCameraPos.xz).xyz;
absorption *= _Crest_ExtinctionMultiplier;
scattering *= _Crest_ExtinctionMultiplier;
const float waterLevel = g_Crest_WaterCenter.y + Cascade::MakeAnimatedWaves(sliceIndex).Sample(_WorldSpaceCameraPos.xz).w;
half shadow = 1.0;
{
// #if CREST_SHADOWS_ON
// Camera should be at center of LOD system so no need for blending (alpha, weights, etc). This might not be
// the case if there is large horizontal displacement, but the _Crest_DataSliceOffset should help by setting a
// large enough slice as minimum.
half2 shadowSoftHard = Cascade::MakeShadow(sliceIndex).SampleShadow(_WorldSpaceCameraPos.xz);
// Soft in red, hard in green. But hard not computed in HDRP.
shadow = 1.0 - shadowSoftHard.x;
// #endif
}
half3 ambientLighting = AmbientLight(_Crest_AmbientLighting);
const half3 extinction = VolumeExtinction(absorption, scattering);
// Out-Scattering Term.
{
const half3 outScatteringTerm = EvaluateOutScattering
(
extinction,
i_positionWS,
view,
i_multiplier,
rawDepth,
waterLevel
);
// Darken scene and light.
sceneColour *= i_OutScatterScene ? outScatteringTerm : 1.0;
#if !CREST_REFLECTION
lightColor *= outScatteringTerm;
ambientLighting *= outScatteringTerm;
#endif
}
volumeOpacity = VolumeOpacity(extinction, fogDistance);
volumeLight = VolumeLighting
(
extinction,
scattering,
_Crest_Anisotropy,
shadow,
view,
ambientLighting,
lightDirection,
lightColor,
half3(0.0, 0.0, 0.0),
_Crest_AmbientTerm,
_Crest_DirectTerm,
_Crest_SunBoost,
_Crest_ShadowsAffectsAmbientFactor
);
}
#ifndef k_DisableCaustics
if (_Crest_CausticsEnabled && hasCaustics)
{
half lightOcclusion = PrimaryLightShadows(i_positionWS, i_positionSS);
half blur = 0.0;
const uint slice0 = PositionToSliceIndex(i_positionWS.xz, 0, g_Crest_WaterScale);
#ifdef CREST_FLOW_ON
half2 flowData = Cascade::MakeFlow(slice0).SampleFlow(i_positionWS.xz);
const Flow flow = Flow::Make(flowData, g_Crest_Time);
blur = _Crest_CausticsMotionBlur;
#endif
const float4 displacement = Cascade::MakeAnimatedWaves(slice0).Sample(i_positionWS.xz);
const float surfaceHeight = displacement.y + g_Crest_WaterCenter.y + displacement.w;
sceneColour *= Caustics
(
#ifdef CREST_FLOW_ON
flow,
#endif
i_positionWS,
surfaceHeight,
lightColor,
lightDirection,
lightOcclusion,
sceneZ,
_Crest_CausticsTiledTexture,
_Crest_CausticsTextureAverage,
_Crest_CausticsStrength,
_Crest_CausticsFocalDepth,
_Crest_CausticsDepthOfField,
_Crest_CausticsDistortionTiledTexture,
_Crest_CausticsDistortionStrength,
blur,
isUnderwater
);
}
#endif
#if CREST_HDRP
volumeLight *= GetCurrentExposureMultiplier();
#endif
#ifndef k_DisableDithering
#if d_Dithering
// Increasing intensity can be required for HDRP.
volumeLight += Utility::ScreenSpaceDither(i_positionSS) * _Crest_DitheringIntensity;
#endif
#endif
if (i_ApplyLighting)
{
sceneColour = lerp(sceneColour, volumeLight, volumeOpacity);
}
return sceneColour;
}
m_CrestNameSpaceEnd
#endif // CREST_UNDERWATER_EFFECT_SHARED_INCLUDED