170 lines
5.4 KiB
HLSL
170 lines
5.4 KiB
HLSL
// Crest Water System
|
|
// Copyright © 2024 Wave Harmonic. All rights reserved.
|
|
|
|
#ifndef CREST_WATER_REFRACTION_H
|
|
#define CREST_WATER_REFRACTION_H
|
|
|
|
#if !d_Crest_SimpleTransparency
|
|
|
|
#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
|
|
|
|
#if (UNITY_VERSION < 60000000) || !defined(CREST_URP)
|
|
float4 _CameraDepthTexture_TexelSize;
|
|
#endif
|
|
|
|
m_CrestNameSpace
|
|
|
|
float2 GetRefractionCoordinates(const half3 i_View, const half3 i_Normal, const float3 i_Position, const half i_IOR, const half i_Strength)
|
|
{
|
|
float3 position = i_Position;
|
|
|
|
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
|
|
position -= _WorldSpaceCameraPos;
|
|
#endif
|
|
|
|
const half3 ray = refract(-i_View, i_Normal, i_IOR) * i_Strength;
|
|
float2 uv = ComputeNormalizedDeviceCoordinates(position + ray, UNITY_MATRIX_VP);
|
|
|
|
#if CREST_HDRP
|
|
// Prevent artifacts at edge. Maybe because depth is an atlas for HDRP.
|
|
uv = clamp(uv, _CameraDepthTexture_TexelSize.xy, 1.0 - _CameraDepthTexture_TexelSize.xy);
|
|
#endif
|
|
|
|
return FoveatedRemapLinearToNonUniform(uv);
|
|
}
|
|
|
|
// 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 half i_AirIOR,
|
|
const half i_WaterIOR,
|
|
const half3 i_NormalWS,
|
|
const float3 i_PositionWS,
|
|
const float2 i_PositionNDC,
|
|
const float4 i_ScreenPositionRaw,
|
|
const float i_PixelZ,
|
|
const half3 i_View,
|
|
const float i_SceneZ,
|
|
const float i_SceneZRaw,
|
|
const float i_Scale,
|
|
const float i_LodAlpha,
|
|
const bool i_Underwater,
|
|
const half i_TotalInternalReflectionIntensity,
|
|
out half3 o_SceneColor,
|
|
out float o_SceneDistance,
|
|
out float3 o_ScenePositionWS,
|
|
out float2 o_PositionSS,
|
|
out bool o_Caustics
|
|
)
|
|
{
|
|
o_Caustics = true;
|
|
|
|
half strength = i_RefractionStrength;
|
|
|
|
const half _AirToWaterRatio = i_AirIOR / i_WaterIOR;
|
|
const half _WaterToAirRatio = i_WaterIOR / i_AirIOR;
|
|
|
|
// If no TIR, then use same IOR.
|
|
const bool isA2WR = !i_Underwater || i_TotalInternalReflectionIntensity < 1.0;
|
|
|
|
const half eta = isA2WR ? _AirToWaterRatio : _WaterToAirRatio;
|
|
|
|
half3 normal = i_NormalWS;
|
|
|
|
// Exchanges accuracy for less artifacts.
|
|
if (isA2WR)
|
|
{
|
|
half multiplier = 0.0;
|
|
|
|
if (i_Underwater)
|
|
{
|
|
multiplier = 1.0;
|
|
// Max fade when water is 5m deep.
|
|
multiplier = saturate(g_Crest_WaterDepthAtViewer * 0.2);
|
|
// Max fade by displacement.
|
|
multiplier *= saturate(g_Crest_MaximumVerticalDisplacement - 1.0);
|
|
// Fade towards screen edge where off screen samples happen. + n is fade start.
|
|
multiplier *= saturate((dot(i_PositionNDC - 0.5, -g_Crest_HorizonNormal) + 0.5) * 2.0);
|
|
}
|
|
|
|
normal.y *= multiplier;
|
|
}
|
|
|
|
// Since we lose detail at a distance, boosting refraction helps visually.
|
|
strength *= lerp(i_Scale, i_Scale * 2.0, i_LodAlpha) * 0.25;
|
|
|
|
// Restrict to a reasonable maximum.
|
|
strength = min(strength, i_RefractionStrength * 4.0);
|
|
|
|
float2 uv = GetRefractionCoordinates(i_View, normal, i_PositionWS, eta, strength);
|
|
|
|
o_PositionSS = min(uv * _ScreenSize.xy, _ScreenSize.xy - 1.0);
|
|
|
|
#if CREST_BIRP
|
|
float deviceDepth = LoadSceneDepth(o_PositionSS);
|
|
#else
|
|
float deviceDepth = SHADERGRAPH_SAMPLE_SCENE_DEPTH(uv);
|
|
#endif
|
|
|
|
#if (CREST_PORTALS != 0)
|
|
#if _ALPHATEST_ON
|
|
Portal::EvaluateRefraction(uv, i_SceneZRaw, i_Underwater, deviceDepth, o_Caustics);
|
|
#endif
|
|
#endif
|
|
|
|
float linearDepth = Utility::CrestLinearEyeDepth(deviceDepth);
|
|
float depthDifference = linearDepth - i_PixelZ;
|
|
|
|
normal *= saturate(depthDifference);
|
|
|
|
uv = GetRefractionCoordinates(i_View, normal, i_PositionWS, eta, strength);
|
|
|
|
o_PositionSS = min(uv * _ScreenSize.xy, _ScreenSize.xy - 1.0);
|
|
|
|
#if CREST_BIRP
|
|
deviceDepth = LoadSceneDepth(o_PositionSS);
|
|
#else
|
|
deviceDepth = SHADERGRAPH_SAMPLE_SCENE_DEPTH(uv);
|
|
#endif
|
|
|
|
linearDepth = Utility::CrestLinearEyeDepth(deviceDepth);
|
|
// It seems that when MSAA is enabled this can sometimes be negative.
|
|
depthDifference = max(linearDepth - i_PixelZ, 0.0);
|
|
|
|
#if CREST_BIRP
|
|
// Sampling artifacts which manifest as a fine outline around refractions. Always
|
|
// affects BIRP unless we use Load. Does not affect URP unless downsampling or MSAA
|
|
// is used, but Load exposes us to RT scaling. Best to use Sample with HDRP too.
|
|
o_SceneColor = LoadSceneColor(o_PositionSS).rgb;
|
|
#else
|
|
// Sampling artifacts if downsampling or MSAA used. Load does not help. And we get
|
|
// outlines around all objects irrespective of refraction.
|
|
o_SceneColor = SHADERGRAPH_SAMPLE_SCENE_COLOR(uv).rgb;
|
|
#endif
|
|
|
|
o_SceneDistance = depthDifference;
|
|
o_ScenePositionWS = ComputeWorldSpacePosition(uv, deviceDepth, UNITY_MATRIX_I_VP);
|
|
#if (SHADEROPTIONS_CAMERA_RELATIVE_RENDERING != 0)
|
|
o_ScenePositionWS += _WorldSpaceCameraPos;
|
|
#endif
|
|
}
|
|
|
|
m_CrestNameSpaceEnd
|
|
|
|
#endif
|
|
#endif
|