修改水

This commit is contained in:
2026-01-01 22:00:33 +08:00
parent 040a222bd6
commit 9ceffccd39
1800 changed files with 103929 additions and 139495 deletions

View File

@@ -0,0 +1,176 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Inspired by https://github.com/speps/GX-EncinoWaves
// First SIZE constant must match FFT_KERNEL_0_RESOLUTION in FFTCompute.cs
#pragma kernel ComputeFFT SIZE=8 PASSES=3 CHANNEL=x TX=8 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=8 PASSES=3 CHANNEL=y TX=1 TY=8 FINAL=1
#pragma kernel ComputeFFT SIZE=16 PASSES=4 CHANNEL=x TX=16 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=16 PASSES=4 CHANNEL=y TX=1 TY=16 FINAL=1
#pragma kernel ComputeFFT SIZE=32 PASSES=5 CHANNEL=x TX=32 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=32 PASSES=5 CHANNEL=y TX=1 TY=32 FINAL=1
#pragma kernel ComputeFFT SIZE=64 PASSES=6 CHANNEL=x TX=64 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=64 PASSES=6 CHANNEL=y TX=1 TY=64 FINAL=1
#pragma kernel ComputeFFT SIZE=128 PASSES=7 CHANNEL=x TX=128 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=128 PASSES=7 CHANNEL=y TX=1 TY=128 FINAL=1
#pragma kernel ComputeFFT SIZE=256 PASSES=8 CHANNEL=x TX=256 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=256 PASSES=8 CHANNEL=y TX=1 TY=256 FINAL=1
#pragma kernel ComputeFFT SIZE=512 PASSES=9 CHANNEL=x TX=512 TY=1 FINAL=0
#pragma kernel ComputeFFT SIZE=512 PASSES=9 CHANNEL=y TX=1 TY=512 FINAL=1
// Must match CASCADE_COUNT in FFTCompute.cs
#define CASCADE_COUNT 16
Texture2D<float2> _Crest_InputButterfly;
#if !FINAL
RWTexture2DArray<float2> _Crest_Output1;
RWTexture2DArray<float2> _Crest_Output2;
RWTexture2DArray<float2> _Crest_Output3;
#else
Texture2DArray<float2> _Crest_InputH;
Texture2DArray<float2> _Crest_InputX;
Texture2DArray<float2> _Crest_InputZ;
// Write zero to W to clear garbage.
RWTexture2DArray<float4> _Crest_Output;
#endif
groupshared float2 _Crest_IntermediatesH[SIZE];
groupshared float2 _Crest_ScratchH[SIZE];
groupshared float2 _Crest_IntermediatesX[SIZE];
groupshared float2 _Crest_ScratchX[SIZE];
groupshared float2 _Crest_IntermediatesZ[SIZE];
groupshared float2 _Crest_ScratchZ[SIZE];
// This was required for Intel machines, but not required for Apple Silicon.
#if defined(SHADER_API_METAL)
// reversebits function appears to not make it across to Metal
#if !defined(reversebits)
uint reversebits( uint x )
{
x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1);
x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2);
x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4);
x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8);
x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16);
return x;
}
#endif
#endif
void ButterflyPass(float2 butterfly, uint coord, uint passIndex, uint cascade)
{
uint indexA, indexB;
const uint offset = 1 << passIndex;
if ((coord / offset) % 2 == 1)
{
indexA = coord - offset;
indexB = coord;
}
else
{
indexA = coord;
indexB = coord + offset;
}
if (passIndex == 0)
{
indexA = reversebits(indexA) >> (32 - PASSES);
indexB = reversebits(indexB) >> (32 - PASSES);
}
const bool pingpong = (passIndex % 2) == 0;
float2 valueA_H, valueB_H;
float2 valueA_X, valueB_X;
float2 valueA_Z, valueB_Z;
if (pingpong)
{
valueA_H = _Crest_IntermediatesH[indexA];
valueB_H = _Crest_IntermediatesH[indexB];
valueA_X = _Crest_IntermediatesX[indexA];
valueB_X = _Crest_IntermediatesX[indexB];
valueA_Z = _Crest_IntermediatesZ[indexA];
valueB_Z = _Crest_IntermediatesZ[indexB];
}
else
{
valueA_H = _Crest_ScratchH[indexA];
valueB_H = _Crest_ScratchH[indexB];
valueA_X = _Crest_ScratchX[indexA];
valueB_X = _Crest_ScratchX[indexB];
valueA_Z = _Crest_ScratchZ[indexA];
valueB_Z = _Crest_ScratchZ[indexB];
}
const float2 weight = butterfly.xy;
const float2 weightedValueH = weight * valueB_H.r + weight.gr * valueB_H.g * float2(-1.0, 1.0);
const float2 weightedValueX = weight * valueB_X.r + weight.gr * valueB_X.g * float2(-1.0, 1.0);
const float2 weightedValueZ = weight * valueB_Z.r + weight.gr * valueB_Z.g * float2(-1.0, 1.0);
const float2 resultH = valueA_H + weightedValueH;
const float2 resultX = valueA_X + weightedValueX;
const float2 resultZ = valueA_Z + weightedValueZ;
if (pingpong)
{
_Crest_ScratchH[coord] = resultH;
_Crest_ScratchX[coord] = resultX;
_Crest_ScratchZ[coord] = resultZ;
}
else
{
_Crest_IntermediatesH[coord] = resultH;
_Crest_IntermediatesX[coord] = resultX;
_Crest_IntermediatesZ[coord] = resultZ;
}
}
float2 conj(float2 v)
{
return float2(v.x, -v.y);
}
[numthreads(TX,TY,1)]
void ComputeFFT(const uint3 id : SV_DispatchThreadID)
{
const uint coord = id.CHANNEL;
#if !FINAL
_Crest_IntermediatesH[coord] = conj(_Crest_Output1[id]);
_Crest_IntermediatesX[coord] = conj(_Crest_Output2[id]);
_Crest_IntermediatesZ[coord] = conj(_Crest_Output3[id]);
#else
_Crest_IntermediatesH[coord] = _Crest_InputH[id];
_Crest_IntermediatesX[coord] = _Crest_InputX[id];
_Crest_IntermediatesZ[coord] = _Crest_InputZ[id];
#endif
[unroll(PASSES)]
for (uint passIndex = 0; passIndex < PASSES; ++passIndex)
{
GroupMemoryBarrierWithGroupSync();
ButterflyPass(_Crest_InputButterfly[uint2(coord, passIndex)].xy, coord, passIndex, id.z);
}
GroupMemoryBarrierWithGroupSync();
const bool pingpong = (PASSES % 2) == 0;
const float2 resultH = pingpong ? _Crest_IntermediatesH[coord] : _Crest_ScratchH[coord];
const float2 resultX = pingpong ? _Crest_IntermediatesX[coord] : _Crest_ScratchX[coord];
const float2 resultZ = pingpong ? _Crest_IntermediatesZ[coord] : _Crest_ScratchZ[coord];
#if !FINAL
_Crest_Output1[id] = resultH;
_Crest_Output2[id] = resultX;
_Crest_Output3[id] = resultZ;
#else
const float sign = ((id.x + id.y) % 2) == 1 ? -1.0 : 1.0;
const float3 res = float3(sign * resultX.x, sign * resultH.x, sign * resultZ.x);
// Write zero to W to clear garbage.
_Crest_Output[id] = float4(sign * resultX.x, sign * resultH.x, sign * resultZ.x, 1.0);
#endif
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: a5caa5dfb6d2c4632b41493dc2ba74d0
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,263 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
// Inspired by https://github.com/speps/GX-EncinoWaves
#pragma kernel SpectrumInitalize
#pragma kernel SpectrumUpdate
#define INV2PI 0.15915494309f
#define PI4 0.33661977236f
#define INVPI2 0.63661977236f
#define HPI 1.57079632679f
#define PI 3.14159265358f
#define PI2 6.28318530717f
#define HSQRT2 0.70710678118f
// These must match corresponding constants in WaveSpectrum.cs
#define SPECTRUM_OCTAVE_COUNT 14.0
#define SPECTRUM_SMALLEST_WL_POW_2 -4.0
uint _Crest_Size;
float _Crest_WindSpeed;
float _Crest_Turbulence;
float _Crest_Gravity;
float _Crest_Period;
float _Crest_Alignment;
uint WangHash(uint seed)
{
seed = (seed ^ 61) ^ (seed >> 16);
seed *= 9;
seed = seed ^ (seed >> 4);
seed *= 0x27d4eb2d;
seed = seed ^ (seed >> 15);
return seed;
}
uint Rand(inout uint rngState)
{
rngState ^= (rngState << 13);
rngState ^= (rngState >> 17);
rngState ^= (rngState << 5);
return rngState;
}
float RandFloat(inout uint rngState)
{
return Rand(rngState) / 4294967296.0f;
}
float RandGauss(inout uint rngState)
{
float u1 = RandFloat(rngState);
float u2 = RandFloat(rngState);
if (u1 < 1e-6f)
u1 = 1e-6f;
return sqrt(-2.0f * log(u1)) * cos(PI2 * u2);
}
void DeepDispersion(float k, out float w, out float dwdk)
{
w = sqrt(abs(_Crest_Gravity * k));
// Allow FFT to loop in time
if( _Crest_Period > 0.0 )
{
float thisPeriod = PI2 / w;
float loops = _Crest_Period / thisPeriod;
// Make sure loops integral number of times
loops = ceil( loops );
// Work our way back to frequency
thisPeriod = _Crest_Period / loops;
w = PI2 / thisPeriod;
}
dwdk = _Crest_Gravity / (2.0f * w);
}
float AlphaBetaSpectrum(float A, float B, float g, float w, float wm)
{
return
(A * g * g / pow(w, 5.0f)) *
exp(-B * pow(wm / w, 4.0f));
}
float PiersonMoskowitzSpectrum(float w)
{
float wm = 0.87f * _Crest_Gravity / _Crest_WindSpeed;
return AlphaBetaSpectrum(8.1e-3f, 1.291f, _Crest_Gravity, w, wm);
}
float PiersonMoskowitzWindTerm( float w )
{
float wm = 0.87f * _Crest_Gravity / _Crest_WindSpeed;
return exp( -1.291 * pow( wm / w, 4.0f ) );
}
float PosCosSquaredDirectionalSpreading( float cosTheta )
{
// Aligned waves.
float alignment;
{
float minWeight = lerp(1.0, 0.1, _Crest_Alignment);
float wt = max(cosTheta, minWeight);
wt *= lerp(0.25, 1.5, _Crest_Alignment);
float power = lerp(1.0, 16.0, _Crest_Alignment);
// Needs 2 to match current settings.
alignment = wt * INVPI2 * pow(abs(cosTheta), power) * PI;
}
float turbulence;
{
if (cosTheta > 0.0)
{
turbulence = lerp(INVPI2 * (cosTheta * cosTheta), PI4, _Crest_Turbulence);
}
else
{
turbulence = PI4 * _Crest_Turbulence;
}
}
// Integrate alignment and turbulence.
return lerp(turbulence, alignment, _Crest_Alignment * (1.0 - _Crest_Turbulence));
}
RWTexture2DArray<float4> _Crest_ResultInit;
Texture2D<float> _Crest_SpectrumControls;
SamplerState linear_clamp_sampler;
float2 _Crest_WindDir;
[numthreads(8,8,1)]
void SpectrumInitalize(uint3 id : SV_DispatchThreadID)
{
const int2 center = _Crest_Size.xx / 2;
const int2 coord = id.xy - center;
if( coord.x == 0 && coord.y == 0 )
{
_Crest_ResultInit[id] = 0.0;
return;
}
uint depth;
{
uint width, height;
_Crest_ResultInit.GetDimensions( width, height, depth );
}
uint maxCoord = int( max( abs( coord.x ), abs( coord.y ) ) );
// Matches variable with same name in ShapeFFT.cs
uint WAVE_SAMPLE_FACTOR = 8;
// If not largest cascade which will get all wavelengths, then limit
// so we split range of frequencies with no overlaps.
// The check on maxCoord below looks pretty magic. It is optimised version of:
// uint samplesPerWave = _Crest_Size / WAVE_SAMPLE_FACTOR;
// Too low wavelength (maxCoord < _Crest_Size / (2 * samplesPerWave) ||
// Too high wavelength maxCoord >= _Crest_Size / samplesPerWave)
if ( id.z < (depth - 1) &&
// Too low wavelength
maxCoord < WAVE_SAMPLE_FACTOR / 2 ||
// Too high wavelength
maxCoord >= WAVE_SAMPLE_FACTOR
)
{
_Crest_ResultInit[id] = 0.0;
return;
}
const float worldSize = 0.5f * (1 << id.z);
// Find wave vector and number
const float2 k = PI2 * coord / worldSize;
const float kMag = length(k);
// Init seed. rngState was _RngState (file level variable), but GameCore platforms does not allow assigning to them.
// See issue #856 for more information: https://github.com/wave-harmonic/crest/issues/856
uint rngState = WangHash(id.z * _Crest_Size * _Crest_Size + id.y * _Crest_Size + id.x);
// Dispersion
float w; float dwdk;
DeepDispersion(kMag, w, dwdk);
// Spectrum - use power values from users spectrum, but borrow wind term from PM
const float wavelength = PI2 / kMag;
const float octaveIndex = log2( wavelength ) - SPECTRUM_SMALLEST_WL_POW_2;
const float2 spectrumUV = float2((octaveIndex + 0.5) / SPECTRUM_OCTAVE_COUNT, 0.5);
const float spectrum = _Crest_SpectrumControls.SampleLevel( linear_clamp_sampler, spectrumUV, 0.0 ) *
PiersonMoskowitzWindTerm( w );
float deltaSPos = spectrum;
float deltaSNeg = spectrum;
// Directional spreading
const float cosTheta = dot( k, _Crest_WindDir ) / kMag;
deltaSPos *= PosCosSquaredDirectionalSpreading( cosTheta );
deltaSNeg *= PosCosSquaredDirectionalSpreading( -cosTheta );
const float dK = PI2 / worldSize;
deltaSPos *= (dK * dK) * dwdk / kMag;
deltaSNeg *= (dK * dK) * dwdk / kMag;
// Amplitude
const float ampPos = RandGauss(rngState) * sqrt(abs(deltaSPos) * 2.0f);
const float ampNeg = RandGauss(rngState) * sqrt(abs(deltaSNeg) * 2.0f);
// Output
const float phasePos = RandFloat(rngState) * PI2;
const float phaseNeg = RandFloat(rngState) * PI2;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const float spiceyMultiplier = 1.5;
_Crest_ResultInit[id] = float4(ampPos * float2(cos(phasePos), -sin(phasePos)), ampNeg * float2(cos(phaseNeg), -sin(phaseNeg)))
* spiceyMultiplier;
}
float _Crest_Time;
float _Crest_Chop;
Texture2DArray<float4> _Crest_Init0;
RWTexture2DArray<float2> _Crest_ResultHeight;
RWTexture2DArray<float2> _Crest_ResultDisplaceX;
RWTexture2DArray<float2> _Crest_ResultDisplaceZ;
float2 cmul(float2 lhs, float2 rhs)
{
return float2(
lhs.x * rhs.x - lhs.y * rhs.y,
lhs.x * rhs.y + lhs.y * rhs.x
);
}
[numthreads(8, 8, 1)]
void SpectrumUpdate(uint3 id : SV_DispatchThreadID)
{
const int2 center = _Crest_Size.xx / 2;
const int2 coord = id.xy - center;
// Find wave vector and number
const float worldSize = 0.5 * (1 << id.z);
const float2 k = PI2 * coord / worldSize;
const float kMag = length(k);
// Dispersion
float w; float dwdk;
DeepDispersion(kMag, w, dwdk);
// Advance time
float sw; float cw;
sincos(w * _Crest_Time, sw, cw);
const float2 fwd = float2(cw, -sw);
const float2 bkwd = float2(cw, sw);
const float4 h0 = _Crest_Init0[id];
const float2 h = cmul(h0.xy, fwd) + cmul(h0.zw, bkwd);
_Crest_ResultHeight[id] = h;
_Crest_ResultDisplaceX[id] = _Crest_Chop * float2(-h.y * k.x, h.x * k.x) / (kMag + 0.00001f);
_Crest_ResultDisplaceZ[id] = _Crest_Chop * float2(-h.y * k.y, h.x * k.y) / (kMag + 0.00001f);
}

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d493ec731bb6e43dfac22cc5921d31e3
ComputeShaderImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant: