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

#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