FFT y el desarrollo del juego (e)

FFT y el desarrollo del juego (e)

ondas analógicas, pueden entenderse como una pila de ondas sinusoidales superpuestas de dirección arbitraria, el espectro de onda sinusoidal (amplitud y fase) puede variar con el tiempo.

En primer lugar poner los resultados muestran:

Por iFFT, el espectro puede ser convertido a un ondas de campo alto grado.

\ [H (\ overrightarrow x, t) = \ sum _ {\ overrightarrow k} \ tilde h (\ overrightarrow k, t) e ^ {2 \ pi \ overrightarrow k \ cdot \ overrightarrow x} \]

Tenemos olas del dominio de la frecuencia de las ondas de herramientas de dominio de tiempo, por lo que las olas en el dominio de la frecuencia es ¿cómo es?

espectro de Philips (Phillips Spectrum)

Estadísticas por el mar, las olas pueden obtener el espectro cambia como una función del tiempo:

\ [\ Tilde h (\ overrightarrow k, t) = \ tilde {H 0} (\ overrightarrow k) e ^ {it \ sqrt {GK}} + \ tilde {H_0 ^ *} (- \ overrightarrow k) e ^ { -se \ sqrt {GK}} \]

\ [\ Begin {alineado} donde: & L es la distancia entre dos puntos espaciales vecinas \\ y k_x = \ frac {2 \ pi m} {L}, - \ frac {N} {2} \ leq m < \ frac {N} {2} \\ y k_z = \ frac {2 \ pi n} {L}, - \ frac {N} {2} \ leq n <\ frac {N} {2} \\ & \ overrightarrow k = (k_x, k_z) \\ & k = | \ overrightarrow k | \\ \\ además: & \ tilde {H_0} (\ overrightarrow k) = \ frac {1} {\ sqrt 2} (\ xi_r + \ xi_i ) \ sqrt {P_H (\ overrightarrow k)} \\ y P_H (\ overrightarrow k) = A \ frac {e ^ {- \ frac {g ^ 2} {k ^ 2V ^ 4}}} {k ^ 4} | \ overrightarrow k \ cdot \ overrightarrow \ omega | ^ 2 \\ \ overrightarrow \ omega: & viento \\ V: & Vientos \\ \ end {alineado} \]

Se puede ver, esta función es muy complejo enorme, pero la buena noticia es que podemos hacer que se ejecute en la GPU.

HLSL lograr

Obtener el espectro de las olas, se puede empezar a poner en práctica las manos, HLSL lograr lo siguiente:

#pragma kernel iFFT2x
#pragma kernel iFFT2y
#pragma kernel GenerateSpectrum
#pragma kernel GenerateTwinRandomGaussian
#pragma kernel GenerateSpectrumStepOne
#pragma kernel GenerateSpectrumStepTwo

static const uint FFT_STAGES = 8;
static const uint FFT_DIMENSION = 1 << FFT_STAGES;
static const uint FFT_BUTTERFLYS = FFT_DIMENSION >> 1;

static const float PI = 3.14159265;
groupshared float2 pingPongArray[FFT_DIMENSION * 2];

uint ReverseBits(uint index, uint count) {
	return reversebits(index) >> (32 - count);
}

float2 ComplexMultiply(float2 a, float2 b) {
	return float2(a.x * b.x - a.y * b.y, a.y * b.x + a.x * b.y);
}

void ButterFlyOnce(float2 input0, float2 input1, float2 twiddleFactor, out float2 output0, out float2 output1) {
	float2 t = ComplexMultiply(twiddleFactor, input1);
	output0 = input0 + t;
	output1 = input0 - t;
}

float2 Euler(float theta) {
	float2 ret;
	sincos(theta, ret.y, ret.x);
	return ret;
}

Texture2D<float2> SrcTex;
RWTexture2D<float2> DstTex;

void iFFT2(uint2 id, bool horizontal)
{
	uint butterFlyID = horizontal ? id.x : id.y;
	uint index0 = butterFlyID * 2;
	uint index1 = butterFlyID * 2 + 1;
	if (horizontal) {
		pingPongArray[index0] = SrcTex[uint2(ReverseBits(index0, FFT_STAGES), id.y)];
		pingPongArray[index1] = SrcTex[uint2(ReverseBits(index1, FFT_STAGES), id.y)];
	} else {
		pingPongArray[index0] = SrcTex[uint2(id.x, ReverseBits(index0, FFT_STAGES))];
		pingPongArray[index1] = SrcTex[uint2(id.x, ReverseBits(index1, FFT_STAGES))];
	}

	uint2 offset = uint2(0, FFT_DIMENSION);
	[unroll]
	for (uint s = 1; s <= FFT_STAGES; s++) {
		GroupMemoryBarrierWithGroupSync();
		// 每个stage中独立的FFT的宽度
		uint m = 1 << s;
		uint halfWidth = m >> 1;
		// 属于第几个iFFT
		uint nFFT = butterFlyID / halfWidth;
		// 在iFFT中属于第几个输入
		uint k = butterFlyID % halfWidth;
		index0 = k + nFFT * m;
		index1 = index0 + halfWidth;
		if (s != FFT_STAGES) {
			ButterFlyOnce(
				pingPongArray[offset.x + index0], pingPongArray[offset.x + index1],
				Euler(2 * PI * k / m),
				pingPongArray[offset.y + index0], pingPongArray[offset.y + index1]);
			offset.xy = offset.yx;
		} else {
			float2 output0;
			float2 output1;
			ButterFlyOnce(
				pingPongArray[offset.x + index0], pingPongArray[offset.x + index1],
				Euler(2 * PI * k / m),
				output0, output1);
			output0 /= FFT_DIMENSION;
			output1 /= FFT_DIMENSION;
			if (horizontal) {
				DstTex[uint2(index0, id.y)] = output0;
				DstTex[uint2(index1, id.y)] = output1;
			} else {
				DstTex[uint2(id.x, index0)] = output0;
				DstTex[uint2(id.x, index1)] = output1;
			}
		}
	}
}

[numthreads(FFT_BUTTERFLYS, 1, 1)]
void iFFT2x(uint3 id : SV_DispatchThreadID) {
	iFFT2(id.xy, true);
}

[numthreads(1, FFT_BUTTERFLYS, 1)]
void iFFT2y(uint3 id : SV_DispatchThreadID) {
	iFFT2(id.xy, false);
}

const static float G = 9.8;

float Pow2(float x) { return x * x; }
float Pow4(float x) { return x * x * x * x; }

float2 H0(float2 k_v, float k, float2 w, float V, float2 xi, float sqrtA) {
	// P_h(\overrightarrow k) = A\frac{e^{-1/(kL)^2}}{k^4} |\overrightarrow k \cdot \overrightarrow \omega| ^ 2
	// L = \frac{V^2}{g}
	float sqrtPh = sqrtA * exp(-Pow2(G / (k * V * V)) / 2) * abs(dot(k_v, w)) / Pow2(k);
	return xi * sqrtPh / sqrt(2);
}

float2 PhillipsSpectrum(
	float2 k_v,
	float sqrtA, // 常数
	float t, // 时间
	float2 w, // 风向
	float V, // 风速
	float2 xi) // 随机数
{
	// 这部分可以预计算
	float k = length(k_v);
	float2 h0 = H0(k_v, k, w, V, xi, sqrtA);
	float2 h0_adj = H0(-k_v, k, w, V, xi, sqrtA) * float2(1, -1);

	// \tilde{h_0}(\overrightarrow k) e^{it\sqrt{gk}} +	\tilde{ h_0^* }(-\overrightarrow k) e^ { -it\sqrt{ gk } }
	half tsqrtGk = t * sqrt(G * k);
	float2 h = ComplexMultiply(h0, Euler(tsqrtGk)) + ComplexMultiply(h0_adj, Euler(-tsqrtGk));

	h.x = isnan(h.x) ? 0 : h.x;
	h.y = isnan(h.y) ? 0 : h.y;
	return h;
}

float RadicalInverse_VdC(uint bits) {
	bits = (bits << 16u) | (bits >> 16u);
	bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
	bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
	bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
	bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
	return bits * 2.3283064365386963e-10f;
}

RWTexture2D<float2> SpectrumTex;
float _Time;
float _SqrtAmplitude;
float2 _WindDirection;
float _WindSpeed;
float _PatchLength;


[numthreads(4, 4, 1)]
void GenerateSpectrum(uint3 id : SV_DispatchThreadID) {
	float2 rand = float2(RadicalInverse_VdC(id.x + 1), RadicalInverse_VdC(id.y + 1));
	float2 xi;
	sincos(2 * PI * rand.y, xi.x, xi.y);
	xi *= sqrt(-2 * log(rand.x));

	float2 mn = (float2)id.xy - FFT_DIMENSION / 2;
	float L = _PatchLength;
	float2 k_v = 2 * PI * mn / L;

	SpectrumTex[id.xy] = 
		PhillipsSpectrum(k_v, _SqrtAmplitude, _Time, _WindDirection, _WindSpeed, xi);
}

RWTexture2D<float2> TwinRandomGaussianTexOutput;

[numthreads(4,4,1)]
void GenerateTwinRandomGaussian(uint3 id : SV_DispatchThreadID) {
	float2 rand = float2(RadicalInverse_VdC(id.x + 1), RadicalInverse_VdC(id.y + 1));
	float2 xi;
	sincos(2 * PI * rand.y, xi.x, xi.y);
	xi *= sqrt(-2 * log(rand.x));
	TwinRandomGaussianTexOutput[id.xy] = xi;
}

Texture2D<float2> TwinRandomGaussianTexInput;
RWTexture2D<float4> SpectrumStepOneOutput;

[numthreads(4,4,1)]
void GenerateSpectrumStepOne(uint3 id : SV_DispatchThreadID) {
	float sqrtA = _SqrtAmplitude;
	float2 w = _WindDirection;
	float V = _WindSpeed;

	float2 xi = TwinRandomGaussianTexInput[id.xy];

	float2 mn = (float2)id.xy - FFT_DIMENSION / 2;
	float L = _PatchLength;
	float2 k_v = 2 * PI * mn / L;

	float k = length(k_v);

	float2 h0 = H0(k_v, k, w, V, xi, sqrtA);
	float2 h0_adj = H0(-k_v, k, w, V, xi, sqrtA) * float2(1, -1);

	SpectrumStepOneOutput[id.xy] = float4(h0, h0_adj);
}

Texture2D<float4> SpectrumStepOneInput;
RWTexture2D<float2> SpectrumStepTwoOutput;

[numthreads(4, 4, 1)]
void GenerateSpectrumStepTwo(uint3 id : SV_DispatchThreadID) {
	float4 input = SpectrumStepOneInput[id.xy];
	float2 h0 = input.xy;
	float2 h0_adj = input.zw;

	float t = _Time;

	float2 mn = (float2)id.xy - FFT_DIMENSION / 2;
	float L = _PatchLength;
	float2 k_v = 2 * PI * mn / L;

	float k = length(k_v);

	half tsqrtGk = t * sqrt(G * k);
	float2 h = ComplexMultiply(h0, Euler(tsqrtGk)) + ComplexMultiply(h0_adj, Euler(-tsqrtGk));

	h.x = isnan(h.x) ? 0 : h.x;
	h.y = isnan(h.y) ? 0 : h.y;
	
	SpectrumStepTwoOutput[id.xy] = h;
}

Hay varios puntos a tener en cuenta en:

  1. Hammersley Secuencia originalmente quería utilizar para hacer el generador de números aleatorios en la GPU, pero este descubrimiento había algún patrón repetitivo, o en el lado de la CPU para hacerlo.
  2. generación Phillips Spectrum puede tomar tiempo parámetro t, porción precalculadas (GenerateSpectrumStepOne), reduciendo la cantidad de cálculo que hay que hacer en cada trama.

material de referencia

  1. TESSENDORF, J., 2001. Simulación de las aguas oceánicas. notas en SIGGRAPH (curso 47), ACM SIGGRAPH
  2. simulación mar FFT (a)

Supongo que te gusta

Origin www.cnblogs.com/hamwj1991/p/12638381.html
Recomendado
Clasificación