forked from BilalY/Rasagar
400 lines
20 KiB
C#
400 lines
20 KiB
C#
using System;
|
|
using Unity.Burst;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Jobs;
|
|
using Unity.Mathematics;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using static Unity.Mathematics.math;
|
|
|
|
namespace UnityEngine.Rendering.HighDefinition
|
|
{
|
|
partial class WaterSystem
|
|
{
|
|
static int SignedMod(int x, int m)
|
|
{
|
|
int r = x % m;
|
|
return r < 0 ? r + m : r;
|
|
}
|
|
|
|
static int HandleWrapMode(int coord, int resolution, TextureWrapMode wrapMode)
|
|
{
|
|
// Only handle repeat and clamp because heh
|
|
if (wrapMode == TextureWrapMode.Repeat)
|
|
return SignedMod(coord, resolution);
|
|
return Mathf.Clamp(coord, 0, resolution - 1);
|
|
}
|
|
|
|
// This function does a "repeat" load
|
|
static T LoadTexture2DArray<T>(NativeArray<T> textureRawBuffer, int2 coord, int sliceIndex, int resolution) where T : struct
|
|
{
|
|
int2 repeatCoord = coord;
|
|
repeatCoord.x = SignedMod(repeatCoord.x, resolution);
|
|
repeatCoord.y = SignedMod(repeatCoord.y, resolution);
|
|
int bandOffset = resolution * resolution * sliceIndex;
|
|
return textureRawBuffer[repeatCoord.x + repeatCoord.y * resolution + bandOffset];
|
|
}
|
|
|
|
static float4 LoadTexture2D(NativeArray<uint> textureRawBuffer, TextureWrapMode wrapModeU, TextureWrapMode wrapModeV, int2 coord, int2 resolution)
|
|
{
|
|
int2 repeatCoord = coord;
|
|
repeatCoord.x = HandleWrapMode(repeatCoord.x, resolution.x, wrapModeU);
|
|
repeatCoord.y = HandleWrapMode(repeatCoord.y, resolution.y, wrapModeV);
|
|
int tapIndex = repeatCoord.x + repeatCoord.y * resolution.x;
|
|
uint packedData = textureRawBuffer[tapIndex];
|
|
return float4(packedData & 0xff, (packedData >> 8) & 0xff, (packedData >> 16) & 0xff, (packedData >> 24) & 0xff) / 255.0f;
|
|
}
|
|
|
|
static float2 LoadTexture2D(NativeArray<short> textureRawBuffer, int2 coord, int2 resolution)
|
|
{
|
|
int2 repeatCoord = coord;
|
|
repeatCoord.x = SignedMod(repeatCoord.x, resolution.x);
|
|
repeatCoord.y = SignedMod(repeatCoord.y, resolution.y);
|
|
int tapIndex = repeatCoord.x + repeatCoord.y * resolution.x;
|
|
short packedData = textureRawBuffer[tapIndex];
|
|
return float2(packedData & 0xff, (packedData >> 8) & 0xff) / 255.0f;
|
|
}
|
|
|
|
static float LoadTexture2D(NativeArray<half> textureRawBuffer, TextureWrapMode wrapModeU, TextureWrapMode wrapModeV, int2 coord, int2 resolution)
|
|
{
|
|
int2 repeatCoord = coord;
|
|
repeatCoord.x = HandleWrapMode(repeatCoord.x, resolution.x, wrapModeU);
|
|
repeatCoord.y = HandleWrapMode(repeatCoord.y, resolution.y, wrapModeV);
|
|
int tapIndex = repeatCoord.x + repeatCoord.y * resolution.x;
|
|
return textureRawBuffer[tapIndex];
|
|
}
|
|
|
|
static float LoadTexture2D(NativeArray<half> textureRawBuffer, int2 coord, int2 resolution)
|
|
{
|
|
int2 repeatCoord = coord;
|
|
repeatCoord.x = SignedMod(repeatCoord.x, resolution.x);
|
|
repeatCoord.y = SignedMod(repeatCoord.y, resolution.y);
|
|
int tapIndex = repeatCoord.x + repeatCoord.y * resolution.x;
|
|
return textureRawBuffer[tapIndex];
|
|
}
|
|
|
|
static void PrepareCoordinates(float2 uv, int2 resolution, out int2 tapCoord, out float2 fract)
|
|
{
|
|
float2 unnormalized = (uv * resolution) - 0.5f;
|
|
tapCoord = (int2)floor(floor(unnormalized) + 0.5f);
|
|
fract = frac(unnormalized);
|
|
}
|
|
|
|
static float4 SampleTexture2DArrayBilinear(NativeArray<float4> textureBuffer, float2 uvCoord, int sliceIndex, int resolution)
|
|
{
|
|
// Convert the position from uv to floating pixel coordinates (for the bilinear interpolation)
|
|
PrepareCoordinates(uvCoord, resolution, out int2 currentTapCoord, out float2 fract);
|
|
|
|
// Read the four samples we want
|
|
float4 p0 = LoadTexture2DArray(textureBuffer, currentTapCoord, sliceIndex, resolution);
|
|
float4 p1 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(1, 0), sliceIndex, resolution);
|
|
float4 p2 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(0, 1), sliceIndex, resolution);
|
|
float4 p3 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(1, 1), sliceIndex, resolution);
|
|
|
|
// Do the bilinear interpolation
|
|
float4 i0 = lerp(p0, p1, fract.x);
|
|
float4 i1 = lerp(p2, p3, fract.x);
|
|
return lerp(i0, i1, fract.y);
|
|
}
|
|
|
|
static float4 SampleTexture2DArrayBilinear(NativeArray<half4> textureBuffer, float2 uvCoord, int sliceIndex, int resolution)
|
|
{
|
|
// Convert the position from uv to floating pixel coordinates (for the bilinear interpolation)
|
|
PrepareCoordinates(uvCoord, resolution, out int2 currentTapCoord, out float2 fract);
|
|
|
|
// Read the four samples we want
|
|
float4 p0 = LoadTexture2DArray(textureBuffer, currentTapCoord, sliceIndex, resolution);
|
|
float4 p1 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(1, 0), sliceIndex, resolution);
|
|
float4 p2 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(0, 1), sliceIndex, resolution);
|
|
float4 p3 = LoadTexture2DArray(textureBuffer, currentTapCoord + new int2(1, 1), sliceIndex, resolution);
|
|
|
|
// Do the bilinear interpolation
|
|
float4 i0 = lerp(p0, p1, fract.x);
|
|
float4 i1 = lerp(p2, p3, fract.x);
|
|
return lerp(i0, i1, fract.y);
|
|
}
|
|
|
|
static float4 SampleTexture2DBilinear(NativeArray<uint> textureBuffer, float2 uvCoord, int2 resolution, TextureWrapMode wrapModeU, TextureWrapMode wrapModeV)
|
|
{
|
|
// Convert the position from uv to floating pixel coordinates (for the bilinear interpolation)
|
|
PrepareCoordinates(uvCoord, resolution, out int2 currentTapCoord, out float2 fract);
|
|
|
|
// Read the four samples we want
|
|
float4 p0 = LoadTexture2D(textureBuffer, wrapModeU, wrapModeV, currentTapCoord, resolution);
|
|
float4 p1 = LoadTexture2D(textureBuffer, wrapModeU, wrapModeV, currentTapCoord + new int2(1, 0), resolution);
|
|
float4 p2 = LoadTexture2D(textureBuffer, wrapModeU, wrapModeV, currentTapCoord + new int2(0, 1), resolution);
|
|
float4 p3 = LoadTexture2D(textureBuffer, wrapModeU, wrapModeV, currentTapCoord + new int2(1, 1), resolution);
|
|
|
|
// Do the bilinear interpolation
|
|
float4 i0 = lerp(p0, p1, fract.x);
|
|
float4 i1 = lerp(p2, p3, fract.x);
|
|
return lerp(i0, i1, fract.y);
|
|
}
|
|
|
|
static float SampleTexture2DBilinear(NativeArray<half> textureBuffer, float2 uvCoord, int2 resolution)
|
|
{
|
|
// Convert the position from uv to floating pixel coordinates (for the bilinear interpolation)
|
|
PrepareCoordinates(uvCoord, resolution, out int2 currentTapCoord, out float2 fract);
|
|
|
|
// Read the four samples we want
|
|
float p0 = LoadTexture2D(textureBuffer, currentTapCoord, resolution);
|
|
float p1 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(1, 0), resolution);
|
|
float p2 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(0, 1), resolution);
|
|
float p3 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(1, 1), resolution);
|
|
|
|
// Do the bilinear interpolation
|
|
float i0 = lerp(p0, p1, fract.x);
|
|
float i1 = lerp(p2, p3, fract.x);
|
|
return lerp(i0, i1, fract.y);
|
|
}
|
|
|
|
static float2 SampleTexture2DBilinear_float2(NativeArray<short> textureBuffer, float2 uvCoord, int2 resolution)
|
|
{
|
|
// Convert the position from uv to floating pixel coordinates (for the bilinear interpolation)
|
|
PrepareCoordinates(uvCoord, resolution, out int2 currentTapCoord, out float2 fract);
|
|
|
|
// Read the four samples we want
|
|
float2 p0 = LoadTexture2D(textureBuffer, currentTapCoord, resolution);
|
|
float2 p1 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(1, 0), resolution);
|
|
float2 p2 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(0, 1), resolution);
|
|
float2 p3 = LoadTexture2D(textureBuffer, currentTapCoord + new int2(1, 1), resolution);
|
|
|
|
// Do the bilinear interpolation
|
|
float2 i0 = lerp(p0, p1, fract.x);
|
|
float2 i1 = lerp(p2, p3, fract.x);
|
|
return lerp(i0, i1, fract.y);
|
|
}
|
|
|
|
static float2 EvaluateWaterGroup0CurrentUV(in WaterSimSearchData wsd, float2 currentUV)
|
|
{
|
|
if (wsd.decalWorkflow)
|
|
{
|
|
float3 positionAWS = mul(wsd.rendering.waterToWorldMatrix, float4(currentUV.x, 0, currentUV.y, 1.0f)).xyz;
|
|
return EvaluateDecalUV(wsd, positionAWS);
|
|
}
|
|
|
|
return float2(currentUV.x - wsd.group0CurrentRegionOffset.x, currentUV.y + wsd.group0CurrentRegionOffset.y) * wsd.group0CurrentRegionScale + 0.5f;
|
|
}
|
|
|
|
static float2 EvaluateWaterGroup1CurrentUV(in WaterSimSearchData wsd, float2 currentUV)
|
|
{
|
|
if (wsd.decalWorkflow)
|
|
{
|
|
float3 positionAWS = mul(wsd.rendering.waterToWorldMatrix, float4(currentUV.x, 0, currentUV.y, 1.0f)).xyz;
|
|
return EvaluateDecalUV(wsd, positionAWS);
|
|
}
|
|
|
|
return float2(currentUV.x - wsd.group1CurrentRegionOffset.x, currentUV.y + wsd.group1CurrentRegionOffset.y) * wsd.group1CurrentRegionOffset + 0.5f;
|
|
}
|
|
|
|
static float ConvertAngle_0_2PI(float angle)
|
|
{
|
|
angle = angle % (2.0f * PI);
|
|
return angle < 0.0f ? angle + 2.0f * PI : angle;
|
|
}
|
|
|
|
static float ConvertAngle_NPI_PPI(float angle)
|
|
{
|
|
angle = angle % (2.0f * PI);
|
|
return angle > PI ? angle - 2.0f * PI : (angle < -PI ? angle + 2.0f * PI : angle);
|
|
}
|
|
|
|
static float EvaluateAngle(float3 cmpDir, float orientation, float influence)
|
|
{
|
|
float3 dir = float3(cmpDir.xy * 2.0f - 1.0f, cmpDir.z);
|
|
float angle = ConvertAngle_NPI_PPI(atan2(dir.y, dir.x) - orientation);
|
|
return angle * influence * dir.z;
|
|
}
|
|
|
|
struct CurrentData
|
|
{
|
|
public int quadrant;
|
|
public float proportion;
|
|
public float angle;
|
|
};
|
|
|
|
static void DecompressDirection(in WaterSimSearchData wsd, float3 cmpDir, float orientation, float influence, out CurrentData currentData)
|
|
{
|
|
float angle = EvaluateAngle(cmpDir, orientation, influence);
|
|
currentData.angle = angle < 0.0f ? angle + 2.0f * PI : angle;
|
|
float data = currentData.angle / WaterConsts.k_SectorSize;
|
|
float relativeAngle = frac(data);
|
|
currentData.quadrant = ((int)data % WaterConsts.k_NumSectors);
|
|
currentData.proportion = pow(currentData.quadrant % 2 == 0 ? relativeAngle : 1.0f - relativeAngle, 0.75f);
|
|
}
|
|
|
|
static void EvaluateGroup0CurrentData(in WaterSimSearchData wsd, float2 currentUV, out CurrentData currentData)
|
|
{
|
|
float3 largeDirection = SampleTexture2DBilinear(wsd.group0CurrentMap, EvaluateWaterGroup0CurrentUV(wsd, currentUV), wsd.group0CurrentMapResolution, wsd.group0CurrentMapWrapModeU, wsd.group0CurrentMapWrapModeV).xyz;
|
|
DecompressDirection(wsd, largeDirection, wsd.spectrum.groupOrientation.x * Mathf.Deg2Rad, wsd.group0CurrentMapInfluence, out currentData);
|
|
}
|
|
|
|
static void EvaluateGroup1CurrentData(in WaterSimSearchData wsd, float2 currentUV, out CurrentData currentData)
|
|
{
|
|
float3 ripplesDirection = SampleTexture2DBilinear(wsd.group1CurrentMap, EvaluateWaterGroup1CurrentUV(wsd, currentUV), wsd.group1CurrentMapResolution, wsd.group1CurrentMapWrapModeU, wsd.group1CurrentMapWrapModeV).xyz;
|
|
DecompressDirection(wsd, ripplesDirection, wsd.spectrum.groupOrientation.y * Mathf.Deg2Rad, wsd.group1CurrentMapInfluence, out currentData);
|
|
}
|
|
|
|
static void SwizzleSamplingCoordinates(float2 coord, int quadrant, NativeArray<float4> sectorData, out float4 tapCoord)
|
|
{
|
|
tapCoord = 0.0f;
|
|
int sectorIndex = quadrant + WaterConsts.k_SectorDataSamplingOffset;
|
|
float4 dir0 = sectorData[2 * sectorIndex];
|
|
float4 dir1 = sectorData[2 * sectorIndex + 1];
|
|
tapCoord.xy = float2(dot(coord.xy, dir0.xy), dot(coord.xy, dir0.zw));
|
|
tapCoord.zw = float2(dot(coord.xy, dir1.xy), dot(coord.xy, dir1.zw));
|
|
}
|
|
|
|
struct PatchSimData
|
|
{
|
|
public float2 uv;
|
|
public float blend;
|
|
public float4 swizzle;
|
|
}
|
|
|
|
struct WaterSimCoord
|
|
{
|
|
public PatchSimData data0;
|
|
public PatchSimData data1;
|
|
public PatchSimData data2;
|
|
}
|
|
|
|
static void ComputePatchSimData(in WaterSimSearchData wsd, float2 uv, int bandIdx, out PatchSimData simData)
|
|
{
|
|
simData.uv = (uv - OrientationToDirection(wsd.spectrum.patchOrientation[bandIdx]) * wsd.rendering.patchCurrentSpeed[bandIdx] * wsd.rendering.simulationTime) / wsd.spectrum.patchSizes[bandIdx];
|
|
simData.blend = 1.0f;
|
|
simData.swizzle = float4(1, 0, 0, 1);
|
|
}
|
|
|
|
static void ComputeWaterUVs(in WaterSimSearchData wsd, float2 uv, out WaterSimCoord simCoord)
|
|
{
|
|
ComputePatchSimData(wsd, uv, 0, out simCoord.data0);
|
|
ComputePatchSimData(wsd, uv, 1, out simCoord.data1);
|
|
ComputePatchSimData(wsd, uv, 2, out simCoord.data2);
|
|
}
|
|
|
|
static void FillPatchData(int patchGroup,
|
|
in PatchSimData largeCoord, in PatchSimData ripplesCoord,
|
|
in CurrentData largeCurrent, in CurrentData ripplesCurrent,
|
|
in WaterSectorData largeSector, in WaterSectorData ripplesSector,
|
|
bool firstPass,
|
|
out PatchSimData simData)
|
|
{
|
|
if (patchGroup == 0)
|
|
{
|
|
simData.uv = largeCoord.uv;
|
|
simData.blend = firstPass ? 1.0f - largeCurrent.proportion : largeCurrent.proportion;
|
|
simData.swizzle = firstPass ? largeSector.dir0 : largeSector.dir1;
|
|
}
|
|
else
|
|
{
|
|
simData.uv = ripplesCoord.uv;
|
|
simData.blend = firstPass ? 1.0f - ripplesCurrent.proportion : ripplesCurrent.proportion;
|
|
simData.swizzle = firstPass ? ripplesSector.dir0 : ripplesSector.dir1;
|
|
}
|
|
}
|
|
|
|
static void AggregateWaterSimCoords(in WaterSimSearchData wsd,
|
|
in WaterSimCoord gr0SC, in WaterSimCoord gr1SC,
|
|
in CurrentData gr0CD, in CurrentData gr1CD,
|
|
bool firstPass, out WaterSimCoord simCoord)
|
|
{
|
|
// Grab the sector data for both groups
|
|
WaterSectorData gr0SD, gr1SD;
|
|
gr0SD.dir0 = wsd.sectorData[2 * (gr0CD.quadrant + WaterConsts.k_SectorDataOtherOffset)];
|
|
gr0SD.dir1 = wsd.sectorData[2 * (gr0CD.quadrant + WaterConsts.k_SectorDataOtherOffset) + 1];
|
|
gr1SD.dir0 = wsd.sectorData[2 * (gr1CD.quadrant + WaterConsts.k_SectorDataOtherOffset)];
|
|
gr1SD.dir1 = wsd.sectorData[2 * (gr1CD.quadrant + WaterConsts.k_SectorDataOtherOffset) + 1];
|
|
|
|
// Pick the right group data
|
|
FillPatchData(wsd.spectrum.patchGroup[0], gr0SC.data0, gr1SC.data0, gr0CD, gr1CD, gr0SD, gr1SD, firstPass, out simCoord.data0);
|
|
FillPatchData(wsd.spectrum.patchGroup[1], gr0SC.data1, gr1SC.data1, gr0CD, gr1CD, gr0SD, gr1SD, firstPass, out simCoord.data1);
|
|
FillPatchData(wsd.spectrum.patchGroup[2], gr0SC.data2, gr1SC.data2, gr0CD, gr1CD, gr0SD, gr1SD, firstPass, out simCoord.data2);
|
|
}
|
|
|
|
static void AddBandContribution(in WaterSimSearchData wsd, in PatchSimData data, int bandIdx, ref float2 horizontalDisplacement, ref float3 verticalDisplacements)
|
|
{
|
|
float3 rawDisplacement = wsd.cpuSimulation ? SampleTexture2DArrayBilinear(wsd.displacementDataCPU, data.uv, bandIdx, wsd.simulationRes).xyz :
|
|
SampleTexture2DArrayBilinear(wsd.displacementDataGPU, data.uv, bandIdx, wsd.simulationRes).xyz;
|
|
rawDisplacement *= wsd.rendering.patchAmplitudeMultiplier[bandIdx] * data.blend;
|
|
|
|
horizontalDisplacement += float2(dot(rawDisplacement.yz, data.swizzle.xy), dot(rawDisplacement.yz, data.swizzle.zw));
|
|
verticalDisplacements[bandIdx] = rawDisplacement.x;
|
|
|
|
}
|
|
|
|
static void EvaluateWaterSimulation(in WaterSimSearchData wsd, WaterSimCoord sc, out float2 horizontalDisplacement, out float3 verticalDisplacements)
|
|
{
|
|
horizontalDisplacement = 0.0f;
|
|
verticalDisplacements = 0.0f;
|
|
|
|
AddBandContribution(in wsd, in sc.data0, 0, ref horizontalDisplacement, ref verticalDisplacements);
|
|
|
|
if (wsd.activeBandCount > 1)
|
|
AddBandContribution(in wsd, in sc.data1, 1, ref horizontalDisplacement, ref verticalDisplacements);
|
|
|
|
if (wsd.activeBandCount > 2)
|
|
AddBandContribution(in wsd, in sc.data2, 2, ref horizontalDisplacement, ref verticalDisplacements);
|
|
}
|
|
|
|
static float2 EvaluateDecalUV(in WaterSimSearchData wsd, float3 positionAWS)
|
|
{
|
|
return (positionAWS.xz - wsd.decalRegionCenter) * wsd.decalRegionScale + 0.5f;
|
|
}
|
|
|
|
static float3 EvaluateWaterMask(in WaterSimSearchData wsd, float3 positionAWS)
|
|
{
|
|
float3 waterMask = 1.0f;
|
|
if (wsd.activeMask)
|
|
{
|
|
if (wsd.decalWorkflow)
|
|
{
|
|
float2 maskUV = EvaluateDecalUV(wsd, positionAWS);
|
|
waterMask = all(maskUV == saturate(maskUV)) ? SampleTexture2DBilinear(wsd.maskBuffer, maskUV, wsd.maskResolution, TextureWrapMode.Clamp, TextureWrapMode.Clamp).xyz : 1;
|
|
}
|
|
else
|
|
{
|
|
float2 maskUV = RotateUV(wsd, positionAWS.xz - wsd.maskOffset) * wsd.maskScale + 0.5f;
|
|
waterMask = SampleTexture2DBilinear(wsd.maskBuffer, maskUV, wsd.maskResolution, wsd.maskWrapModeU, wsd.maskWrapModeV).xyz;
|
|
waterMask = wsd.maskRemap.xxx + waterMask * wsd.maskRemap.yyy;
|
|
}
|
|
}
|
|
return waterMask;
|
|
}
|
|
|
|
static float3 ShuffleDisplacement(float3 displacement)
|
|
{
|
|
return float3(-displacement.y, displacement.x, -displacement.z);
|
|
}
|
|
|
|
static void EvaluateDisplacedPoints(float3 displacementC, float3 displacementR, float3 displacementU,
|
|
float normalization, float pixelSize,
|
|
out float3 p0, out float3 p1, out float3 p2)
|
|
{
|
|
p0 = displacementC * normalization;
|
|
p1 = displacementR * normalization + float3(pixelSize, 0, 0);
|
|
p2 = displacementU * normalization + float3(0, 0, pixelSize);
|
|
}
|
|
|
|
static float3 SurfaceGradientFromPerturbedNormal(float3 nrmVertexNormal, float3 v)
|
|
{
|
|
float3 n = nrmVertexNormal;
|
|
float s = 1.0f / max(float.Epsilon, abs(dot(n, v)));
|
|
return s * (dot(n, v) * n - v);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static float3 SurfaceGradientResolveNormal(float3 nrmVertexNormal, float3 surfGrad)
|
|
{
|
|
return normalize(nrmVertexNormal - surfGrad);
|
|
}
|
|
}
|
|
}
|