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.
 
 
 
 

227 lines
8.1 KiB

#ifndef WATER_DECAL_UTILITIES_H
#define WATER_DECAL_UTILITIES_H
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Water/ShaderVariablesWater.cs.hlsl"
float2 GetGradient(float2 intPos)
{
float rand = frac(sin(dot(intPos, float2(12.9898, 78.233))) * 43758.5453);;
float angle = 6.283185 * rand;
return float2(cos(angle), sin(angle));
}
float DeformerNoise2D(float2 pos)
{
float2 i = floor(pos);
float2 f = pos - i;
float2 blend = f * f * (3.0 - 2.0 * f);
float g0 = dot(GetGradient(i + float2(0, 0)), f - float2(0, 0));
float g1 = dot(GetGradient(i + float2(1, 0)), f - float2(1, 0));
float g2 = dot(GetGradient(i + float2(0, 1)), f - float2(0, 1));
float g3 = dot(GetGradient(i + float2(1, 1)), f - float2(1, 1));
float noiseVal = lerp(lerp(g0, g1, blend.x), lerp(g2, g3, blend.x), blend.y);
return saturate(noiseVal / 0.7 * 0.5 + 0.5); // normalize to about [0:1]
}
// Distance to a parabola by IQ
// https://iquilezles.org/articles/distfunctions2d/
float sdParabola(float2 pos, float k )
{
pos.x = abs(pos.x);
float ik = 1.0/k;
float p = ik*(pos.y - 0.5*ik)/3.0;
float q = 0.25*ik*ik*pos.x;
float h = q*q - p*p*p;
float r = sqrt(abs(h));
float x = (h>0.0) ? pow(q+r,1.0/3.0) - pow(abs(q-r), 1.0/3.0)*sign(r-q) : 2.0*cos(atan2(r, q)/3.0)*sqrt(p);
return length(pos-float2(x,k*x*x)) * sign(pos.x-x);
}
struct WaveData
{
float position;
int bellIndex;
};
WaveData EvaluateWaveData(float position)
{
WaveData waveData;
// We need to know in which bell we are.
waveData.bellIndex = position > 0.0 ? floor(position) : ceil(-position);
// We're only interested in the fractional part of the position
waveData.position = 1.0 - frac(position);
// Return the wave data
return waveData;
}
float BreakingRegionFactor(float2 breakingRange, float2 positionOS)
{
return saturate((positionOS.x - breakingRange.x) / (breakingRange.y - breakingRange.x));
}
float2 EvaluateBlendRegion(float2 blendRegion, float2 breakingRange, float2 positionOS)
{
return lerp(blendRegion, float2(0.1, 0.9), BreakingRegionFactor(breakingRange, positionOS));
}
float ShoreWaveFirstShape(WaveData waveData)
{
return 0.5 * sin((waveData.position - 1.25) * 2.0 * PI) + 0.5;
}
float ShoreWaveSecondShape(WaveData waveData)
{
return waveData.position < 0.2 ? waveData.position * waveData.position * 25.0 : 0.5 * cos(4 * (waveData.position - 0.2)) + 0.5;
}
float ShoreWaveBlendShapes(float firstLobe, float secondLobe, float2 breakingRange, float normalizedWidth)
{
// Variable that goes from 0 to 1 and reaches 1.0 when the
float shapePicking = saturate(normalizedWidth / breakingRange.x);
float amplitude = lerp(firstLobe * shapePicking, secondLobe, shapePicking);
if (normalizedWidth >= breakingRange.x)
{
if (normalizedWidth <= breakingRange.y)
{
float range = BreakingRegionFactor(breakingRange, normalizedWidth);
amplitude *= lerp(1.0, 0.3, range);
}
else
{
float range = (normalizedWidth - breakingRange.y) / (1.0 - breakingRange.y);
amplitude *= lerp(0.3, 0.0, range);
}
}
return amplitude;
}
float EvaluateSineWaveActivation(uint bellIndex, uint skippedWaves)
{
// Based on the requested frequency, return the activation function
return bellIndex % skippedWaves == 0 ? 1.0 : 0.0;
}
void EvaluateBoxAmplitude_float(float2 uv, float2 blendRegion, bool cubicBlend, out float amplitude)
{
// Apply the edge attenuation
float2 distanceToEdges = 1 - abs(uv * 2 - 1);
if (blendRegion.x != 0.0f) distanceToEdges.x /= blendRegion.x;
if (blendRegion.y != 0.0f) distanceToEdges.y /= blendRegion.y;
float2 lerpFactor = saturate(distanceToEdges);
if (cubicBlend) lerpFactor *= lerpFactor;
amplitude = min(lerpFactor.x, lerpFactor.y);
}
void EvaluateBowWaveAmplitude_float(float2 uv, float elevation, out float amplitude)
{
float2 normPos = uv * 2 - 1;
float transitionSize = 0.1;
normPos.y = normPos.y * 0.5 + 0.5;
float2 parabolaPos = normPos;
parabolaPos.y -= transitionSize;
float dist = sdParabola(parabolaPos, 1);
if (dist > 0.0)
{
float transition = smoothstep(0, 1, saturate(dist / transitionSize));
amplitude = lerp(elevation, 0.0, transition);
}
else
{
float transition = smoothstep(0, 1, saturate(-dist / (transitionSize)));
amplitude = lerp(elevation, 1, transition);
}
// Apply attenuation so that the tail has less deformation with a cubic profile
float lengthAttenuation = (1.0 - saturate(normPos.y));
amplitude *= lengthAttenuation * lengthAttenuation;
}
float EvaluateWaveBlendAttenuation(float2 blendRegion, float2 breakingRange, float2 positionOS)
{
float2 region = EvaluateBlendRegion(blendRegion, breakingRange, positionOS);
// Apply the edge attenuation
float leftFactor = saturate((positionOS.y) / region.x);
float rightFactor = saturate((1.0 - positionOS.y) / (1.0 - region.y));
float factor = leftFactor * rightFactor;
return factor * factor;
}
float EvaluateDeepFoamAmount(float2 deepFoamRange, float positionOS)
{
float rangeSize = max(deepFoamRange.y - deepFoamRange.x, 0.001);
float midPoint = deepFoamRange.x + rangeSize * 0.5;
float appartitionFactor = saturate((positionOS - deepFoamRange.x) / (midPoint - deepFoamRange.x));
float fadeFactor = saturate((deepFoamRange.y - positionOS) / (deepFoamRange.y - midPoint));
return appartitionFactor * fadeFactor;
}
float EvaluateSurfaceFoamAmount(float2 blendRegion, float2 breakingRange, float2 positionOS)
{
// Evaluate the region where the foam happens
float2 region = EvaluateBlendRegion(blendRegion, breakingRange, positionOS);
// Wave breaking point
float positionInRange = saturate((positionOS.y - region.x) / (region.y - region.x));
float v = positionInRange > 0.5 ? 1.0 - positionInRange : positionInRange;
float breakingFactor = saturate(v / 0.2);
float appartitionFactor = saturate((positionOS.x - breakingRange.x) / 0.05);
float fadeFactor = saturate((breakingRange.y - positionOS.x) / 0.05);
return appartitionFactor * fadeFactor * breakingFactor;
}
void EvaluateShoreWaveDecal_float(float2 uv, float waveLength, uint skippedWaves, float2 waveBlend,
float waveOffset, float2 breakingRange, float2 deepFoamRange, out float amplitude, out float2 foam)
{
// Compute the overall position (takes into account wave speed and wave length)
float t = ((uv.x * 2 - 1) - waveOffset) / waveLength;
// Evaluate the wave data
WaveData waveData = EvaluateWaveData(t);
float waveActivation = EvaluateSineWaveActivation(waveData.bellIndex, skippedWaves);
float attenuation = EvaluateWaveBlendAttenuation(waveBlend, breakingRange, uv);
float noise2D = DeformerNoise2D(uv * 0.25);
// Evaluate the round lobe
float firstShape = ShoreWaveFirstShape(waveData);
// Evaluate the breaking lobe
float secondShape = ShoreWaveSecondShape(waveData);
// Start from the target amplitude
amplitude = (0.8 + 0.2 * noise2D) * 0.5;
// Blend the lobes
amplitude *= ShoreWaveBlendShapes(firstShape, secondShape, breakingRange, uv.x);
// Apply the edge attenuation and skip wave
amplitude *= waveActivation * attenuation;
// Define where the foam appears on the wave
float surfacefoamWaveLocation = saturate((waveData.position - 0.1) / 0.1) * (1.0 - saturate((waveData.position - 0.5) / 0.02));
float deepFoamWaveLocation = saturate((waveData.position - 0.1) / 0.1) * (1.0 - saturate((waveData.position - 0.3) / 0.1));
// Define what amount of foam appears
float deepfoamAmount = EvaluateDeepFoamAmount(deepFoamRange, uv.x);
float surfaceFoamAmount = EvaluateSurfaceFoamAmount(waveBlend, breakingRange, uv);
// Evaluate the perlin noise
float perlinNoise = 0.2 + noise2D;
// Combine to generate the foam
foam.x = surfaceFoamAmount * surfacefoamWaveLocation * 4;
foam.y = deepfoamAmount * deepFoamWaveLocation * 2;
foam *= perlinNoise * waveActivation * attenuation;
}
#endif // WATER_DECAL_UTILITIES_H