You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
233 lines
9.9 KiB
233 lines
9.9 KiB
#ifndef WATER_UTILITIES_H
|
|
#define WATER_UTILITIES_H
|
|
|
|
#define WATER_IOR 1.3333
|
|
#define WATER_INV_IOR 1.0 / WATER_IOR
|
|
#define AMBIENT_SCATTERING_INTENSITY 0.25
|
|
#define HEIGHT_SCATTERING_INTENSITY 0.25
|
|
#define DISPLACEMENT_SCATTERING_INTENSITY 0.25
|
|
#define UNDER_WATER_REFRACTION_DISTANCE 100.0
|
|
#define MAX_MENISCUS_REFRACTION_MULTIPLIER 0.5
|
|
#define MENISCUS_THRESHOLD 0.05
|
|
|
|
// Includes
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/SampleWaterSurface.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/UnderWaterUtilities.hlsl"
|
|
|
|
float2 EvaluateSurfaceGradients(float3 p0, float3 p1, float3 p2)
|
|
{
|
|
float3 v0 = normalize(p1 - p0);
|
|
float3 v1 = normalize(p2 - p0);
|
|
float3 geometryNormal = normalize(cross(v1, v0));
|
|
return SurfaceGradientFromPerturbedNormal(float3(0, 1, 0), geometryNormal).xz;
|
|
}
|
|
|
|
#if !defined(WATER_SIMULATION)
|
|
float3 ComputeDebugNormal(float3 worldPos)
|
|
{
|
|
float3 worldPosDdx = normalize(ddx(worldPos));
|
|
float3 worldPosDdy = normalize(ddy(worldPos));
|
|
return normalize(-cross(worldPosDdx, worldPosDdy));
|
|
}
|
|
|
|
float3 EvaluateWaterSurfaceGradient_VS(float3 positionAWS, int LOD, int bandIndex)
|
|
{
|
|
// Compute the simulation coordinates
|
|
float2 uvBand = TransformWaterUV(positionAWS.xz, bandIndex);
|
|
|
|
// Compute the texture size param for the filtering
|
|
int2 res = _BandResolution >> LOD;
|
|
float4 texSize = 0.0;
|
|
texSize.xy = res;
|
|
texSize.zw = 1.0f / res;
|
|
|
|
// First band
|
|
float4 additionalData = SampleTexture2DArrayBicubicLOD(TEXTURE2D_ARRAY_ARGS(_WaterAdditionalDataBuffer, s_linear_repeat_sampler), uvBand, bandIndex, texSize, LOD);
|
|
float3 surfaceGradient = float3(additionalData.x, 0, additionalData.y);
|
|
|
|
// Blend the various surface gradients
|
|
return surfaceGradient;
|
|
}
|
|
|
|
float3 BlendWaterNormal(float3 normalTS, float3 normalWS)
|
|
{
|
|
// TODO: figure out why i have to invert the x component here
|
|
normalTS.x = -normalTS.x;
|
|
float3x3 frame = GetLocalFrame(normalWS);
|
|
return TransformTangentToWorld(normalTS, frame, true);
|
|
}
|
|
#endif
|
|
|
|
struct FoamData
|
|
{
|
|
float smoothness;
|
|
float foamValue;
|
|
};
|
|
|
|
void EvaluateFoamData(float surfaceFoam, float customFoam, float3 positionOS, out FoamData foamData)
|
|
{
|
|
float foamLifeTime = saturate(1.0 - (surfaceFoam + customFoam));
|
|
foamData.foamValue = FoamErosion(foamLifeTime, positionOS.xz);
|
|
|
|
// Blend the smoothness of the water and the foam
|
|
foamData.smoothness = lerp(_WaterSmoothness, _FoamSmoothness, saturate(foamData.foamValue));
|
|
}
|
|
|
|
#define WATER_BACKGROUND_ABSORPTION_DISTANCE 1000.f
|
|
|
|
float EvaluateHeightBasedScattering(float lowFrequencyHeight, float distanceToCamera)
|
|
{
|
|
// Lerp towards middle height of 0.5 as distance increases
|
|
// height is already faded because it's computed form vertex displacement which is faded.
|
|
// But add additional fading using twice the distance to reduce visual repetition
|
|
lowFrequencyHeight = lerp(0.5, lowFrequencyHeight, DistanceFade(distanceToCamera * 2, 0));
|
|
|
|
float heightScatteringValue = lerp(0.0, HEIGHT_SCATTERING_INTENSITY, lowFrequencyHeight);
|
|
return lerp(0.0, heightScatteringValue, _HeightBasedScattering);
|
|
}
|
|
|
|
float EvaluateDisplacementScattering(float displacement)
|
|
{
|
|
float displacementScatteringValue = lerp(0.0, DISPLACEMENT_SCATTERING_INTENSITY, displacement / (_ScatteringWaveHeight * WATER_SYSTEM_CHOPPINESS));
|
|
return lerp(0.0, displacementScatteringValue, _DisplacementScattering);
|
|
}
|
|
|
|
float GetWaveTipThickness(float normalizedDistanceToMaxWaveHeightPlane, float3 worldView, float3 refractedRay)
|
|
{
|
|
float H = saturate(normalizedDistanceToMaxWaveHeightPlane);
|
|
return 1.f - saturate(1 + refractedRay.y - 0.2) * (H * H) / 0.4;
|
|
}
|
|
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl"
|
|
|
|
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);
|
|
}
|
|
|
|
void ComputeWaterRefractionParams(float3 waterPosRWS, float2 positionNDC, float3 V,
|
|
float3 waterNormal, float3 lowFrequencyNormals, bool aboveWater,
|
|
bool disableUnderWaterIOR, float3 upVector, float maxRefractionDistance,
|
|
float3 extinctionCoeff,
|
|
out float3 refractedWaterPosRWS, out float2 distortedWaterNDC, out float3 absorptionTint)
|
|
{
|
|
absorptionTint = 0.0f;
|
|
|
|
// Compute the position of the surface behind the water surface
|
|
float directWaterDepth = SampleCameraDepth(positionNDC);
|
|
float3 directWaterPosRWS = ComputeWorldSpacePosition(positionNDC, directWaterDepth, UNITY_MATRIX_I_VP);
|
|
|
|
// Compute the distance between the water surface and the object behind
|
|
float underWaterDistance = directWaterDepth == UNITY_RAW_FAR_CLIP_VALUE ? WATER_BACKGROUND_ABSORPTION_DISTANCE : length(directWaterPosRWS - waterPosRWS);
|
|
|
|
// We approach the refraction differently if we are under or above water for various reasons
|
|
|
|
float3 distortedWaterWS = 0.0f;
|
|
if (aboveWater || disableUnderWaterIOR)
|
|
{
|
|
float3 refractedView = lerp(waterNormal, upVector, EdgeBlendingFactor(positionNDC, length(waterPosRWS))) * (1 - upVector);
|
|
|
|
// If camera is below the water surface, we mulitply the maxRefractionDistance with (half) the distance between the water surface pixel and the camera water depth.
|
|
float refractionDistance;
|
|
if (!aboveWater)
|
|
refractionDistance = maxRefractionDistance * abs(waterPosRWS.y) * 0.5f;
|
|
else
|
|
refractionDistance = min(underWaterDistance, maxRefractionDistance);
|
|
|
|
distortedWaterWS = waterPosRWS + refractedView * refractionDistance;
|
|
}
|
|
|
|
// If underwater
|
|
if (!aboveWater)
|
|
{
|
|
float3 refractedView = refract(-V, waterNormal, WATER_IOR);
|
|
if (any(refractedView != 0.0f)) // not TIR
|
|
absorptionTint = 1.0f;
|
|
|
|
if (disableUnderWaterIOR)
|
|
{
|
|
// At the limit between refraction and internal reflection, we simulate a higher refraction to avoid having a harsh threshold between both ray directions.
|
|
float NdotV = dot(waterNormal, V);
|
|
float k = 1.f - WATER_IOR * WATER_IOR * (1.f - NdotV * NdotV);
|
|
float refractionValueMultiplier = 1;
|
|
if (k >= -MENISCUS_THRESHOLD && k <= MENISCUS_THRESHOLD)
|
|
{
|
|
float lerpFactor = saturate((k + MENISCUS_THRESHOLD) / (2 * MENISCUS_THRESHOLD));
|
|
refractionValueMultiplier *= lerp(MAX_MENISCUS_REFRACTION_MULTIPLIER, 0, lerpFactor);
|
|
distortedWaterWS += refractedView * refractionValueMultiplier;
|
|
|
|
absorptionTint = saturate(2 * lerpFactor - 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
distortedWaterWS = waterPosRWS + refractedView * UNDER_WATER_REFRACTION_DISTANCE;
|
|
}
|
|
}
|
|
|
|
// Project the point on screen
|
|
distortedWaterNDC = saturate(ComputeNormalizedDeviceCoordinates(distortedWaterWS, UNITY_MATRIX_VP));
|
|
distortedWaterNDC = min(distortedWaterNDC, 1.0f - _ScreenSize.zw);
|
|
|
|
// Compute the position of the surface behind the water surface
|
|
float refractedWaterDepth = SampleCameraDepth(distortedWaterNDC);
|
|
refractedWaterPosRWS = ComputeWorldSpacePosition(distortedWaterNDC, refractedWaterDepth, UNITY_MATRIX_I_VP);
|
|
float refractedWaterDistance = refractedWaterDepth == UNITY_RAW_FAR_CLIP_VALUE ? WATER_BACKGROUND_ABSORPTION_DISTANCE : length(refractedWaterPosRWS - waterPosRWS);
|
|
|
|
// If the point that we are reading is closer than the water surface
|
|
if (dot(refractedWaterPosRWS - waterPosRWS, V) > 0.0)
|
|
{
|
|
// We read the direct depth (no refraction)
|
|
refractedWaterDistance = underWaterDistance;
|
|
refractedWaterPosRWS = directWaterPosRWS;
|
|
distortedWaterNDC = positionNDC;
|
|
}
|
|
|
|
// Evaluate the absorption tint
|
|
if (aboveWater)
|
|
absorptionTint = exp(-refractedWaterDistance * extinctionCoeff);
|
|
}
|
|
|
|
float EvaluateTipThickness(float3 viewWS, float3 lowFrequencyNormals, float lowFrequencyHeight)
|
|
{
|
|
// Compute the tip thickness
|
|
float tipHeight = saturate(lowFrequencyHeight * 2.0 - 1.0); // ignore negative displacement
|
|
float3 lowFrequencyRefractedRay = refract(-viewWS, lowFrequencyNormals, WATER_INV_IOR);
|
|
return GetWaveTipThickness(max(0.01, tipHeight), viewWS, lowFrequencyRefractedRay);
|
|
}
|
|
|
|
float3 EvaluateRefractionColor(float3 absorptionTint, float3 caustics)
|
|
{
|
|
// Evaluate the refraction color (we need to account for the initial absoption (light to underwater))
|
|
return absorptionTint * caustics * absorptionTint;
|
|
}
|
|
|
|
float3 EvaluateScatteringColor(float3 positionOS, float lowFrequencyHeight, float horizontalDisplacement, float3 absorptionTint, float deepFoam)
|
|
{
|
|
// Evaluate the pre-displaced absolute position
|
|
float3 positionRWS = TransformObjectToWorld(positionOS);
|
|
float distanceToCamera = length(positionRWS);
|
|
|
|
// Evaluate the scattering terms (where the refraction doesn't happen)
|
|
float heightBasedScattering = EvaluateHeightBasedScattering(lowFrequencyHeight, distanceToCamera);
|
|
float displacementScattering = EvaluateDisplacementScattering(horizontalDisplacement);
|
|
float ambientScattering = AMBIENT_SCATTERING_INTENSITY * _AmbientScattering;
|
|
|
|
// Sum the scattering terms
|
|
float scatteringTerms = saturate(ambientScattering + heightBasedScattering + displacementScattering);
|
|
return _WaterAlbedo.xyz * scatteringTerms * (1.f - absorptionTint) * (1.0 + deepFoam);
|
|
}
|
|
#endif // WATER_UTILITIES_H
|