修改水

This commit is contained in:
2026-01-01 22:00:33 +08:00
parent 040a222bd6
commit 9ceffccd39
1800 changed files with 103929 additions and 139495 deletions

View File

@@ -0,0 +1,42 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_ALPHA_H
#define CREST_WATER_ALPHA_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/InputsDriven.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Helpers.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Cascade.hlsl"
m_CrestNameSpace
float ClipSurface(const float2 i_PositionWSXZ)
{
// Do not include transition slice to avoid blending as we do a black border instead.
uint slice0; uint slice1; float alpha;
PosToSliceIndices(i_PositionWSXZ, 0.0, g_Crest_LodCount - 1.0, g_Crest_WaterScale, slice0, slice1, alpha);
const Cascade cascade0 = Cascade::Make(slice0);
const Cascade cascade1 = Cascade::Make(slice1);
const float weight0 = (1.0 - alpha) * cascade0._Weight;
const float weight1 = (1.0 - weight0) * cascade1._Weight;
float value = 0.0;
if (weight0 > m_CrestSampleLodThreshold)
{
Cascade::MakeClip(slice0).SampleClip(i_PositionWSXZ, weight0, value);
}
if (weight1 > m_CrestSampleLodThreshold)
{
Cascade::MakeClip(slice1).SampleClip(i_PositionWSXZ, weight1, value);
}
return lerp(g_Crest_ClipByDefault, value, weight0 + weight1);
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: c1e69a8c6920249c1ad0b50907793046
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_CAUSTICS_H
#define CREST_WATER_CAUSTICS_H
#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/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Texture.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Flow.hlsl"
#if (CREST_SHIFTING_ORIGIN != 0)
#include "Packages/com.waveharmonic.crest.shifting-origin/Runtime/Shaders/ShiftingOrigin.hlsl"
#endif
m_CrestNameSpace
half3 Caustics
(
const float3 i_ScenePositionWS,
const float i_SurfacePositionY,
const half3 i_LightIntensity,
const half3 i_LightDirection,
const half i_LightShadow,
const float i_SceneDepth,
const TiledTexture i_CausticsTexture,
const half i_TextureAverage,
const half i_Strength,
const half i_FocalDepth,
const half i_DepthOfField,
const TiledTexture i_DistortionTexture,
const half i_DistortionStrength,
const half i_Blur,
const bool i_Underwater
)
{
half sceneDepth = i_SurfacePositionY - i_ScenePositionWS.y;
// Compute mip index manually, with bias based on sea floor depth. We compute it manually because if it is computed automatically it produces ugly patches
// where samples are stretched/dilated. The bias is to give a focusing effect to caustics - they are sharpest at a particular depth. This doesn't work amazingly
// well and could be replaced.
float mipLod = log2(i_SceneDepth) + abs(sceneDepth - i_FocalDepth) / i_DepthOfField + i_Blur;
// Project along light dir, but multiply by a fudge factor reduce the angle bit - compensates for fact that in real life
// caustics come from many directions and don't exhibit such a strong directonality
// Removing the fudge factor (4.0) will cause the caustics to move around more with the waves. But this will also
// result in stretched/dilated caustics in certain areas. This is especially noticeable on angled surfaces.
float2 lightProjection = i_LightDirection.xz * sceneDepth / (4.0 * i_LightDirection.y);
float3 cuv1 = 0.0; float3 cuv2 = 0.0;
{
float2 surfacePosXZ = i_ScenePositionWS.xz;
float surfacePosScale = 1.37;
#if (CREST_SHIFTING_ORIGIN != 0)
// Apply tiled floating origin offset. Always needed.
surfacePosXZ -= ShiftingOriginOffset(i_CausticsTexture);
// Scale was causing popping.
surfacePosScale = 1.0;
#endif
surfacePosXZ += lightProjection;
float scale = i_CausticsTexture._scale / 10.0;
const float speed = g_Crest_Time * i_CausticsTexture._speed;
cuv1 = float3
(
surfacePosXZ / scale + float2(0.044 * speed + 17.16, -0.169 * speed),
mipLod
);
cuv2 = float3
(
surfacePosScale * surfacePosXZ / scale + float2(0.248 * speed, 0.117 * speed),
mipLod
);
}
if (i_Underwater)
{
float2 surfacePosXZ = i_ScenePositionWS.xz;
#if (CREST_SHIFTING_ORIGIN != 0)
// Apply tiled floating origin offset. Always needed.
surfacePosXZ -= ShiftingOriginOffset(i_DistortionTexture);
#endif
surfacePosXZ += lightProjection;
float scale = i_DistortionTexture._scale / 10.0;
half2 causticN = i_DistortionStrength * UnpackNormal(i_DistortionTexture.Sample(surfacePosXZ / scale)).xy;
cuv1.xy += 1.30 * causticN;
cuv2.xy += 1.77 * causticN;
}
half causticsStrength = i_Strength;
// Occlusion.
{
causticsStrength *= i_LightShadow;
}
return 1.0 + causticsStrength *
(
0.5 * i_CausticsTexture.SampleLevel(cuv1.xy, cuv1.z).xyz +
0.5 * i_CausticsTexture.SampleLevel(cuv2.xy, cuv2.z).xyz
- i_TextureAverage
);
}
half3 Caustics
(
const Flow i_Flow,
const float3 i_ScenePositionWS,
const float i_SurfacePositionY,
const half3 i_LightIntensity,
const half3 i_LightDirection,
const half i_LightShadow,
const float i_SceneDepth,
const TiledTexture i_CausticsTexture,
const half i_TextureAverage,
const half i_Strength,
const half i_FocalDepth,
const half i_DepthOfField,
const TiledTexture i_DistortionTexture,
const half i_DistortionStrength,
const half i_Blur,
const bool i_Underwater
)
{
half blur = 0.0;
half3 flow = half3(i_Flow._Flow.x, 0, i_Flow._Flow.y);
if (i_Blur > 0.0)
{
// Calculate blur in flowing water as will likely be more disturbed, resulting in
// caustics being less defined.
blur = length(i_Flow._Flow) * i_Blur;
}
return Caustics
(
i_ScenePositionWS - flow * i_Flow._Offset0,
i_SurfacePositionY,
i_LightIntensity,
i_LightDirection,
i_LightShadow,
i_SceneDepth,
i_CausticsTexture,
i_TextureAverage,
i_Strength,
i_FocalDepth,
i_DepthOfField,
i_DistortionTexture,
i_DistortionStrength,
blur,
i_Underwater
) * i_Flow._Weight0 + Caustics
(
i_ScenePositionWS - flow * i_Flow._Offset1,
i_SurfacePositionY,
i_LightIntensity,
i_LightDirection,
i_LightShadow,
i_SceneDepth,
i_CausticsTexture,
i_TextureAverage,
i_Strength,
i_FocalDepth,
i_DepthOfField,
i_DistortionTexture,
i_DistortionStrength,
blur,
i_Underwater
) * i_Flow._Weight1;
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: b2ee0978718e447e3ab93a881f3d5dcf
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,224 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_FOAM_H
#define CREST_WATER_FOAM_H
#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/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Texture.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Flow.hlsl"
#if (CREST_SHIFTING_ORIGIN != 0)
#include "Packages/com.waveharmonic.crest.shifting-origin/Runtime/Shaders/ShiftingOrigin.hlsl"
#endif
m_CrestNameSpace
half WhiteFoamTexture
(
const TiledTexture i_Texture,
const half i_Foam,
const half i_Feather,
const float2 i_WorldXZ0,
const float2 i_WorldXZ1,
const float2 i_TexelOffset,
const half i_LodAlpha,
const Cascade i_CascadeData0
)
{
const float2 uvOffset = i_TexelOffset + g_Crest_Time * i_Texture._speed / 32.0;
// Scale with lods to get multiscale detail. 10 is magic number that gets the
// material 'scale' slider into an intuitive range.
const float scale = i_Texture._scale * i_CascadeData0._Scale / 10.0;
half ft = lerp
(
i_Texture.Sample((i_WorldXZ0 + uvOffset) / scale).r,
i_Texture.Sample((i_WorldXZ1 + uvOffset) / (2.0 * scale)).r,
i_LodAlpha
);
// Black point fade.
half result = saturate(1.0 - i_Foam);
return smoothstep(result, result + i_Feather, ft);
}
half MultiScaleFoamAlbedo
(
const TiledTexture i_Texture,
const half i_Feather,
const half i_FoamData,
const Cascade i_CascadeData0,
const Cascade i_CascadeData1,
const half i_LodAlpha,
const float2 i_UndisplacedXZ
)
{
float2 worldXZ0 = i_UndisplacedXZ;
float2 worldXZ1 = i_UndisplacedXZ;
#if (CREST_SHIFTING_ORIGIN != 0)
// Apply tiled floating origin offset. Only needed if:
// - _FoamScale is a non integer value
// - _FoamScale is over 48
worldXZ0 -= ShiftingOriginOffset(i_Texture, i_CascadeData0);
worldXZ1 -= ShiftingOriginOffset(i_Texture, i_CascadeData1);
#endif // CREST_SHIFTING_ORIGIN
return WhiteFoamTexture(i_Texture, i_FoamData, i_Feather, worldXZ0, worldXZ1, (float2)0.0, i_LodAlpha, i_CascadeData0);
}
half2 MultiScaleFoamNormal
(
const TiledTexture i_Texture,
const half i_Feather,
const half i_NormalStrength,
const half i_FoamData,
const half i_FoamAlbedo,
const Cascade i_CascadeData0,
const Cascade i_CascadeData1,
const half i_LodAlpha,
const float2 i_UndisplacedXZ,
const float i_PixelZ
)
{
float2 worldXZ0 = i_UndisplacedXZ;
float2 worldXZ1 = i_UndisplacedXZ;
#if (CREST_SHIFTING_ORIGIN != 0)
// Apply tiled floating origin offset. Only needed if:
// - _FoamScale is a non integer value
// - _FoamScale is over 48
worldXZ0 -= ShiftingOriginOffset(i_Texture, i_CascadeData0);
worldXZ1 -= ShiftingOriginOffset(i_Texture, i_CascadeData1);
#endif // CREST_SHIFTING_ORIGIN
// 0.25 is magic number found through tweaking.
const float2 dd = float2(0.25 * i_PixelZ * i_Texture._texel, 0.0);
const half whiteFoam_x = WhiteFoamTexture(i_Texture, i_FoamData, i_Feather, worldXZ0, worldXZ1, dd.xy, i_LodAlpha, i_CascadeData0);
const half whiteFoam_z = WhiteFoamTexture(i_Texture, i_FoamData, i_Feather, worldXZ0, worldXZ1, dd.yx, i_LodAlpha, i_CascadeData0);
// Compute a foam normal - manually push in derivatives. If I used blend
// smooths all the normals towards straight up when there is no foam.
// Gets material slider into friendly range.
const float magicStrengthFactor = 0.01;
return magicStrengthFactor * i_NormalStrength * half2(whiteFoam_x - i_FoamAlbedo, whiteFoam_z - i_FoamAlbedo) / dd.x;
}
half MultiScaleFoamAlbedo
(
const Flow i_Flow,
const TiledTexture i_Texture,
const half i_Feather,
const half i_Foam,
const Cascade i_CascadeData0,
const Cascade i_CascadeData1,
const half i_LodAlpha,
const float2 i_UndisplacedXZ
)
{
return MultiScaleFoamAlbedo
(
i_Texture,
i_Feather,
i_Foam,
i_CascadeData0,
i_CascadeData1,
i_LodAlpha,
i_UndisplacedXZ - i_Flow._Flow * i_Flow._Offset0
) * i_Flow._Weight0 + MultiScaleFoamAlbedo
(
i_Texture,
i_Feather,
i_Foam,
i_CascadeData0,
i_CascadeData1,
i_LodAlpha,
i_UndisplacedXZ - i_Flow._Flow * i_Flow._Offset1
) * i_Flow._Weight1;
}
half2 MultiScaleFoamNormal
(
const Flow i_Flow,
const TiledTexture i_Texture,
const half i_Feather,
const half i_NormalStrength,
const half i_FoamData,
const half i_FoamAlbedo,
const Cascade i_CascadeData0,
const Cascade i_CascadeData1,
const half i_LodAlpha,
const float2 i_UndisplacedXZ,
const float i_PixelZ
)
{
return MultiScaleFoamNormal
(
i_Texture,
i_Feather,
i_NormalStrength,
i_FoamData,
i_FoamAlbedo,
i_CascadeData0,
i_CascadeData1,
i_LodAlpha,
i_UndisplacedXZ - i_Flow._Flow * i_Flow._Offset0,
i_PixelZ
) * i_Flow._Weight0 + MultiScaleFoamNormal
(
i_Texture,
i_Feather,
i_NormalStrength,
i_FoamData,
i_FoamAlbedo,
i_CascadeData0,
i_CascadeData1,
i_LodAlpha,
i_UndisplacedXZ - i_Flow._Flow * i_Flow._Offset1,
i_PixelZ
) * i_Flow._Weight1;
}
void ApplyFoamToSurface
(
half i_Foam,
const half2 i_Normal,
const half3 i_Albedo,
const half i_Occlusion,
const half i_Smoothness,
const half i_Specular,
const bool i_Underwater,
inout half3 io_Albedo,
inout half3 io_NormalWS,
inout half3 io_Emission,
inout half io_Occlusion,
inout float io_Smoothness,
inout half3 io_Specular
)
{
// Apply foam to surface.
io_Albedo = lerp(io_Albedo, i_Albedo, i_Foam);
io_Emission *= 1.0 - i_Foam;
io_Occlusion = lerp(io_Occlusion, i_Occlusion, i_Foam);
io_Smoothness = lerp(io_Smoothness, i_Smoothness, i_Foam);
io_NormalWS.xz -= i_Normal;
io_NormalWS = normalize(io_NormalWS);
// Foam Transmission
if (i_Underwater)
{
// Foam will be black when not facing the sun. This is a hacky way to have foam lit
// as if it had transmission.
// There is still ugliness around the edges. There will either be black or
// incorrect reflections depending on the magic value.
io_NormalWS.y *= i_Foam > 0.15 ? -1.0 : 1.0;
io_Specular = lerp(io_Specular, i_Specular, i_Foam);
}
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 58f878eb932e14fb0b1d13173caa2857
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,644 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Guard against missing uniforms.
#ifdef SHADERPASS
#define m_Properties \
const float2 i_UndisplacedXZ, \
const float i_LodAlpha, \
const half i_WaterLevelOffset, \
const float2 i_WaterLevelDerivatives, \
const half2 i_Flow, \
const half3 i_ViewDirectionWS, \
const bool i_Facing, \
const half3 i_SceneColor, \
const float i_SceneDepthRaw, \
const float4 i_ScreenPosition, \
const float4 i_ScreenPositionRaw, \
const float3 i_PositionWS, \
const float3 i_PositionVS, \
const float2 i_StaticLightMapUV, \
out half3 o_Albedo, \
out half3 o_NormalWS, \
out half3 o_Specular, \
out half3 o_Emission, \
out half o_Smoothness, \
out half o_Occlusion, \
out half o_Alpha
// Guard against Shader Graph preview.
#ifndef SHADERGRAPH_PREVIEW
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Shim.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/InputsDriven.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Cascade.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Geometry.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Depth.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Texture.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Flow.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Lighting.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Normal.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Reflection.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Refraction.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Caustics.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/VolumeLighting.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Fresnel.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Foam.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Alpha.hlsl"
#if (CREST_PORTALS != 0)
#include "Packages/com.waveharmonic.crest.portals/Runtime/Shaders/Library/Portals.hlsl"
#endif
#if (CREST_SHADOWS_BUILT_IN_RENDER_PIPELINE != 0)
#if CREST_BIRP
#define SHADOWS_SPLIT_SPHERES 1
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Legacy/Core.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Legacy/Shadows.hlsl"
#endif
#endif
bool _Crest_DrawBoundaryXZ;
float4 _Crest_BoundaryXZ;
m_CrestNameSpace
static const TiledTexture _Crest_NormalMapTiledTexture =
TiledTexture::Make(_Crest_NormalMapTexture, sampler_Crest_NormalMapTexture, _Crest_NormalMapTexture_TexelSize, _Crest_NormalMapScale, _Crest_NormalMapScrollSpeed);
static const TiledTexture _Crest_FoamTiledTexture =
TiledTexture::Make(_Crest_FoamTexture, sampler_Crest_FoamTexture, _Crest_FoamTexture_TexelSize, _Crest_FoamScale, _Crest_FoamScrollSpeed);
static const TiledTexture _Crest_CausticsTiledTexture =
TiledTexture::Make(_Crest_CausticsTexture, sampler_Crest_CausticsTexture, _Crest_CausticsTexture_TexelSize, _Crest_CausticsTextureScale, _Crest_CausticsScrollSpeed);
static const TiledTexture _Crest_CausticsDistortionTiledTexture =
TiledTexture::Make(_Crest_CausticsDistortionTexture, sampler_Crest_CausticsDistortionTexture, _Crest_CausticsDistortionTexture_TexelSize, _Crest_CausticsDistortionScale, 1.0);
void Fragment(m_Properties)
{
o_Albedo = 0.0;
o_NormalWS = half3(0.0, 1.0, 0.0);
o_Specular = 0.0;
o_Emission = 0.0;
o_Smoothness = 0.7;
o_Occlusion = 1.0;
o_Alpha = 1.0;
// Editor only. There is no defined editor symbol.
if (_Crest_DrawBoundaryXZ)
{
const float2 p = abs(i_PositionWS.xz - _Crest_BoundaryXZ.xy);
const float2 s = _Crest_BoundaryXZ.zw * 0.5;
if ((p.x > s.x && p.x < s.x + 1.0 && p.y < s.y + 1.0) || (p.y > s.y && p.y < s.y + 1.0 && p.x < s.x + 1.0))
{
o_Emission = half3(1.0, 0.0, 1.0);
#if CREST_HDRP
o_Emission /= GetCurrentExposureMultiplier();
#endif
}
}
const bool underwater = IsUnderwater(i_Facing, g_Crest_ForceUnderwater);
// TODO: Should we use PosToSIs or check for overflow?
float slice0 = _Crest_LodIndex;
float slice1 = _Crest_LodIndex + 1;
#ifdef CREST_FLOW_ON
const Flow flow = Flow::Make(i_Flow, g_Crest_Time);
#endif
const Cascade cascade0 = Cascade::Make(slice0);
const Cascade cascade1 = Cascade::Make(slice1);
float sceneRawZ = i_SceneDepthRaw;
#if (CREST_PORTALS != 0)
#ifndef CREST_SHADOWPASS
#if _ALPHATEST_ON
if (m_CrestPortal)
{
const float pixelRawZ = i_ScreenPositionRaw.z / i_ScreenPositionRaw.w;
if (OutsideOfPortal(i_ScreenPosition.xy, pixelRawZ, sceneRawZ))
{
o_Alpha = 0.0;
return;
}
}
#endif
#endif
#endif
float sceneZ = Utility::CrestLinearEyeDepth(sceneRawZ);
float pixelZ = -i_PositionVS.z;
const bool isLastLod = _Crest_LodIndex == (uint)g_Crest_LodCount - 1;
const float weight0 = (1.0 - i_LodAlpha) * cascade0._Weight;
const float weight1 = (1.0 - weight0) * cascade1._Weight;
// Data that fades towards the edge.
half foam = 0.0; half _determinant = 0.0; half4 albedo = 0.0; half2 shadow = 0.0;
if (weight0 > m_CrestSampleLodThreshold)
{
Cascade::MakeAnimatedWaves(slice0).SampleNormals(i_UndisplacedXZ, weight0, o_NormalWS.xz, _determinant);
if (_Crest_FoamEnabled)
{
Cascade::MakeFoam(slice0).SampleFoam(i_UndisplacedXZ, weight0, foam);
}
if (_Crest_AlbedoEnabled)
{
Cascade::MakeAlbedo(slice0).SampleAlbedo(i_UndisplacedXZ, weight0, albedo);
}
if (_Crest_ShadowsEnabled)
{
Cascade::MakeShadow(slice0).SampleShadow(i_PositionWS.xz, weight0, shadow);
}
}
if (weight1 > m_CrestSampleLodThreshold)
{
Cascade::MakeAnimatedWaves(slice1).SampleNormals(i_UndisplacedXZ, weight1, o_NormalWS.xz, _determinant);
if (_Crest_FoamEnabled)
{
Cascade::MakeFoam(slice1).SampleFoam(i_UndisplacedXZ, weight1, foam);
}
if (_Crest_AlbedoEnabled)
{
Cascade::MakeAlbedo(slice1).SampleAlbedo(i_UndisplacedXZ, weight1, albedo);
}
if (_Crest_ShadowsEnabled)
{
Cascade::MakeShadow(slice1).SampleShadow(i_PositionWS.xz, weight1, shadow);
}
}
// Invert so shadows are black as we normally multiply this by lighting.
shadow = 1.0 - shadow;
// Data that displays to the edge.
// The default simulation value has been written to the border of the last slice.
half3 absorption = 0.0; half3 scattering = 0.0;
{
const float weight0 = (1.0 - (isLastLod ? 0.0 : i_LodAlpha)) * cascade0._Weight;
const float weight1 = (1.0 - weight0) * cascade1._Weight;
if (weight0 > m_CrestSampleLodThreshold)
{
if (g_Crest_SampleScatteringSimulation)
{
Cascade::MakeScattering(slice0).SampleScattering(i_UndisplacedXZ, weight0, scattering);
}
if (g_Crest_SampleAbsorptionSimulation)
{
Cascade::MakeAbsorption(slice0).SampleAbsorption(i_UndisplacedXZ, weight0, absorption);
}
}
if (weight1 > m_CrestSampleLodThreshold)
{
if (g_Crest_SampleScatteringSimulation)
{
Cascade::MakeScattering(slice1).SampleScattering(i_UndisplacedXZ, weight1, scattering);
}
if (g_Crest_SampleAbsorptionSimulation)
{
Cascade::MakeAbsorption(slice1).SampleAbsorption(i_UndisplacedXZ, weight1, absorption);
}
}
}
if (!g_Crest_SampleScatteringSimulation)
{
scattering = _Crest_Scattering.xyz;
}
if (!g_Crest_SampleAbsorptionSimulation)
{
absorption = _Crest_Absorption.xyz;
}
// Determinant needs to be one when no waves.
if (isLastLod)
{
_determinant += 1.0 - weight0;
}
// Normal.
{
WaterNormal
(
i_WaterLevelDerivatives,
i_ViewDirectionWS,
_Crest_MinimumReflectionDirectionY,
underwater,
o_NormalWS
);
if (_Crest_NormalMapEnabled)
{
o_NormalWS.xz += SampleNormalMaps
(
#ifdef CREST_FLOW_ON
flow,
#endif
_Crest_NormalMapTiledTexture,
_Crest_NormalMapStrength,
i_UndisplacedXZ,
i_LodAlpha,
cascade0
);
}
o_NormalWS = normalize(o_NormalWS);
o_NormalWS.xz *= _Crest_NormalsStrengthOverall;
o_NormalWS.y = lerp(1.0, o_NormalWS.y, _Crest_NormalsStrengthOverall);
if (underwater)
{
// Flip when underwater.
o_NormalWS.xyz *= -1.0;
}
}
// Default for opaque render type.
float sceneDistance = 1000.0;
float3 scenePositionWS = 0.0;
half3 ambientLight = 0.0;
AmbientLight
(
ambientLight
);
float3 lightIntensity = 0.0;
half3 lightDirection = 0.0;
PrimaryLight
(
i_PositionWS,
lightIntensity,
lightDirection
);
half3 additionalLight = AdditionalLighting(i_PositionWS, i_ScreenPositionRaw, i_StaticLightMapUV);
#if d_Crest_ReceiveShadowsTransparent
// Sample shadow maps.
float4 shadowCoord = GET_SHADOW_COORDINATES(float4(i_PositionWS, 1.0));
half shadows = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, shadowCoord);
shadows = lerp(_LightShadowData.r, 1.0, shadows);
shadows = min(shadow.y, shadows);
#endif
#if d_Transparent
bool caustics;
RefractedScene
(
_Crest_RefractionStrength,
o_NormalWS,
i_ScreenPosition.xy,
pixelZ,
i_SceneColor,
sceneZ,
sceneRawZ,
underwater,
o_Emission,
sceneDistance,
scenePositionWS,
caustics
);
#endif
float refractedSeaLevel = 0;
float3 refractedSurfacePosition = 0;
if (!underwater)
{
// Sample larger slice to avoid the first slice.
float4 displacement = Cascade::MakeAnimatedWaves(slice1).Sample(scenePositionWS.xz);
refractedSeaLevel = g_Crest_WaterCenter.y + displacement.w;
refractedSurfacePosition = displacement.xyz;
refractedSurfacePosition.y += refractedSeaLevel;
}
// Out-scattering.
if (!underwater)
{
// Account for average extinction of light as it travels down through volume. Assume flat water as anything else would be expensive.
half3 extinction = absorption.xyz + scattering.xyz;
o_Emission *= exp(-extinction * max(0.0, refractedSeaLevel - scenePositionWS.y));
}
#if d_Transparent
// Caustics
if (_Crest_CausticsEnabled && !underwater && caustics)
{
float3 position = scenePositionWS;
#if CREST_BIRP
position = float3(i_ScreenPosition.xy * _ScreenSize.xy, 0);
#endif
half lightOcclusion = PrimaryLightShadows(position);
half blur = 0.0;
#ifdef CREST_FLOW_ON
blur = _Crest_CausticsMotionBlur;
#endif
o_Emission *= Caustics
(
#ifdef CREST_FLOW_ON
flow,
#endif
scenePositionWS,
refractedSurfacePosition.y,
lightIntensity,
lightDirection,
lightOcclusion,
sceneDistance,
_Crest_CausticsTiledTexture,
_Crest_CausticsTextureAverage,
_Crest_CausticsStrength,
_Crest_CausticsFocalDepth,
_Crest_CausticsDepthOfField,
_Crest_CausticsDistortionTiledTexture,
_Crest_CausticsDistortionStrength,
blur,
underwater
);
}
#endif
half3 sss = 0.0;
if (_Crest_SSSEnabled)
{
sss = PinchSSS
(
_determinant,
_Crest_SSSPinchMinimum,
_Crest_SSSPinchMaximum,
_Crest_SSSPinchFalloff,
_Crest_SSSIntensity,
lightDirection,
_Crest_SSSDirectionalFalloff,
i_ViewDirectionWS
);
}
// Volume Lighting
half3 volumeLight = 0.0;
half3 volumeOpacity = 0.0;
VolumeLighting
(
absorption,
scattering,
_Crest_Anisotropy,
shadow.x,
i_ViewDirectionWS,
ambientLight,
lightDirection,
lightIntensity,
additionalLight,
_Crest_AmbientTerm,
_Crest_DirectTerm,
sceneDistance,
sss,
_Crest_ShadowsAffectsAmbientFactor,
volumeLight,
volumeOpacity
);
// Fresnel
float reflected = 0.0;
float transmitted = 0.0;
{
ApplyFresnel
(
i_ViewDirectionWS,
o_NormalWS,
underwater,
1.0, // air
_Crest_RefractiveIndexOfWater,
_Crest_TotalInternalReflectionIntensity,
transmitted,
reflected
);
if (underwater)
{
o_Emission *= transmitted;
o_Emission += volumeLight * reflected;
}
else
{
o_Emission *= 1.0 - volumeOpacity;
o_Emission += volumeLight * volumeOpacity;
o_Emission *= transmitted;
}
}
// Specular
{
o_Specular = _Crest_Specular * reflected * shadow.y;
}
// Smoothness
{
// Vary smoothness by distance.
o_Smoothness = lerp(_Crest_Smoothness, _Crest_SmoothnessFar, pow(saturate(pixelZ / _Crest_SmoothnessFarDistance), _Crest_SmoothnessFalloff));
}
// Occlusion
{
o_Occlusion = underwater ? _Crest_OcclusionUnderwater : _Crest_Occlusion;
}
// Planar Reflections
if (_Crest_PlanarReflectionsEnabled)
{
half4 reflection = PlanarReflection
(
_Crest_ReflectionTexture,
sampler_Crest_ReflectionTexture,
_Crest_PlanarReflectionsIntensity,
o_Smoothness,
_Crest_PlanarReflectionsRoughness,
o_NormalWS,
_Crest_PlanarReflectionsDistortion,
i_ViewDirectionWS,
i_ScreenPosition.xy,
underwater
);
half alpha = reflection.a;
o_Emission = lerp(o_Emission, reflection.rgb, alpha * reflected * o_Occlusion);
// Override reflections with planar reflections.
// Results are darker than Unity's.
o_Occlusion *= 1.0 - alpha;
}
// Foam
if (_Crest_FoamEnabled)
{
half albedo = MultiScaleFoamAlbedo
(
#ifdef CREST_FLOW_ON
flow,
#endif
_Crest_FoamTiledTexture,
_Crest_FoamFeather,
foam,
cascade0,
cascade1,
i_LodAlpha,
i_UndisplacedXZ
);
half2 normal = MultiScaleFoamNormal
(
#ifdef CREST_FLOW_ON
flow,
#endif
_Crest_FoamTiledTexture,
_Crest_FoamFeather,
_Crest_FoamNormalStrength,
foam,
albedo,
cascade0,
cascade1,
i_LodAlpha,
i_UndisplacedXZ,
pixelZ
);
half3 intensity = _Crest_FoamIntensityAlbedo;
#if d_Crest_ReceiveShadowsTransparent
// @HACK: Scale intensity as BIRP does not support shadows for transparent objects.
intensity = max(_Crest_FoamIntensityAlbedo * saturate(ShadeSH9(float4(o_NormalWS, 1.0))), _Crest_FoamIntensityAlbedo * shadows);
#endif
ApplyFoamToSurface
(
albedo,
normal,
intensity,
_Crest_Occlusion,
_Crest_FoamSmoothness,
_Crest_Specular,
underwater,
o_Albedo,
o_NormalWS,
o_Emission,
o_Occlusion,
o_Smoothness,
o_Specular
);
// We will use this for shadow casting.
foam = albedo;
}
// Albedo
if (_Crest_AlbedoEnabled)
{
const float foamMask = _Crest_AlbedoIgnoreFoam ? (1.0 - saturate(foam)) : 1.0;
o_Albedo = lerp(o_Albedo, albedo.rgb, albedo.a * foamMask);
o_Emission *= 1.0 - albedo.a * foamMask;
}
// Alpha
{
#ifndef CREST_SHADOWPASS
#if d_Transparent
// Feather at intersection. Cannot be used for shadows since depth is not available.
o_Alpha = saturate((sceneZ - pixelZ) / 0.2);
#endif
#endif
// This keyword works for all RPs despite BIRP having prefixes in serialised data.
#if _ALPHATEST_ON
#if CREST_SHADOWPASS
o_Alpha = max(foam, albedo.a) - _Crest_ShadowCasterThreshold;
#endif
// Add 0.5 bias for LOD blending and texel resolution correction. This will help to
// tighten and smooth clipped edges.
o_Alpha -= ClipSurface(i_PositionWS.xz) > 0.5 ? 2.0 : 0.0;
#endif // _ALPHATEST_ON
// Specular in HDRP is still affected outside the 0-1 alpha range.
o_Alpha = min(o_Alpha, 1.0);
}
#if d_Crest_ReceiveShadowsTransparent
// @HACK: Dull highlights as BIRP does not support shadows for transparent objects.
o_Smoothness *= lerp(1, shadows, foam * 100);
// @FIXME: 0.2 to difference when high. Likely incorrect shadow sampling.
o_Specular = shadows < 0.2 ? 1.0 - max(shadows, 0.3) : o_Specular;
#endif
}
m_CrestNameSpaceEnd
#endif // SHADERGRAPH_PREVIEW
void Fragment_float(m_Properties)
{
#if SHADERGRAPH_PREVIEW
o_Albedo = 0.0;
o_NormalWS = half3(0.0, 1.0, 0.0);
o_Specular = 0.0;
o_Emission = 0.0;
o_Smoothness = 0.7;
o_Occlusion = 1.0;
o_Alpha = 1.0;
#else // SHADERGRAPH_PREVIEW
m_Crest::Fragment
(
i_UndisplacedXZ,
i_LodAlpha,
i_WaterLevelOffset,
i_WaterLevelDerivatives,
i_Flow,
i_ViewDirectionWS,
i_Facing,
i_SceneColor,
i_SceneDepthRaw,
i_ScreenPosition,
i_ScreenPositionRaw,
i_PositionWS,
i_PositionVS,
i_StaticLightMapUV,
o_Albedo,
o_NormalWS,
o_Specular,
o_Emission,
o_Smoothness,
o_Occlusion,
o_Alpha
);
#endif // SHADERGRAPH_PREVIEW
}
#undef m_Properties
#endif // SHADERPASS

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: fdafd40ae20374db88e293739fad1854
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,83 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_FRESNEL_H
#define CREST_WATER_FRESNEL_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
m_CrestNameSpace
float CalculateFresnelReflectionCoefficient(const float i_CosineTheta, const float i_RefractiveIndexOfAir, const float i_RefractiveIndexOfWater)
{
// Fresnel calculated using Schlick's approximation.
// See: http://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf
// Reflectance at facing angle.
float R_0 = (i_RefractiveIndexOfAir - i_RefractiveIndexOfWater) / (i_RefractiveIndexOfAir + i_RefractiveIndexOfWater);
R_0 *= R_0;
const float R_theta = R_0 + (1.0 - R_0) * pow(max(0., 1.0 - i_CosineTheta), 5.0);
return R_theta;
}
void ApplyReflectionUnderwater(
const half3 i_ViewDirectionWS,
const half3 i_NormalWS,
const float i_RefractiveIndexOfAir,
const float i_RefractiveIndexOfWater,
out float o_LightTransmitted,
out float o_LightReflected
) {
// The the angle of outgoing light from water's surface (whether refracted form outside or internally reflected).
const float cosOutgoingAngle = max(dot(i_NormalWS, i_ViewDirectionWS), 0.);
// Calculate the amount of light transmitted from the sky (o_LightTransmitted).
{
// Have to calculate the incident angle of incoming light to water.
// Surface based on how it would be refracted so as to hit the camera.
const float cosIncomingAngle = cos(asin(clamp((i_RefractiveIndexOfWater * sin(acos(cosOutgoingAngle))) / i_RefractiveIndexOfAir, -1.0, 1.0)));
const float reflectionCoefficient = CalculateFresnelReflectionCoefficient(cosIncomingAngle, i_RefractiveIndexOfAir, i_RefractiveIndexOfWater);
o_LightTransmitted = (1.0 - reflectionCoefficient);
o_LightTransmitted = max(o_LightTransmitted, 0.0);
}
// Calculate the amount of light reflected from below the water.
{
// Angle of incident is angle of reflection.
const float cosIncomingAngle = cosOutgoingAngle;
const float reflectionCoefficient = CalculateFresnelReflectionCoefficient(cosIncomingAngle, i_RefractiveIndexOfAir, i_RefractiveIndexOfWater);
o_LightReflected = reflectionCoefficient;
}
}
void ApplyFresnel
(
const half3 i_ViewDirectionWS,
const half3 i_NormalWS,
const bool i_IsUnderwater,
const float i_RefractiveIndexOfAir,
const float i_RefractiveIndexOfWater,
const float i_TirIntensity,
out float o_LightTransmitted,
out float o_LightReflected
)
{
o_LightTransmitted = 1.0;
if (i_IsUnderwater)
{
ApplyReflectionUnderwater(i_ViewDirectionWS, i_NormalWS, i_RefractiveIndexOfAir, i_RefractiveIndexOfWater, o_LightTransmitted, o_LightReflected);
// Limit how strong TIR is. Not sure if this is the best way but it seems to work gracefully.
o_LightTransmitted = max(o_LightTransmitted, 1.0 - i_TirIntensity);
o_LightReflected = min(o_LightReflected, i_TirIntensity);
}
else
{
const float cosAngle = max(dot(i_NormalWS, i_ViewDirectionWS), 0.0);
// Hardcode water IOR for above surface.
o_LightReflected = CalculateFresnelReflectionCoefficient(cosAngle, i_RefractiveIndexOfAir, 1.33);
}
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 7e2e3d8f8fe32492583d087ad2add952
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,75 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_VERT_HELPERS_H
#define CREST_WATER_VERT_HELPERS_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Cascade.hlsl"
m_CrestNameSpace
// i_meshScaleAlpha is passed in as it is provided per tile and is set only for LOD0
float ComputeLodAlpha(float3 i_worldPos, float i_meshScaleAlpha, in const Cascade i_cascadeData0)
{
// taxicab distance from water center drives LOD transitions
float2 offsetFromCenter = abs(float2(i_worldPos.x - g_Crest_WaterCenter.x, i_worldPos.z - g_Crest_WaterCenter.z));
float taxicab_norm = max(offsetFromCenter.x, offsetFromCenter.y);
// interpolation factor to next lod (lower density / higher sampling period)
// TODO - pass this in, and then make a node to provide it automatically
float lodAlpha = taxicab_norm / i_cascadeData0._Scale - 1.0;
// LOD alpha is remapped to ensure patches weld together properly. Patches can vary significantly in shape (with
// strips added and removed), and this variance depends on the base vertex density of the mesh, as this defines the
// strip width.
lodAlpha = max((lodAlpha - g_Crest_LodAlphaBlackPointFade) / g_Crest_LodAlphaBlackPointWhitePointFade, 0.);
// blend out lod0 when viewpoint gains altitude
lodAlpha = min(lodAlpha + i_meshScaleAlpha, 1.);
#if _DEBUGDISABLESMOOTHLOD_ON
lodAlpha = 0.;
#endif
return lodAlpha;
}
void SnapAndTransitionVertLayout(in const float4x4 i_objectMatrix, in const float i_meshScaleAlpha, in const Cascade i_cascadeData0, in const float i_geometryGridSize, inout float3 io_worldPos, out float o_lodAlpha)
{
const float GRID_SIZE_2 = 2.0 * i_geometryGridSize, GRID_SIZE_4 = 4.0 * i_geometryGridSize;
// snap the verts to the grid
// The snap size should be twice the original size to keep the shape of the eight triangles (otherwise the edge layout changes).
float2 objectPosXZWS = i_objectMatrix._m03_m23;
// Relative world space - add camera pos to get back out to world. Would be nice if we could operate in RWS..
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
objectPosXZWS += _WorldSpaceCameraPos.xz;
#endif
io_worldPos.xz -= frac(objectPosXZWS / GRID_SIZE_2) * GRID_SIZE_2; // caution - sign of frac might change in non-hlsl shaders
// compute lod transition alpha
o_lodAlpha = ComputeLodAlpha(io_worldPos, i_meshScaleAlpha, i_cascadeData0);
// now smoothly transition vert layouts between lod levels - move interior verts inwards towards center
float2 m = frac(io_worldPos.xz / GRID_SIZE_4); // this always returns positive
float2 offset = m - 0.5;
// Check if vert is within one square from the center point which the verts move towards. the verts that need moving
// inwards should have a radius of 0.25, whereas the outer ring of verts will have radius 0.5. Pick half way between
// to give max leeway for numerical robustness.
const float minRadius = 0.375;
if (abs(offset.x) < minRadius) io_worldPos.x += offset.x * o_lodAlpha * GRID_SIZE_4;
if (abs(offset.y) < minRadius) io_worldPos.z += offset.y * o_lodAlpha * GRID_SIZE_4;
}
void SnapAndTransitionVertLayout(in const float i_meshScaleAlpha, in const Cascade i_cascadeData0, in const float i_geometryGridSize, inout float3 io_worldPos, out float o_lodAlpha)
{
SnapAndTransitionVertLayout(UNITY_MATRIX_M, i_meshScaleAlpha, i_cascadeData0, i_geometryGridSize, io_worldPos, o_lodAlpha);
}
m_CrestNameSpaceEnd
#endif // CREST_WATER_VERT_HELPERS_H

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1917cef0dc0904e92aeeb267b5543b49
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,123 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_NORMAL_H
#define CREST_WATER_NORMAL_H
#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/Texture.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Flow.hlsl"
#if (CREST_SHIFTING_ORIGIN != 0)
#include "Packages/com.waveharmonic.crest.shifting-origin/Runtime/Shaders/ShiftingOrigin.hlsl"
#endif
m_CrestNameSpace
half2 SampleNormalMaps
(
const TiledTexture i_NormalMap,
const half i_Strength,
const float2 i_UndisplacedXZ,
const float i_LodAlpha,
const Cascade i_CascadeData
)
{
float2 worldXZUndisplaced = i_UndisplacedXZ;
#if (CREST_SHIFTING_ORIGIN != 0)
// Apply tiled floating origin offset. Always needed.
worldXZUndisplaced -= ShiftingOriginOffset(i_NormalMap, i_CascadeData);
#endif
const float2 v0 = float2(0.94, 0.34), v1 = float2(-0.85, -0.53);
float scale = i_NormalMap._scale * i_CascadeData._Scale / 10.0;
const float spdmulL = _Crest_ChunkNormalScrollSpeed.x * i_NormalMap._speed;
half2 norm =
UnpackNormal(i_NormalMap.Sample((worldXZUndisplaced + v0 * g_Crest_Time * spdmulL) / scale)).xy +
UnpackNormal(i_NormalMap.Sample((worldXZUndisplaced + v1 * g_Crest_Time * spdmulL) / scale)).xy;
// blend in next higher scale of normals to obtain continuity
const half nblend = i_LodAlpha * _Crest_ChunkFarNormalsWeight;
if (nblend > 0.001)
{
// next lod level
scale *= 2.0;
const float spdmulH = _Crest_ChunkNormalScrollSpeed.y * i_NormalMap._speed;
norm = lerp(norm,
UnpackNormal(i_NormalMap.Sample((worldXZUndisplaced + v0 * g_Crest_Time * spdmulH) / scale)).xy +
UnpackNormal(i_NormalMap.Sample((worldXZUndisplaced + v1 * g_Crest_Time * spdmulH) / scale)).xy,
nblend);
}
// approximate combine of normals. would be better if normals applied in local frame.
return i_Strength * norm;
}
half2 SampleNormalMaps
(
const Flow i_Flow,
const TiledTexture i_NormalMap,
const half i_Strength,
const float2 i_UndisplacedXZ,
const float i_LodAlpha,
const Cascade i_CascadeData
)
{
return SampleNormalMaps
(
i_NormalMap,
i_Strength,
i_UndisplacedXZ - i_Flow._Flow * (i_Flow._Offset0 - i_Flow._Period * 0.5),
i_LodAlpha,
i_CascadeData
) * i_Flow._Weight0 + SampleNormalMaps
(
i_NormalMap,
i_Strength,
i_UndisplacedXZ - i_Flow._Flow * (i_Flow._Offset1 - i_Flow._Period * 0.5),
i_LodAlpha,
i_CascadeData
) * i_Flow._Weight1;
}
void WaterNormal
(
const float2 i_WaterLevelDerivatives,
const half3 i_ViewDirectionWS,
const half i_MinimumReflectionDirectionY,
const bool i_Underwater,
inout half3 io_NormalWS
)
{
// Account for water level changes which change angle of water surface, impacting normal.
io_NormalWS.xz += -i_WaterLevelDerivatives;
// Finalise normal
io_NormalWS = normalize(io_NormalWS);
if (i_Underwater)
{
return;
}
// Limit how close to horizontal reflection ray can get, useful to avoid unsightly below-horizon reflections.
{
float3 refl = reflect(-i_ViewDirectionWS, io_NormalWS);
if (refl.y < i_MinimumReflectionDirectionY)
{
// Find the normal that keeps the reflection direction above the horizon. Compute
// the reflection dir that does work, normalize it, and then normal is half vector
// between this good reflection direction and view direction.
float3 FL = refl;
FL.y = i_MinimumReflectionDirectionY;
FL = normalize(FL);
io_NormalWS = normalize(FL + i_ViewDirectionWS);
}
}
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5e48e10254d2e49b8b040871986b6605
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,66 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_REFLECTION_H
#define CREST_WATER_REFLECTION_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Utility.hlsl"
float4 _Crest_ReflectionPositionNormal[2];
Texture2DArray _Crest_ReflectionTexture;
SamplerState sampler_Crest_ReflectionTexture;
m_CrestNameSpace
half4 PlanarReflection
(
const Texture2DArray i_ReflectionsTexture,
const SamplerState i_ReflectionsSampler,
const half i_Intensity,
const half i_Smoothness,
const half i_Roughness,
const half3 i_NormalWS,
const half i_NormalStrength,
const half3 i_ViewDirectionWS,
const float2 i_PositionNDC,
const bool i_Underwater
)
{
half3 planeNormal = half3(0.0, i_Underwater ? -1.0 : 1.0, 0.0);
half3 reflected = reflect(-i_ViewDirectionWS, lerp(planeNormal, i_NormalWS, i_NormalStrength));
reflected.y = -reflected.y;
float4 positionCS = mul(UNITY_MATRIX_VP, half4(reflected, 0.0));
#if UNITY_UV_STARTS_AT_TOP
positionCS.y = -positionCS.y;
#endif
float2 positionNDC = positionCS.xy * rcp(positionCS.w) * 0.5 + 0.5;
// Cancel out distortion if out of bounds. We could make this nicer by doing an edge fade but the improvement is
// barely noticeable. Edge fade requires recalculating the above a second time.
{
float4 positionAndNormal = _Crest_ReflectionPositionNormal[i_Underwater];
if (dot(positionNDC - positionAndNormal.xy, positionAndNormal.zw) < 0.0)
{
positionNDC = lerp(i_PositionNDC, positionNDC, 0.25);
}
}
const half roughness = PerceptualSmoothnessToPerceptualRoughness(i_Smoothness);
const half level = PerceptualRoughnessToMipmapLevel(roughness, i_Roughness);
half4 reflection = i_ReflectionsTexture.SampleLevel(sampler_Crest_ReflectionTexture, float3(positionNDC, i_Underwater), level);
// If more than four layers are used on the terrain, they will appear black if HDR
// is enabled on the planar reflection camera. Alpha is probably a negative value.
reflection.a = saturate(reflection.a);
reflection.a *= i_Intensity;
return reflection;
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 59ed70386a75c40a594ac82af7bd817b
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,113 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_REFRACTION_H
#define CREST_WATER_REFRACTION_H
#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/Utility/Depth.hlsl"
#if (CREST_PORTALS != 0)
#include "Packages/com.waveharmonic.crest.portals/Runtime/Shaders/Library/Portals.hlsl"
#endif
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Utility.hlsl"
#ifndef SUPPORTS_FOVEATED_RENDERING_NON_UNIFORM_RASTER
#define FoveatedRemapLinearToNonUniform(uv) uv
#endif
m_CrestNameSpace
// We take the unrefracted scene colour as input because having a Scene Colour node in the graph
// appears to be necessary to ensure the scene colours are bound?
void RefractedScene
(
const half i_RefractionStrength,
const half3 i_NormalWS,
const float2 i_PositionNDC,
const float i_PixelZ,
const half3 i_SceneColorUnrefracted,
const float i_SceneZ,
const float i_SceneZRaw,
const bool i_Underwater,
out half3 o_SceneColor,
out float o_SceneDistance,
out float3 o_ScenePositionWS,
out bool o_Caustics
)
{
float2 positionNDC = i_PositionNDC;
float sceneDepthRaw = i_SceneZRaw;
o_Caustics = true;
// View ray intersects geometry surface either above or below water surface.
float2 refractOffset = i_RefractionStrength * i_NormalWS.xz;
if (!i_Underwater)
{
// We're above the water, so behind interface is depth fog.
refractOffset *= min(1.0, 0.5 * (i_SceneZ - i_PixelZ)) / i_SceneZ;
}
else
{
// When looking up through water, full strength ends up being quite intense so reduce it a bunch.
refractOffset *= 0.3;
}
// Blend at the edge of the screen to avoid artifacts.
refractOffset *= 1.0 - EdgeBlendingFactor(positionNDC, i_PixelZ);
const float2 positionNDCRefracted = FoveatedRemapLinearToNonUniform(positionNDC + refractOffset);
float sceneDepthRawRefracted = SHADERGRAPH_SAMPLE_SCENE_DEPTH(positionNDCRefracted);
#if (CREST_PORTALS != 0)
#if _ALPHATEST_ON
// Portals
ApplyPortalRefractions(positionNDCRefracted, i_SceneZRaw, i_Underwater, sceneDepthRawRefracted, o_Caustics);
#endif
#endif
const float sceneZRefract = Utility::CrestLinearEyeDepth(sceneDepthRawRefracted);
// Depth fog & caustics - only if view ray starts from above water.
// Compute depth fog alpha based on refracted position if it landed on an
// underwater surface, or on unrefracted depth otherwise.
if (sceneZRefract > i_PixelZ)
{
// Refracted.
o_SceneDistance = sceneZRefract - i_PixelZ;
o_SceneColor = SHADERGRAPH_SAMPLE_SCENE_COLOR(positionNDCRefracted);
positionNDC = positionNDCRefracted;
sceneDepthRaw = sceneDepthRawRefracted;
}
else
{
// Unrefracted.
// It seems that when MSAA is enabled this can sometimes be negative.
o_SceneDistance = max(i_SceneZ - i_PixelZ, 0.0);
o_SceneColor = i_SceneColorUnrefracted;
// NOTE: Causes refraction artifact with caustics. Cannot remember exactly why this was added.
// o_Caustics = false;
positionNDC = FoveatedRemapLinearToNonUniform(positionNDC);
}
if (i_Underwater)
{
// Depth fog is handled by underwater shader.
o_SceneDistance = i_PixelZ;
}
o_ScenePositionWS = ComputeWorldSpacePosition(positionNDC, sceneDepthRaw, UNITY_MATRIX_I_VP);
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
o_ScenePositionWS += _WorldSpaceCameraPos;
#endif
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bede8020b874c4bc389767163dc07528
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,56 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Constants for shader graph. For example, we can force shader features when we have yet to make a keyword for it in
// shader graph.
// This file must be included before all other includes. And it must be done for every node. This is due to #ifndef
// limiting includes from being evaluated once, and we cannot specify the order because shader graph does this.
#ifndef CREST_SHADERGRAPH_CONSTANTS_H
#define CREST_SHADERGRAPH_CONSTANTS_H
#ifdef UNIVERSAL_PIPELINE_CORE_INCLUDED
#define CREST_URP 1
#if (SHADERPASS == SHADERPASS_SHADOWCASTER)
#define CREST_SHADOWPASS 1
#endif
#if _SURFACE_TYPE_TRANSPARENT
#define d_Transparent 1
#endif
#elif BUILTIN_TARGET_API
#define CREST_BIRP 1
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Legacy/InputsDriven.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Legacy/Common.hlsl"
#if _BUILTIN_SURFACE_TYPE_TRANSPARENT
#define d_Transparent 1
#endif
// SHADERPASS is currently broken:
// https://forum.unity.com/threads/built-in-renderer-shaderpass-include-in-wrong-place.1444156/
// #if (SHADERPASS == SHADERPASS_SHADOWCASTER)
// #define CREST_SHADOWPASS 1
// #endif
#else
// HDRP does not appear to have a reliable keyword to target.
#define CREST_HDRP 1
#if _SURFACE_TYPE_TRANSPARENT
#define d_Transparent 1
#endif
#if (SHADERPASS == SHADERPASS_SHADOWS)
#define CREST_SHADOWPASS 1
#endif
#endif
#if defined(CREST_HDRP) && (SHADERPASS == SHADERPASS_FORWARD)
#define CREST_HDRP_FORWARD_PASS 1
#endif
#endif // CREST_SHADERGRAPH_CONSTANTS_H

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a641876bd41254d07831fd1b0b92f99b
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,38 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_UTILITY_H
#define CREST_UTILITY_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
m_CrestNameSpace
half InverseLerp(half a, half b, half t)
{
return (t - a) / (b - a);
}
// Taken from:
// https://github.com/Unity-Technologies/Graphics/blob/f56d2b265eb9e01b0376623e909f98c88bc60662/Packages/com.unity.render-pipelines.high-definition/Runtime/Surface/Shaders/WaterUtilities.hlsl#L781-L797
float EdgeBlendingFactor(float2 screenPosition, float distanceToWaterSurface)
{
// Convert the screen position to NDC
float2 screenPosNDC = screenPosition * 2 - 1;
// We want the value to be 0 at the center and go to 1 at the edges
float distanceToEdge = 1.0 - min((1.0 - abs(screenPosNDC.x)), (1.0 - abs(screenPosNDC.y)));
// What we want here is:
// - +inf -> 0.5 value is 0
// - 0.5-> 0.25 value is going from 0 to 1
// - 0.25 -> 0 value is 1
float distAttenuation = 1.0 - saturate((distanceToWaterSurface - 0.75) / 0.25);
// Based on if the water surface is close, we want to make the blending region even bigger
return lerp(saturate((distanceToEdge - 0.8) / (0.2)), saturate(distanceToEdge + 0.25), distAttenuation);
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 8a725a22984064d758e158a964ac1255
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,186 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Guard against missing uniforms.
#ifdef SHADERPASS
#define m_Properties \
const float3 i_PositionWS, \
const float3 i_ObjectPosition, \
const float3 i_CameraPosition, \
const float i_Time, \
out float3 o_PositionWS, \
out float2 o_UndisplacedXZ, \
out float o_LodAlpha, \
out half o_WaterLevelOffset, \
out float2 o_WaterLevelDerivatives, \
out half2 o_Flow
// Guard against Shader Graph preview.
#ifndef SHADERGRAPH_PREVIEW
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Shim.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/InputsDriven.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Cascade.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Geometry.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Utility/Depth.hlsl"
#if CREST_URP
#if (SHADERPASS == SHADERPASS_MOTION_VECTORS)
#define _TRANSPARENT_WRITES_MOTION_VEC 1
#endif
#endif
#if _TRANSPARENT_WRITES_MOTION_VEC
#define m_Slice clamp(_Crest_LodIndex + (isMotionVectors ? g_Crest_LodChange : 0), 0, g_Crest_LodCount)
#define m_Make(slice) Make(slice, isMotionVectors)
#else
#define m_Slice _Crest_LodIndex
#define m_Make(slice) Make(slice)
#endif
m_CrestNameSpace
void Vertex(m_Properties)
{
// This will get called twice.
// With current and previous time respectively.
o_UndisplacedXZ = 0.0;
o_LodAlpha = 0.0;
o_WaterLevelOffset = 0.0;
o_WaterLevelDerivatives = 0.0;
o_Flow = 0.0;
const bool isMotionVectors = i_Time < _Time.y;
const float slice0 = m_Slice;
const float slice1 = slice0 + 1;
const Cascade cascade0 = Cascade::m_Make(slice0);
const Cascade cascade1 = Cascade::m_Make(slice1);
o_PositionWS = i_PositionWS;
// Vertex snapping and LOD transition.
SnapAndTransitionVertLayout
(
_Crest_ChunkMeshScaleAlpha,
Cascade::Make(_Crest_LodIndex),
_Crest_ChunkGeometryGridWidth,
o_PositionWS,
o_LodAlpha
);
// Fix precision errors at edges.
{
// Scale up by small "epsilon" to solve numerical issues. Expand slightly about tile center.
// :WaterGridPrecisionErrors
const float2 tileCenterXZ = i_ObjectPosition.xz;
const float2 cameraPositionXZ = abs(i_CameraPosition.xz);
// Scale "epsilon" by distance from zero. There is an issue where overlaps can cause SV_IsFrontFace
// to be flipped (needs to be investigated). Gaps look bad from above surface, and overlaps look bad
// from below surface. We want to close gaps without introducing overlaps. A fixed "epsilon" will
// either not solve gaps at large distances or introduce too many overlaps at small distances. Even
// with scaling, there are still unsolvable overlaps underwater (especially at large distances).
// 100,000 (0.00001) is the maximum position before Unity warns the user of precision issues.
o_PositionWS.xz = lerp(tileCenterXZ, o_PositionWS.xz, lerp(1.0, 1.01, max(cameraPositionXZ.x, cameraPositionXZ.y) * 0.00001));
}
o_UndisplacedXZ = o_PositionWS.xz;
// Calculate sample weights. params.z allows shape to be faded out (used on last lod to support pop-less scale transitions).
const float weight0 = (1.0 - o_LodAlpha) * cascade0._Weight;
const float weight1 = (1.0 - weight0) * cascade1._Weight;
// Data that needs to be sampled at the undisplaced position.
if (weight0 > m_CrestSampleLodThreshold)
{
#if _TRANSPARENT_WRITES_MOTION_VEC
if (isMotionVectors)
{
Cascade::MakeAnimatedWavesSource(slice0).SampleDisplacement(o_UndisplacedXZ, weight0, o_PositionWS, o_WaterLevelDerivatives);
}
else
#endif
{
Cascade::MakeAnimatedWaves(slice0).SampleDisplacement(o_UndisplacedXZ, weight0, o_PositionWS, o_WaterLevelDerivatives);
}
}
if (weight1 > m_CrestSampleLodThreshold)
{
#if _TRANSPARENT_WRITES_MOTION_VEC
if (isMotionVectors)
{
Cascade::MakeAnimatedWavesSource(slice1).SampleDisplacement(o_UndisplacedXZ, weight1, o_PositionWS, o_WaterLevelDerivatives);
}
else
#endif
{
Cascade::MakeAnimatedWaves(slice1).SampleDisplacement(o_UndisplacedXZ, weight1, o_PositionWS, o_WaterLevelDerivatives);
}
}
// Data that needs to be sampled at the displaced position.
if (weight0 > m_CrestSampleLodThreshold)
{
#if CREST_FLOW_ON
Cascade::MakeFlow(slice0).SampleFlow(o_UndisplacedXZ, weight0, o_Flow);
#endif
}
if (weight1 > m_CrestSampleLodThreshold)
{
#if CREST_FLOW_ON
Cascade::MakeFlow(slice1).SampleFlow(o_UndisplacedXZ, weight1, o_Flow);
#endif
}
#if _TRANSPARENT_WRITES_MOTION_VEC
if (isMotionVectors)
{
o_PositionWS.xz -= g_Crest_WaterCenter.xz;
o_PositionWS.xz *= g_Crest_WaterScaleChange;
o_PositionWS.xz += g_Crest_WaterCenter.xz;
o_PositionWS += g_Crest_WaterCenterDelta;
}
#endif
}
m_CrestNameSpaceEnd
#endif // SHADERGRAPH_PREVIEW
void Vertex_float(m_Properties)
{
#if SHADERGRAPH_PREVIEW
o_PositionWS = 0.0;
o_UndisplacedXZ = 0.0;
o_LodAlpha = 0.0;
o_WaterLevelOffset = 0.0;
o_WaterLevelDerivatives = 0.0;
o_Flow = 0.0;
#else // SHADERGRAPH_PREVIEW
m_Crest::Vertex
(
i_PositionWS,
i_ObjectPosition,
i_CameraPosition,
i_Time,
o_PositionWS,
o_UndisplacedXZ,
o_LodAlpha,
o_WaterLevelOffset,
o_WaterLevelDerivatives,
o_Flow
);
#endif // SHADERGRAPH_PREVIEW
}
#undef m_Properties
#endif // SHADERPASS

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 2c406004ec98d40d292b359364c27961
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,84 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#ifndef CREST_WATER_VOLUME_LIGHTING_H
#define CREST_WATER_VOLUME_LIGHTING_H
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Macros.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Library/Globals.hlsl"
#include "Packages/com.waveharmonic.crest/Runtime/Shaders/Surface/Utility.hlsl"
m_CrestNameSpace
// Schlick phase function.
float SchlickPhase(float phaseG, float cosTheta)
{
const float schlickK = 1.5 * phaseG - 0.5 * phaseG * phaseG * phaseG;
const float phaseFactor = 1.0 + schlickK * cosTheta;
return (1.0 - schlickK * schlickK) / (4.0 * PI * phaseFactor * phaseFactor);
}
void VolumeLighting
(
const half3 i_Absorption,
const half3 i_Scattering,
const half i_PhaseG,
const half i_DirectionalLightShadow,
const half3 i_ViewDirectionWS,
const half3 i_AmbientLighting,
const half3 i_PrimaryLightDirection,
const half3 i_PrimaryLightIntensity,
const half3 i_AdditionalLight,
const half i_AmbientLightingTerm,
const half i_PrimaryLightingTerm,
const half i_WaterRayLength,
const half3 i_SunBoost,
const half i_ShadowsAffectAmbientLightingFactor,
out half3 o_VolumeLight,
out half3 o_VolumeOpacity
)
{
// Extinction is light absorbed plus light scattered out.
const half3 extinction = i_Absorption + i_Scattering;
const float ambientLightShadow = lerp
(
1.0,
i_DirectionalLightShadow,
saturate(min(min(extinction.x, extinction.y), extinction.z) * i_ShadowsAffectAmbientLightingFactor * g_Crest_DynamicSoftShadowsFactor)
);
// Sun
const float sunPhase = SchlickPhase(i_PhaseG, dot(i_PrimaryLightDirection, i_ViewDirectionWS));
const float3 inScatteredSun = (1.0 + i_SunBoost) * sunPhase * i_PrimaryLightIntensity * i_PrimaryLightingTerm;
const float3 inScatteredAmbient = i_AmbientLighting * i_AmbientLightingTerm * ambientLightShadow;
// Total inscattered
const float3 inscattered = (inScatteredAmbient + i_AdditionalLight + inScatteredSun * i_DirectionalLightShadow);
const float3 scatteringAmount = saturate(i_Scattering / max(extinction, 0.00001));
o_VolumeLight = inscattered * scatteringAmount;
// Like 'alpha' value or obscurance. Volume light needs multiplying by this value to be correct in shallows.
o_VolumeOpacity = 1.0 - exp(-extinction * max(0.0, i_WaterRayLength));
}
half PinchSSS
(
const half i_Pinch,
const half i_Minimum,
const half i_Maximum,
const half i_Falloff,
const half i_Intensity,
const half3 i_SunDirection,
const half i_SunDirectionFalloff,
const half3 i_ViewDirectionWS
)
{
half pinch = pow(saturate(InverseLerp(i_Minimum, i_Maximum, max(2.0 - i_Pinch, 0.0))), i_Falloff);
half sun = pow(saturate(dot(i_ViewDirectionWS, -i_SunDirection)), i_SunDirectionFalloff);
return pinch * sun * i_Intensity;
}
m_CrestNameSpaceEnd
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: bf1d64d62eeee4f058f304a0c2f36590
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: 00ffe7d0b7161420897069dc6e12822c
ScriptedImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 2
userData:
assetBundleName:
assetBundleVariant:
script: {fileID: 11500000, guid: 625f186215c104763be7675aa2d941aa, type: 3}