载入中...
搜索中...
未找到
FluidSimulation.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6
7namespace eve::fluids {
8
9SimGrid SimGrid::make(const MeshSdf& sdf, float cellSize) {
10 SimGrid grid;
11 grid.cellSize = std::max(cellSize, 1e-4f);
12 const glm::vec3 extent = glm::vec3(sdf.dims) * sdf.cellSize;
13 const glm::vec3 cells = glm::ceil((extent + glm::vec3(2.f * grid.cellSize)) / grid.cellSize);
14 grid.origin = sdf.origin - glm::vec3(grid.cellSize);
15 grid.dims = glm::max(glm::ivec3(1), glm::ivec3(cells));
16 return grid;
17}
18
19int SimGrid::cellCount() const { return dims.x * dims.y * dims.z; }
20
21int SimGrid::cellIndex(int x, int y, int z) const { return x + dims.x * (y + dims.y * z); }
22
23bool SimGrid::inBounds(int x, int y, int z) const {
24 return x >= 0 && y >= 0 && z >= 0 && x < dims.x && y < dims.y && z < dims.z;
25}
26
27glm::ivec3 SimGrid::cellOf(const glm::vec3& p) const {
28 glm::ivec3 c = glm::ivec3(glm::floor((p - origin) / cellSize));
29 return glm::clamp(c, glm::ivec3(0), dims - glm::ivec3(1));
30}
31
33 : params_(params), maxParticles_(std::max(1, maxParticles)) {
34 particles_.resize(size_t(maxParticles_));
35 densities_.resize(size_t(maxParticles_));
36 lambdas_.resize(size_t(maxParticles_));
37 gradSums_.resize(size_t(maxParticles_));
38 cellNext_.resize(size_t(maxParticles_));
39}
40
42 sdf_ = sdf;
43 grid_ = SimGrid::make(sdf_, params_.supportRadius);
44 cellHead_.assign(size_t(grid_.cellCount()), -1);
45}
46
48 count_ = 0;
49 std::fill(cellHead_.begin(), cellHead_.end(), -1);
50}
51
52int FluidSimulation::spawnDrop(const glm::vec3& center, float radius, int count) {
53 int added = 0;
54 for (int k = 0; k < count && count_ < maxParticles_; ++k) {
55 // Deterministic golden-ratio sphere fill so tests are stable.
56 const float z = (float((0x9E3779B9u * uint32_t(k + 1)) % 10000u) / 10000.f) * 2.f - 1.f;
57 const float a = 2.399963229728653f * float(k);
58 const float rr = radius * std::sqrt(std::max(0.f, 1.f - z * z));
59 glm::vec3 p = center + glm::vec3(std::cos(a) * rr, radius * z, std::sin(a) * rr);
60 if (sdf_.voxelCount() > 0) {
61 const float d = sdf_.sample(p);
62 if (d < params_.particleRadius) {
63 glm::vec3 n = sdf_.gradient(p);
64 const float nl = glm::length(n);
65 if (nl > 1e-6f)
66 n /= nl;
67 else
68 n = glm::vec3(0.f, 1.f, 0.f);
69 p = p - n * (d - params_.particleRadius);
70 }
71 }
72 particles_[size_t(count_)] = FluidParticle{p, glm::vec3(0.f)};
73 densities_[size_t(count_)] = 0.f;
74 ++count_;
75 ++added;
76 }
77 return added;
78}
79
80void FluidSimulation::step(float dt) { step(dt, std::max(1, params_.iterations)); }
81
82void FluidSimulation::step(float dt, int substeps) {
83 if (count_ <= 0 || sdf_.voxelCount() <= 0) return;
84 const int iters = std::max(1, substeps);
85 const float sub = dt / float(iters);
86 for (int i = 0; i < iters; ++i) {
87 rebuildGrid();
88 integrate(sub);
89 const int pbf = std::max(0, params_.pbfIterations);
90 for (int k = 0; k < pbf; ++k) {
91 rebuildGrid();
92 computeDensitiesAndGrads();
93 computeLambdas();
94 applyPositionCorrections();
95 }
96 }
97}
98
99void FluidSimulation::rebuildGrid() {
100 if (cellHead_.empty()) return;
101 std::fill(cellHead_.begin(), cellHead_.end(), -1);
102 for (int i = 0; i < count_; ++i) {
103 const glm::ivec3 c = grid_.cellOf(particles_[size_t(i)].pos);
104 const int cell = grid_.cellIndex(c.x, c.y, c.z);
105 cellNext_[size_t(i)] = cellHead_[size_t(cell)];
106 cellHead_[size_t(cell)] = i;
107 }
108}
109
110void FluidSimulation::computeDensitiesAndGrads() {
111 const float h = params_.supportRadius;
112 const float h2 = h * h;
113 for (int i = 0; i < count_; ++i) {
114 const glm::vec3 pi = particles_[size_t(i)].pos;
115 const glm::ivec3 c = grid_.cellOf(pi);
116 float dens = 0.f;
117 glm::vec3 gradSum(0.f);
118 for (int z = -1; z <= 1; ++z) {
119 for (int y = -1; y <= 1; ++y) {
120 for (int x = -1; x <= 1; ++x) {
121 const int cx = c.x + x;
122 const int cy = c.y + y;
123 const int cz = c.z + z;
124 if (!grid_.inBounds(cx, cy, cz)) continue;
125 const int cell = grid_.cellIndex(cx, cy, cz);
126 for (int j = cellHead_[size_t(cell)]; j >= 0; j = cellNext_[size_t(j)]) {
127 const glm::vec3 dx = pi - particles_[size_t(j)].pos;
128 const float r2 = glm::dot(dx, dx);
129 if (r2 < h2) {
130 dens += fluidPoly6(r2, h);
131 gradSum += fluidSpikyGrad(dx, h);
132 }
133 }
134 }
135 }
136 }
137 densities_[size_t(i)] = dens;
138 gradSums_[size_t(i)] = gradSum;
139 }
140}
141
142void FluidSimulation::computeLambdas() {
143 const float rho0 = std::max(params_.restDensity, 1e-6f);
144 for (int i = 0; i < count_; ++i) {
145 const float rho = densities_[size_t(i)];
146 const float C = std::max(0.f, rho / rho0 - 1.f);
147 const float gradSq = glm::dot(gradSums_[size_t(i)], gradSums_[size_t(i)]);
148 lambdas_[size_t(i)] = -C / (gradSq + 1e-6f);
149 }
150}
151
152void FluidSimulation::applyPositionCorrections() {
153 const float h = params_.supportRadius;
154 const float h2 = h * h;
155 const float rho0 = std::max(params_.restDensity, 1e-6f);
156 const float radius = params_.particleRadius;
157 for (int i = 0; i < count_; ++i) {
158 const glm::vec3 pi = particles_[size_t(i)].pos;
159 const float li = lambdas_[size_t(i)];
160 const glm::ivec3 c = grid_.cellOf(pi);
161 glm::vec3 delta(0.f);
162 for (int z = -1; z <= 1; ++z) {
163 for (int y = -1; y <= 1; ++y) {
164 for (int x = -1; x <= 1; ++x) {
165 const int cx = c.x + x;
166 const int cy = c.y + y;
167 const int cz = c.z + z;
168 if (!grid_.inBounds(cx, cy, cz)) continue;
169 const int cell = grid_.cellIndex(cx, cy, cz);
170 for (int j = cellHead_[size_t(cell)]; j >= 0; j = cellNext_[size_t(j)]) {
171 if (j == i) continue;
172 const glm::vec3 dx = pi - particles_[size_t(j)].pos;
173 const float r2 = glm::dot(dx, dx);
174 if (r2 < h2) delta += (li + lambdas_[size_t(j)]) * fluidSpikyGrad(dx, h);
175 }
176 }
177 }
178 }
179 glm::vec3 p = pi + delta / rho0;
180 // Clamp the PBF position correction (standard PBF stabilization):
181 // overlapping particles would otherwise amplify the spiky gradient
182 // unboundedly and fling the drop apart.
183 const float maxDelta = 0.0005f * h;
184 const glm::vec3 correction = delta / rho0;
185 const float cl = glm::length(correction);
186 if (cl > maxDelta && cl > 1e-9f) p = pi + correction * (maxDelta / cl);
187 const float d = sdf_.sample(p);
188 {
189 glm::vec3 n = sdf_.gradient(p);
190 const float nl = glm::length(n);
191 if (nl > 1e-6f)
192 n /= nl;
193 else
194 n = glm::vec3(0.f, 1.f, 0.f);
195 p = p - n * (d - radius);
196 }
197 particles_[size_t(i)].pos = p;
198 }
199}
200
201void FluidSimulation::integrate(float dt) {
202 const float h = params_.supportRadius;
203 const float h2 = h * h;
204 const float radius = params_.particleRadius;
205 for (int i = 0; i < count_; ++i) {
206 glm::vec3 p = particles_[size_t(i)].pos;
207 glm::vec3 v = particles_[size_t(i)].vel;
208
209 // XSPH viscosity + Bingham yield stress + Akinci-style cohesion.
210 if (params_.viscosity > 0.f || params_.cohesion > 0.f || params_.yieldStress > 0.f) {
211 glm::vec3 viscAcc(0.f);
212 glm::vec3 cohesionAcc(0.f);
213 float shearAcc = 0.f;
214 float neighborWeight = 0.f;
215 float cohesionWeight = 0.f;
216 const float particleVolume = std::pow(2.f * radius, 3.f);
217 const glm::ivec3 c = grid_.cellOf(p);
218 for (int z = -1; z <= 1; ++z) {
219 for (int y = -1; y <= 1; ++y) {
220 for (int x = -1; x <= 1; ++x) {
221 const int cx = c.x + x;
222 const int cy = c.y + y;
223 const int cz = c.z + z;
224 if (!grid_.inBounds(cx, cy, cz)) continue;
225 const int cell = grid_.cellIndex(cx, cy, cz);
226 for (int j = cellHead_[size_t(cell)]; j >= 0; j = cellNext_[size_t(j)]) {
227 if (j == i) continue;
228 const glm::vec3 dx = p - particles_[size_t(j)].pos;
229 const float r2 = glm::dot(dx, dx);
230 if (r2 < h2) {
231 const float weight = fluidPoly6(r2, h) * particleVolume;
232 if (params_.viscosity > 0.f || params_.yieldStress > 0.f) {
233 viscAcc += (particles_[size_t(j)].vel - v) * weight;
234 neighborWeight += weight;
235 }
236 if (params_.yieldStress > 0.f)
237 shearAcc += glm::length(particles_[size_t(j)].vel - v) * weight;
238 if (params_.cohesion > 0.f) {
239 cohesionAcc -= dx * weight;
240 cohesionWeight += weight;
241 }
242 }
243 }
244 }
245 }
246 }
247 // Bingham plastic: effective viscosity grows as shear rate drops,
248 // so low-shear mud "freezes" and piles up instead of spreading.
249 float effectiveViscosity = params_.viscosity;
250 if (params_.yieldStress > 0.f && shearAcc > 1e-6f) effectiveViscosity += params_.yieldStress / shearAcc;
251 if (neighborWeight > 1e-6f) v += (viscAcc / neighborWeight) * std::clamp(effectiveViscosity * dt, 0.f, 1.f);
252 glm::vec3 cohesionNormal = sdf_.gradient(p);
253 if (glm::length(cohesionNormal) > 1e-6f)
254 cohesionNormal = glm::normalize(cohesionNormal);
255 else
256 cohesionNormal = glm::vec3(0.f, 1.f, 0.f);
257 cohesionAcc -= cohesionNormal * glm::dot(cohesionAcc, cohesionNormal);
258 if (cohesionWeight > 1e-6f)
259 v += fluidClampSpeed((cohesionAcc / cohesionWeight) * (params_.cohesion * 100.f * dt), radius);
260 }
261
262 v += params_.gravity * dt;
263 v *= std::max(0.f, 1.f - params_.damping * dt);
264 if (params_.adhesion > 0.f) v *= std::exp(-params_.adhesion * 300.f * dt);
265 v = fluidClampSpeed(v, params_.maxVelocity);
266 p += v * dt;
267
268 // Project onto the solid surface and kill inward normal velocity so
269 // gravity only drives tangential flow (water film stays on the model).
270 float d = sdf_.sample(p);
271 glm::vec3 n = sdf_.gradient(p);
272 const float nl = glm::length(n);
273 if (nl > 1e-6f)
274 n /= nl;
275 else
276 n = glm::vec3(0.f, 1.f, 0.f);
277 {
278 p = p - n * (d - radius);
279 const float vn = glm::dot(v, n);
280 v -= n * vn;
281 d = radius;
282 }
283 // Adhesion: pull the film toward the solid while within range.
284 if (params_.adhesion > 0.f && d < h) v += -n * (params_.adhesion * std::max(0.f, 1.f - d / h) * dt * 30.f);
285
286 particles_[size_t(i)].pos = p;
287 particles_[size_t(i)].vel = v;
288 }
289}
290
291} // namespace eve::fluids
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
glm::vec4 p[6]
glm::vec3 n
Definition Grass.cpp:63
float v
std::int32_t c
int h
MeleePoint3 a
Definition MeleeHit.cpp:40
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
float radius
float d
float dx
std::uint32_t count
Cell cell
float step
Definition TreeMesh.cpp:314
void clear()
Remove all particles.
FluidSimulation(int maxParticles, const FluidParams &params=FluidParams{})
Fluid simulation.
void setSdf(const MeshSdf &sdf)
Replace the collision surface. Particles keep their state.
int spawnDrop(const glm::vec3 &center, float radius, int count)
Spawn a roughly spherical drop of particles near the surface.
const MeshSdf & sdf() const
Sdf.
void step(float dt)
Advance the simulation by dt seconds (params.iterations substeps).
Uniform signed-distance voxel field over a box.
Definition FluidSdf.h:28
glm::ivec3 dims
Voxel resolution along each axis.
Definition FluidSdf.h:35
float cellSize
World-space size of one voxel.
Definition FluidSdf.h:33
glm::vec3 gradient(const glm::vec3 &p) const
Analytic trilinear gradient (outward normal) at p.
Definition FluidSdf.cpp:128
glm::vec3 origin
World-space position of voxel (0,0,0).
Definition FluidSdf.h:31
int voxelCount() const
Voxel count.
Definition FluidSdf.cpp:87
float sample(const glm::vec3 &p) const
Trilinearly interpolated signed distance at p.
Definition FluidSdf.cpp:95
std::vector< ParamSpec > params
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
float fluidPoly6(float r2, float h)
Poly6 kernel value W(r2, h).
Definition FluidMath.h:65
glm::vec3 fluidClampSpeed(const glm::vec3 &v, float maxSpeed)
Clamp a vector's magnitude to maxSpeed.
Definition FluidMath.h:113
glm::vec3 fluidSpikyGrad(const glm::vec3 &dx, float h)
Spiky gradient of the kernel w.r.t. particle i position.
Definition FluidMath.h:78
Tuning knobs of one fluid simulation.
Definition FluidMath.h:26
float damping
Linear air damping applied each substep.
Definition FluidMath.h:44
float restDensity
Rest density; normalized to 1.
Definition FluidMath.h:32
float supportRadius
SPH support radius h (kernel cutoff), typically 4x particleRadius.
Definition FluidMath.h:30
float particleRadius
Resting particle radius in world units.
Definition FluidMath.h:28
float adhesion
Fluid-surface adhesion strength (contact angle / sticking).
Definition FluidMath.h:42
int pbfIterations
PBF density-constraint relaxation passes per substep.
Definition FluidMath.h:50
float cohesion
Fluid-fluid cohesion strength (droplet formation).
Definition FluidMath.h:40
int iterations
Solver substeps per call to step(dt).
Definition FluidMath.h:48
float viscosity
XSPH viscosity strength (0 = inviscid).
Definition FluidMath.h:36
float maxVelocity
Velocity clamp after integration.
Definition FluidMath.h:46
float yieldStress
Bingham yield stress: below this shear rate particles "freeze".
Definition FluidMath.h:38
glm::vec3 gravity
Gravity vector in world units / s^2.
Definition FluidMath.h:34
One simulated particle (CPU reference layout).
Definition FluidMath.h:54
Uniform grid used for neighbor queries.
bool inBounds(int x, int y, int z) const
In bounds.
static SimGrid make(const MeshSdf &sdf, float cellSize)
Build a grid covering the SDF domain plus one cell of padding.
float cellSize
Cell size in world units (== SPH support radius).
int cellCount() const
Cell count.
glm::vec3 origin
World position of cell (0,0,0).
glm::ivec3 cellOf(const glm::vec3 &p) const
Cell of.
int cellIndex(int x, int y, int z) const
Cell index.
glm::ivec3 dims
Cell counts per axis.