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"
39 const float pi = float(M_PI);
40 const float twoPi = float(2.0*M_PI);
41 const float g = 9.80665f;
43 struct DispersionParameters{
45 float twoPiOverSideLength;
54 float waveSpectrumPhillips(
const float omega,
55 const float alpha = 0.0081f)
57 const float omega2 = omega*omega;
58 const float omega4 = omega2*omega2;
59 const float omega5 = omega4*omega;
61 return (alpha*g*g) / omega5;
64 float waveSpectrumPiersonMoskowitz(
const float omega,
66 const float alpha = 0.0081f)
68 const float omega_p_over_omega = omega_p / omega;
69 const float omega_p_over_omega4 = (omega_p_over_omega*omega_p_over_omega)*(omega_p_over_omega*omega_p_over_omega);
71 return waveSpectrumPhillips(omega, alpha)*exp(
float(-5.0 / 4.0)*omega_p_over_omega4);
74 float dispersionLonguetHiggins(
const float omega,
77 const float windWaveAngle)
81 float mu = omega <= omega_p ? 5.f : -2.5f;
82 float s_omega = 11.5f*pow(g / (omega_p*U10), 2.5f)*pow(omega / omega_p, mu);
83 assert(isfinite(s_omega));
97 float J = s_omega + 0.5f;
98 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));
99 float N_s_omega = float(1.0 / (2.0*sqrt(M_PI)))*(gammaRatio);
100 assert(isfinite(N_s_omega));
108 float D = N_s_omega*pow(max(0.0f, cos(0.5f*windWaveAngle)), 2.f*s_omega);
118 float waveNumberMute,
119 float waveNumberPass,
122 float dominantWavePeriod,
126 if (windDirection >= pi) {
127 windDirection -= twoPi;
129 if (windSpeed < 2.f) {
133 float dominantAngularVelocity = float(2.0*M_PI) / dominantWavePeriod;
136 glm::vec2 windDir(glm::cos(windDirection), glm::sin(windDirection));
138 for (
int j = 0; j < N; j++) {
139 for (
int i = 0; i < N; i++) {
140 if ((i == 0) && (j == 0)) {
145 ivec2 ij(i <= N / 2 ? i : i - N,
146 j <= N / 2 ? j : j - N);
148 const vec2 K = (twoPi / L)*vec2(ij);
151 float angularVelocity = sqrt(g*k);
154 float cosWindWaveAngle = std::max(-1.f, std::min(1.f, (1.f / k)*dot(K, windDir)));
155 float windWaveAngle = std::acos(cosWindWaveAngle);
156 assert(std::isfinite(windWaveAngle));
159 if (waveNumberMute != waveNumberPass) {
160 pass = max(0.f, min(1.f, (k - waveNumberMute) / (waveNumberPass - waveNumberMute)));
163 const float S = waveSpectrumPiersonMoskowitz(angularVelocity,
164 dominantAngularVelocity,
166 const float D = dispersionLonguetHiggins(angularVelocity,
167 dominantAngularVelocity,
171 const float chainFactor = (1.f / (2.f*k))*sqrt(g / k);
173 sumS += chainFactor*pass*S;
174 E[N*j + i] = chainFactor*pass*S*D;
180 float w = 1.f / sumS;
181 if (std::numeric_limits<float>::epsilon() < std::abs(scale)) {
184 for (
int i = 0; i < N*N; i++) {
189#define MINSTD_RAND_MAX ((1u<<31)-2u)
190static uint32_t minstd_rand(uint32_t &seed)
192 seed = ((uint64_t)seed * 48271u) % ((1u<<31)-1u);
195void Cogs::WaveSpectrum::createRandomizedWaveSpectrumInstance(std::vector<glm::vec2>& H,
196 const std::vector<float>& E,
200 for (
size_t j = 0; j < N; j++) {
201 for (
size_t i = 0; i < N; i++) {
205 float U0 = (float)((
double)(minstd_rand(seed) + 1) / (
double)(MINSTD_RAND_MAX + 2));
206 float U1 = (float)((2.0*M_PI*(
double)minstd_rand(seed)) / (
double)(MINSTD_RAND_MAX + 1));
209 complex<float> eta = sqrt(-2.f*log(U0))*complex<float>(cos(U1), sin(U1));
211 complex<float> res = sqrt(E[N*j + i] / 2.f) * eta;
213 H[N*j + i] = vec2(res.real(), res.imag());
218float Cogs::WaveSpectrum::setConditions(
const float tileExtent,
219 const float waveNumberMute,
220 const float waveNumberPass,
221 const float significantWavePeriod,
222 const float windSpeed,
227 this->tileExtent = tileExtent;
229 float significantWaveLength = (g*significantWavePeriod*significantWavePeriod) / (2.f*glm::pi<float>());
231 int harmonic = std::max(1,
static_cast<int>(std::round(std::log2(tileExtent / significantWaveLength))));
233 tileExtentAdjust = ((1 << harmonic)*significantWaveLength) / tileExtent;
235 float rv = createDirectionalWaveSpectrum(frequencyDomain.E,
237 tileExtentAdjust*tileExtent,
242 significantWavePeriod,
244 createRandomizedWaveSpectrumInstance(frequencyDomain.a, frequencyDomain.E, 42, N);
246 ITextures* textures = device->getTextures();
247 frequencyDomain.aTex = textures->loadTexture(
reinterpret_cast<unsigned char*
>(frequencyDomain.a.data()), N, N, TextureFormat::R32G32_FLOAT);
248 textures->annotate(frequencyDomain.aTex,
"Initial spectrum instance.");
253void Cogs::WaveSpectrum::initialize(IGraphicsDevice* device)
256 this->device = device;
258 gpgpuQuadRenderer.initialize(device);
259 fourierTransform.initialize(device, gpgpuQuadRenderer);
261 auto ie = device->getEffects();
267 disperse.effect = ie->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
268 "Terrain/WaveSpectrumDispersionPS.hlsl",
270 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
271 disperse.il = device->getBuffers()->loadInputLayout(&handle, 1, disperse.effect);
278 texturePack.effect = ie->loadEffect(
"Terrain/GPGPUPassThroughVS.hlsl",
279 "Terrain/OceanBuildTexPositionPS.hlsl",
281 VertexFormatHandle handle = gpgpuQuadRenderer.vertexFormat();
282 texturePack.il = device->getBuffers()->loadInputLayout(&handle, 1, texturePack.effect);
287void Cogs::WaveSpectrum::setSize(
const int NLog2)
289 fourierTransform.setSize(NLog2);
294 std::vector<float> zeros(N*N);
295 frequencyDomain.E.resize(N*N);
296 frequencyDomain.a.resize(N*N);
298 ITextures* textures = device->getTextures();
299 IRenderTargets* renderTargets = device->getRenderTargets();
304 for (
int i = 0; i < 2; i++) {
305 phaseTex[i] = textures->loadTexture(
reinterpret_cast<uint8_t*
>(zeros.data()), N, N, TextureFormat::R32_FLOAT,
TextureFlags::RenderTarget);
310 frequencyDomain.dzdu_dzdv_Tex = textures->loadTexture(
nullptr, N, N, TextureFormat::R32G32B32A32_FLOAT,
TextureFlags::RenderTarget);
314 spatialDomain.dzdu_dzdv_Tex = textures->loadTexture(
nullptr, N, N, TextureFormat::R32G32B32A32_FLOAT,
TextureFlags::RenderTarget);
317 spatialDomain.xyTarget = renderTargets->createRenderTarget(spatialDomain.xyTex);
318 spatialDomain.zTarget = renderTargets->createRenderTarget(spatialDomain.zTex);
319 spatialDomain.dzduTarget = renderTargets->createRenderTarget(spatialDomain.dzdu_dzdv_Tex);
321 for (
int i = 0; i < 2; i++) {
322 TextureHandle texs[4] = {
324 frequencyDomain.xyTex,
325 frequencyDomain.zTex,
326 frequencyDomain.dzdu_dzdv_Tex
328 dispersionTarget[i] = renderTargets->createRenderTarget(texs, 4);
331 TextureHandle packedTexs[2] = {
333 packed.dxdu_dydv_dzdu_dzdvTex
335 packed.packTarget = renderTargets->createRenderTarget(packedTexs, 2);
339bool Cogs::WaveSpectrum::update(RenderContext& renderContext,
const float dt)
341 IContext* context = renderContext.context;
344 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::Disperse");
348 context->setViewport(0, 0,
float(N),
float(N));
349 context->setEffect(disperse.effect);
350 context->setInputLayout(disperse.il);
352 gpgpuQuadRenderer.bind(context);
354 context->setTexture(
"aTex", 0, frequencyDomain.aTex);
355 context->setTexture(
"phaseTex", 0, phaseTex[(frame + 1) & 1]);
358 MappedBuffer<DispersionParameters> constants(context, disperse.constantBuffer,
MapMode::WriteDiscard);
361 constants->twoPiOverSideLength = float(2.0*M_PI / (tileExtentAdjust*tileExtent));
365 context->setConstantBuffer(
"DispersionParameters", disperse.constantBuffer);
367 gpgpuQuadRenderer.draw(context);
371 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::iFFT");
373 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.xyTarget, frequencyDomain.xyTex,
true);
375 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.zTarget, frequencyDomain.zTex,
false);
377 fourierTransform.inverseFourierTransform(renderContext, gpgpuQuadRenderer, spatialDomain.dzduTarget, frequencyDomain.dzdu_dzdv_Tex,
true);
381 CommandGroupAnnotation preGroup(renderContext.context,
"WaveSpectrum::Pack");
384 context->setViewport(0, 0,
float(N),
float(N));
386 context->setEffect(texturePack.effect);
390 constants->N_minus_1 = N - 1;
391 constants->L_over_twoN = tileExtent / (2.f*N);
394 context->setConstantBuffer(
"PackParamters", texturePack.constantBuffer);
395 context->setTexture(
"waveXY", 0, spatialDomain.xyTex);
396 context->setTexture(
"waveZ", 1, spatialDomain.zTex);
397 context->setTexture(
"wavedZdu_dZdv", 2, spatialDomain.dzdu_dzdv_Tex);
399 gpgpuQuadRenderer.bind(context);
400 context->setInputLayout(texturePack.il);
401 gpgpuQuadRenderer.draw(context);
403 auto textures = device->getTextures();
404 textures->generateMipmaps(packed.xyzTex);
405 textures->generateMipmaps(packed.dxdu_dydv_dzdu_dzdvTex);
408 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)
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.