347 lines
11 KiB
HLSL
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
|