Blender V5.0
ocean_spectrum.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.hh"
10#include "BLI_math_constants.h"
11
12#include "BKE_ocean.h"
13
14#include "ocean_intern.h"
15
16#include <algorithm>
17#include <cmath>
18
19#ifdef WITH_OCEANSIM
20
21/* -------------------------------------------------------------------- */
26
27/*
28 * Original code from EncinoWaves project Copyright (c) 2015 Christopher Jon Horvath
29 * Modifications made to work within blender.
30 *
31 * Licensed under the Apache License, Version 2.0 (the "License");
32 * you may not use this file except in compliance with the License.
33 * You may obtain a copy of the License at
34 *
35 * http://www.apache.org/licenses/LICENSE-2.0
36 */
37
42static float alpha_beta_spectrum(const float alpha,
43 const float beta,
44 const float gamma,
45 const float omega,
46 const float peakomega)
47{
48 return (alpha * sqrt(gamma) / pow(omega, 5.0)) * exp(-beta * pow(peakomega / omega, 4.0));
49}
50
51static float peak_sharpen(const float omega, const float peakomega, const float gamma)
52{
54 const float sigma = (omega < peakomega) ? 0.07 : 0.09;
55 const float exponent = -square((omega - peakomega) / (sigma * peakomega)) / 2.0;
56 return pow(gamma, exp(exponent));
57}
58
62static float ocean_spectrum_wind_and_damp(const Ocean *oc,
63 const float kx,
64 const float kz,
65 const float val)
66{
67 const float k2 = kx * kx + kz * kz;
68 const float k_mag_inv = 1.0f / k2;
69 const float k_dot_w = (kx * k_mag_inv * oc->_wx) + (kz * k_mag_inv * oc->_wz);
70
71 /* Bias towards wind direction. */
72 float newval = val * pow(fabs(k_dot_w), oc->_wind_alignment);
73
74 /* Eliminate wavelengths smaller than cutoff. */
75 // val *= exp(-k2 * m_cutoff);
76
77 /* Reduce reflected waves. */
78 if (k_dot_w < 0.0f) {
79 if (oc->_wind_alignment > 0.0) {
80 newval *= oc->_damp_reflections;
81 }
82 }
83
84 return newval;
85}
86
87static float jonswap(const Ocean *oc, const float k2)
88{
89 /* Get our basic JONSWAP value from #alpha_beta_spectrum. */
90 const float k_mag = sqrt(k2);
91
92 const float m_omega = GRAVITY * k_mag * tanh(k_mag * oc->_depth);
93 const float omega = sqrt(m_omega);
94
95 const float m_fetch = oc->_fetch_jonswap;
96
97 /* Strictly, this should be a random value from a Gaussian (mean 3.3, variance 0.67),
98 * clamped 1.0 to 6.0. */
99 float m_gamma = oc->_sharpen_peak_jonswap;
100 m_gamma = std::max<double>(m_gamma, 1.0);
101 m_gamma = std::min<double>(m_gamma, 6.0);
102
103 const float m_windspeed = oc->_V;
104
105 /* NOTE(@ideasman42): from upstream project in: `src/EncinoWaves/Spectra.h`,
106 * `square(m_windspeed)` is used, *not* `sqrt(m_windspeed)`, this change makes geometry
107 * significantly more *choppy* as well as causing this spectrum to differed significantly
108 * from the "Established Ocean". Keep as is unless a larger refactor/validation of this
109 * algorithm is undertaken. */
110 const float m_dimensionlessFetch = fabs(GRAVITY * m_fetch / sqrt(m_windspeed));
111 const float m_alpha = 0.076 * pow(m_dimensionlessFetch, -0.22);
112
113 const float m_tau = M_PI * 2;
114 const float m_peakomega = m_tau * 3.5 * fabs(GRAVITY / oc->_V) *
115 pow(m_dimensionlessFetch, -0.33);
116
117 const float beta = 1.25f;
118
119 float val = alpha_beta_spectrum(m_alpha, beta, GRAVITY, omega, m_peakomega);
120
121 /* Peak sharpening. */
122 val *= peak_sharpen(omega, m_peakomega, m_gamma);
123
124 return val;
125}
126
127float BLI_ocean_spectrum_piersonmoskowitz(const Ocean *oc, const float kx, const float kz)
128{
129 const float k2 = kx * kx + kz * kz;
130
131 if (k2 == 0.0f) {
132 /* No DC component. */
133 return 0.0f;
134 }
135
136 /* Get Pierson-Moskowitz value from #alpha_beta_spectrum. */
137 const float peak_omega_PM = 0.87f * GRAVITY / oc->_V;
138
139 const float k_mag = sqrt(k2);
140 const float m_omega = GRAVITY * k_mag * tanh(k_mag * oc->_depth);
141
142 const float omega = sqrt(m_omega);
143 const float alpha = 0.0081f;
144 const float beta = 1.291f;
145
146 float val = alpha_beta_spectrum(alpha, beta, GRAVITY, omega, peak_omega_PM);
147
148 val = ocean_spectrum_wind_and_damp(oc, kx, kz, val);
149
150 return val;
151}
152
153float BLI_ocean_spectrum_texelmarsenarsloe(const Ocean *oc, const float kx, const float kz)
154{
155 const float k2 = kx * kx + kz * kz;
156
157 if (k2 == 0.0f) {
158 /* No DC component. */
159 return 0.0f;
160 }
161
162 float val = jonswap(oc, k2);
163
164 val = ocean_spectrum_wind_and_damp(oc, kx, kz, val);
165
166 /* TMA modifications to JONSWAP. */
167 const float m_depth = oc->_depth;
168 const float gain = sqrt(m_depth / GRAVITY);
169
170 const float k_mag = sqrt(k2);
171
172 const float m_omega = GRAVITY * k_mag * tanh(k_mag * oc->_depth);
173 const float omega = sqrt(m_omega);
174
175 const float kitaigorodskiiDepth_wh = omega * gain;
176 const float kitaigorodskiiDepth = 0.5 + (0.5 * tanh(1.8 * (kitaigorodskiiDepth_wh - 1.125)));
177
178 val *= kitaigorodskiiDepth;
179
180 val = ocean_spectrum_wind_and_damp(oc, kx, kz, val);
181
182 return val;
183}
184
185float BLI_ocean_spectrum_jonswap(const Ocean *oc, const float kx, const float kz)
186{
187 const float k2 = kx * kx + kz * kz;
188
189 if (k2 == 0.0f) {
190 /* No DC component. */
191 return 0.0f;
192 }
193
194 float val = jonswap(oc, k2);
195
196 val = ocean_spectrum_wind_and_damp(oc, kx, kz, val);
197
198 return val;
199}
200
202
203#endif /* WITH_OCEANSIM */
float BLI_ocean_spectrum_piersonmoskowitz(const struct Ocean *oc, float kx, float kz)
float BLI_ocean_spectrum_jonswap(const struct Ocean *oc, float kx, float kz)
float BLI_ocean_spectrum_texelmarsenarsloe(const struct Ocean *oc, float kx, float kz)
#define M_PI
btVector3 m_depth
#define pow
#define exp
#define sqrt
#define tanh
ccl_device_inline float beta(const float x, const float y)
Definition math_base.h:661
ccl_device_inline float2 fabs(const float2 a)
T square(const T &a)