1#include "ClipmapTerrainTypes.h"
2#include "RenderContext.h"
3#include "WaveSpectrum.h"
5#include "Rendering/ITextures.h"
6#include "Rendering/IRenderTargets.h"
7#include "Rendering/IGraphicsDevice.h"
8#include "Rendering/IBuffers.h"
9#include "Rendering/CommandGroupAnnotation.h"
40 const float pi = float(M_PI);
41 const float twoPi = float(2.0*M_PI);
42 const float g = 9.80665f;
44 struct DispersionParameters{
46 float twoPiOverSideLength;
55 float waveSpectrumPhillips(
const float omega,
56 const float alpha = 0.0081f)
58 const float omega2 = omega*omega;
59 const float omega4 = omega2*omega2;
60 const float omega5 = omega4*omega;
62 return (alpha*g*g) / omega5;
65 float waveSpectrumPiersonMoskowitz(
const float omega,
67 const float alpha = 0.0081f)
69 const float omega_p_over_omega = omega_p / omega;
70 const float omega_p_over_omega4 = (omega_p_over_omega*omega_p_over_omega)*(omega_p_over_omega*omega_p_over_omega);
72 return waveSpectrumPhillips(omega, alpha)*exp(
float(-5.0 / 4.0)*omega_p_over_omega4);
75 float dispersionLonguetHiggins(
const float omega,
78 const float windWaveAngle)
82 float mu = omega <= omega_p ? 5.f : -2.5f;
83 float s_omega = 11.5f*pow(g / (omega_p*U10), 2.5f)*pow(omega / omega_p, mu);
84 assert(isfinite(s_omega));
98 float J = s_omega + 0.5f;
99 float gammaRatio = sqrt(J)*(1.f - 1.f / (8.f*J) + 1.f / (128.f*J*J) + 5.f / (1024.f*J*J*J) - 21.f / (32768.f*J*J*J*J));
100 float N_s_omega = float(1.0 / (2.0*sqrt(M_PI)))*(gammaRatio);
101 assert(isfinite(N_s_omega));
109 float D = N_s_omega*pow(max(0.0f, cos(0.5f*windWaveAngle)), 2.f*s_omega);
119 float waveNumberMute,
120 float waveNumberPass,
123 float dominantWavePeriod,
127 if (windDirection >= pi) {
128 windDirection -= twoPi;
130 if (windSpeed < 2.f) {
134 float dominantAngularVelocity = float(2.0*M_PI) / dominantWavePeriod;
137 glm::vec2 windDir(glm::cos(windDirection), glm::sin(windDirection));
139 for (
int j = 0; j < N; j++) {
140 for (
int i = 0; i < N; i++) {
141 if ((i == 0) && (j == 0)) {
146 ivec2 ij(i <= N / 2 ? i : i - N,
147 j <= N / 2 ? j : j - N);
149 const vec2 K = (twoPi / L)*vec2(ij);
152 float angularVelocity = sqrt(g*k);
155 float cosWindWaveAngle = std::max(-1.f, std::min(1.f, (1.f / k)*dot(K, windDir)));
156 float windWaveAngle = std::acos(cosWindWaveAngle);
157 assert(std::isfinite(windWaveAngle));
160 if (waveNumberMute != waveNumberPass) {
161 pass = max(0.f, min(1.f, (k - waveNumberMute) / (waveNumberPass - waveNumberMute)));
164 const float S = waveSpectrumPiersonMoskowitz(angularVelocity,
165 dominantAngularVelocity,
167 const float D = dispersionLonguetHiggins(angularVelocity,
168 dominantAngularVelocity,
172 const float chainFactor = (1.f / (2.f*k))*sqrt(g / k);
174 sumS += chainFactor*pass*S;
175 E[N*j + i] = chainFactor*pass*S*D;
181 float w = 1.f / sumS;
182 if (std::numeric_limits<float>::epsilon() < std::abs(scale)) {
185 for (
int i = 0; i < N*N; i++) {
190#define MINSTD_RAND_MAX ((1u<<31)-2u)
191static uint32_t minstd_rand(uint32_t &seed)
193 seed = ((uint64_t)seed * 48271u) % ((1u<<31)-1u);
196void Cogs::WaveSpectrum::createRandomizedWaveSpectrumInstance(std::vector<glm::vec2>& H,
197 const std::vector<float>& E,
201 for (
size_t j = 0; j < N; j++) {
202 for (
size_t i = 0; i < N; i++) {
206 float U0 = (float)((
double)(minstd_rand(seed) + 1) / (
double)(MINSTD_RAND_MAX + 2));
207 float U1 = (float)((2.0*M_PI*(
double)minstd_rand(seed)) / (
double)(MINSTD_RAND_MAX + 1));
210 complex<float> eta = sqrt(-2.f*log(U0))*complex<float>(cos(U1), sin(U1));
212 complex<float> res = sqrt(E[N*j + i] / 2.f) * eta;
214 H[N*j + i] = vec2(res.real(), res.imag());
219float Cogs::WaveSpectrum::setConditions(
const float tileExtent,
220 const float waveNumberMute,
221 const float waveNumberPass,
222 const float significantWavePeriod,
223 const float windSpeed,
228 this->tileExtent = tileExtent;
230 float significantWaveLength = (g*significantWavePeriod*significantWavePeriod) / (2.f*glm::pi<float>());
232 int harmonic = std::max(1,
static_cast<int>(std::round(std::log2(tileExtent / significantWaveLength))));
234 tileExtentAdjust = ((1 << harmonic)*significantWaveLength) / tileExtent;
236 float rv = createDirectionalWaveSpectrum(frequencyDomain.E,
238 tileExtentAdjust*tileExtent,
243 significantWavePeriod,
245 createRandomizedWaveSpectrumInstance(frequencyDomain.a, frequencyDomain.E, 42, N);
247 ITextures* textures = device->getTextures();
248 frequencyDomain.aTex = textures->loadTexture(
reinterpret_cast<unsigned char*
>(frequencyDomain.a.data()), N, N, TextureFormat::R32G32_FLOAT);
249 textures->annotate(frequencyDomain.aTex,
"Initial spectrum instance.");
254void Cogs::WaveSpectrum::initialize(IGraphicsDevice* device)
257 this->device = device;
259 gpgpuQuadRenderer.initialize(device);
260 fourierTransform.initialize(device, gpgpuQuadRenderer);
262 auto ie = device->getEffects();
268 disperse.effect = ie->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
269 "Terrain/WaveSpectrumDispersionPS.hlsl",
272 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
273 disperse.il = device->getBuffers()->loadInputLayout(&handle, 1, disperse.effect);
280 texturePack.effect = ie->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
281 "Terrain/OceanBuildTexPositionPS.hlsl",
284 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
285 texturePack.il = device->getBuffers()->loadInputLayout(&handle, 1, texturePack.effect);
290void Cogs::WaveSpectrum::setSize(
const int NLog2)
292 fourierTransform.setSize(NLog2);
297 std::vector<float> zeros(N*N);
298 frequencyDomain.E.resize(N*N);
299 frequencyDomain.a.resize(N*N);
301 ITextures* textures = device->getTextures();
302 IRenderTargets* renderTargets = device->getRenderTargets();
307 for (
int i = 0; i < 2; i++) {
308 phaseTex[i] = textures->loadTexture(
reinterpret_cast<uint8_t*
>(zeros.data()), N, N, TextureFormat::R32_FLOAT,
TextureFlags::RenderTarget);
313 frequencyDomain.dzdu_dzdv_Tex = textures->loadTexture(
nullptr, N, N, TextureFormat::R32G32B32A32_FLOAT,
TextureFlags::RenderTarget);
317 spatialDomain.dzdu_dzdv_Tex = textures->loadTexture(
nullptr, N, N, TextureFormat::R32G32B32A32_FLOAT,
TextureFlags::RenderTarget);
320 spatialDomain.xyTarget = renderTargets->createRenderTarget(spatialDomain.xyTex);
321 spatialDomain.zTarget = renderTargets->createRenderTarget(spatialDomain.zTex);
322 spatialDomain.dzduTarget = renderTargets->createRenderTarget(spatialDomain.dzdu_dzdv_Tex);
324 for (
int i = 0; i < 2; i++) {
325 TextureHandle texs[4] = {
327 frequencyDomain.xyTex,
328 frequencyDomain.zTex,
329 frequencyDomain.dzdu_dzdv_Tex
331 dispersionTarget[i] = renderTargets->createRenderTarget(texs, 4);
334 TextureHandle packedTexs[2] = {
336 packed.dxdu_dydv_dzdu_dzdvTex
338 packed.packTarget = renderTargets->createRenderTarget(packedTexs, 2);
342bool Cogs::WaveSpectrum::update(RenderContext& renderContext,
const float dt)
344 IContext* context = renderContext.context;
347 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::Disperse");
351 context->setViewport(0, 0,
float(N),
float(N));
352 context->setEffect(disperse.effect);
353 context->setInputLayout(disperse.il);
355 gpgpuQuadRenderer.bind(context);
357 context->setTexture(
"aTex", 0, frequencyDomain.aTex);
358 context->setTexture(
"phaseTex", 0, phaseTex[(frame + 1) & 1]);
361 MappedBuffer<DispersionParameters> constants(context, disperse.constantBuffer,
MapMode::WriteDiscard);
364 constants->twoPiOverSideLength = float(2.0*M_PI / (tileExtentAdjust*tileExtent));
368 context->setConstantBuffer(
"DispersionParameters", disperse.constantBuffer);
370 gpgpuQuadRenderer.draw(context);
374 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::iFFT");
376 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.xyTarget, frequencyDomain.xyTex,
true);
378 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.zTarget, frequencyDomain.zTex,
false);
380 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.dzduTarget, frequencyDomain.dzdu_dzdv_Tex,
true);
384 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::Pack");
387 context->setViewport(0, 0,
float(N),
float(N));
389 context->setEffect(texturePack.effect);
393 constants->N_minus_1 = N - 1;
394 constants->L_over_twoN = tileExtent / (2.f*N);
397 context->setConstantBuffer(
"PackParamters", texturePack.constantBuffer);
398 context->setTexture(
"waveXY", 0, spatialDomain.xyTex);
399 context->setTexture(
"waveZ", 1, spatialDomain.zTex);
400 context->setTexture(
"wavedZdu_dZdv", 2, spatialDomain.dzdu_dzdv_Tex);
402 gpgpuQuadRenderer.bind(context);
403 context->setInputLayout(texturePack.il);
404 gpgpuQuadRenderer.draw(context);
406 auto textures = device->getTextures();
407 textures->generateMipmaps(packed.xyzTex);
408 textures->generateMipmaps(packed.dxdu_dydv_dzdu_dzdvTex);
411 frame = (frame + 1) & 1;
static float createDirectionalWaveSpectrum(std::vector< float > &E, int N, float L, float freqPassZero, float freqPassOne, float windSpeed, float windDirection, float dominantWavePeriod, float scale=0.f)
@ Ready
The resource has loaded successfully and is ready for use.
std::vector< PreprocessorDefinition > PreprocessorDefinitions
A set of preprocessor definitions.
@ Write
The buffer can be mapped and written to by the CPU after creation.
@ ConstantBuffer
The buffer can be bound as input to effects as a constant buffer.
static const Handle_t InvalidHandle
Represents an invalid handle.
@ WriteDiscard
Write access. When unmapping the graphics system will discard the old contents of the resource.
@ RenderTarget
The texture can be used as a render target and drawn into.
@ GenerateMipMaps
The texture supports automatic mipmap generation performed by the graphics device.
@ Dynamic
Buffer will be loaded and modified with some frequency.