载入中...
搜索中...
未找到
VolumeFluidSurface.cpp
浏览该文件的文档.
1#include <algorithm>
2#include <cmath>
3#include <glm/gtc/matrix_transform.hpp>
4#include "common/Profile.h"
7
8namespace eve::fluids {
9namespace {
10constexpr size_t kMaxGasParticles = 65536;
11constexpr uint64_t kMaxGasCoveredPixels = 4000000;
12constexpr float kBlurWeights[7] = {.006f, .061f, .242f, .383f, .242f, .061f, .006f};
13} // namespace
14void FluidSurfaceRenderer::renderVolume(const VolumeFluid& sim) { renderVolumeInternal(sim, false, 1.f); }
15
16void FluidSurfaceRenderer::renderVolumeColorOnly(const VolumeFluid& sim) { renderVolumeInternal(sim, true, 1.f); }
17
19 if (!std::isfinite(alpha) || alpha < 0.f || alpha > 1.f)
21 DiagnosticCode::InvalidArgument, "Invalid surface interpolation alpha", "fluids.surface.interpolation"));
22 renderVolumeInternal(sim, false, alpha);
23 return Result<void>::success();
24}
25
26Result<void> FluidSurfaceRenderer::configureRefraction(float transparency, float absorption, float coefficient,
27 int downsample) {
28 if (!std::isfinite(transparency) || transparency < 0.f || transparency > 1.f || !std::isfinite(absorption) ||
29 absorption < 0.f || absorption > 30.f || !std::isfinite(coefficient) || coefficient < -.1f ||
30 coefficient > .1f || downsample < 1 || downsample > 4)
32 "Invalid fluid refraction controls",
33 "fluids.surface.configureRefraction"));
34 refractionTransparency_ = transparency;
35 refractionAbsorption_ = absorption;
36 refractionCoefficient_ = coefficient;
37 refractionDownsample_ = downsample;
38 return Result<void>::success();
39}
40
45
47 if (!refractionEnabled_) return Result<void>::success();
48 const int width = params_.width, height = params_.height;
49 const size_t pixels = size_t(width) * size_t(height);
50 if (!auxiliaryCurrent_)
52 "Current color frame has no host-visible surface auxiliaries",
53 "fluids.surface.configuredRefraction"));
54 if (sceneColor.size() != pixels * 4u)
56 "Refraction scene dimensions are invalid",
57 "fluids.surface.configuredRefraction"));
58
59 const int targetWidth = std::max(1, (width + refractionDownsample_ - 1) / refractionDownsample_);
60 const int targetHeight = std::max(1, (height + refractionDownsample_ - 1) / refractionDownsample_);
61 refractionScratch_.resize(size_t(targetWidth) * size_t(targetHeight) * 4u);
62 const auto sample = [](std::span<const uint8_t> source, int sourceWidth, int sourceHeight, float x, float y,
63 int channel) {
64 x = std::clamp(x, 0.f, float(sourceWidth - 1));
65 y = std::clamp(y, 0.f, float(sourceHeight - 1));
66 const int x0 = int(std::floor(x)), y0 = int(std::floor(y));
67 const int x1 = std::min(x0 + 1, sourceWidth - 1), y1 = std::min(y0 + 1, sourceHeight - 1);
68 const float tx = x - float(x0), ty = y - float(y0);
69 const auto at = [&](int px, int py) {
70 return float(source[(size_t(py) * size_t(sourceWidth) + size_t(px)) * 4u + size_t(channel)]);
71 };
72 return std::lerp(std::lerp(at(x0, y0), at(x1, y0), tx), std::lerp(at(x0, y1), at(x1, y1), tx), ty);
73 };
74 for (int y = 0; y < targetHeight; ++y)
75 for (int x = 0; x < targetWidth; ++x) {
76 const float sourceX = (float(x) + .5f) * float(width) / float(targetWidth) - .5f;
77 const float sourceY = (float(y) + .5f) * float(height) / float(targetHeight) - .5f;
78 const size_t out = (size_t(y) * size_t(targetWidth) + size_t(x)) * 4u;
79 for (int channel = 0; channel < 4; ++channel)
80 refractionScratch_[out + size_t(channel)] = uint8_t(
81 std::clamp(std::lround(sample(sceneColor, width, height, sourceX, sourceY, channel)), 0l, 255l));
82 }
83
84 const std::span<const uint8_t> reduced(refractionScratch_);
85 for (size_t i = 0; i < pixels; ++i) {
86 const size_t rgba = i * 4u;
87 const float coverage = float(color_[rgba + 3u]) / 255.f;
88 if (coverage <= 0.f) {
89 std::copy_n(sceneColor.data() + rgba, 4u, color_.data() + rgba);
90 continue;
91 }
92 const int x = int(i % size_t(width)), y = int(i / size_t(width));
93 const float u = (float(x) + .5f) / float(width) + normals_[i].x * thickness_[i] * refractionCoefficient_;
94 const float v = (float(y) + .5f) / float(height) - normals_[i].y * thickness_[i] * refractionCoefficient_;
95 const float rx = u * float(targetWidth) - .5f;
96 const float ry = v * float(targetHeight) - .5f;
97 for (int channel = 0; channel < 3; ++channel) {
98 const float liquid = float(color_[rgba + size_t(channel)]) / 255.f;
99 const float absorbed = std::exp(-refractionAbsorption_ * (1.f - liquid) * std::max(thickness_[i], 0.f));
100 const float refracted = sample(reduced, targetWidth, targetHeight, rx, ry, channel) * absorbed;
101 const float fluid = std::lerp(float(color_[rgba + size_t(channel)]), refracted, refractionTransparency_);
102 const float composed = float(sceneColor[rgba + size_t(channel)]) * (1.f - coverage) + fluid * coverage;
103 color_[rgba + size_t(channel)] = uint8_t(std::clamp(std::lround(composed), 0l, 255l));
104 }
105 color_[rgba + 3u] = 255u;
106 }
107 residentColorCurrent_ = false;
108 return Result<void>::success();
109}
110
111void FluidSurfaceRenderer::renderVolumeInternal(const VolumeFluid& sim, bool colorOnly, float alpha) {
112 EV_PROFILE_MODULE("fluids", "FluidSurfaceRenderer::renderVolume");
113 residentColorCurrent_ = false;
114 const auto copied = anisotropyEnabled_
115 ? sim.copyInterpolatedSurfaceRenderData(alpha, positions_, volumeColors_, particleRadii_,
116 particleOrientations_)
117 : sim.copyInterpolatedRenderData(alpha, positions_, volumeColors_);
118 const float spacing = copied.value();
119 const float radius = spacing * 0.9f;
120 anisotropicFrame_ = anisotropyEnabled_ && !positions_.empty();
121 if (anisotropicFrame_) buildAnisotropicSplats();
122 const bool uniform = !particleBlendConfigured_ && !volumeColors_.empty() &&
123 std::all_of(volumeColors_.begin() + 1, volumeColors_.end(),
124 [&](const glm::vec4& color) { return color == volumeColors_.front(); });
125 uniformVolumeColor_ = uniform ? volumeColors_.front() : glm::vec4(0.f);
126 const bool applyDownsampling = !colorOnly;
127 uniformVolumeGpuShade_ = colorOnly && uniform && preferGpu_;
128 multicolorVolumeGpuShade_ = colorOnly && !uniform && !particleBlendConfigured_ && preferGpu_;
129 if (applyDownsampling && surfaceDownsample_ > 1) {
130 ensureReducedRenderer();
131 reducedRenderer_->positions_ = positions_;
132 reducedRenderer_->anisotropicFrame_ = anisotropicFrame_;
133 if (anisotropicFrame_) {
134 reducedRenderer_->particleRadii_ = particleRadii_;
135 reducedRenderer_->particleOrientations_ = particleOrientations_;
136 reducedRenderer_->buildAnisotropicSplats();
137 }
138 reducedRenderer_->renderInternal(reducedRenderer_->positions_, radius);
139 expandReducedOutputs(false);
140 } else {
141 renderInternal(positions_, radius);
142 }
143 if (applyDownsampling && thicknessDownsample_ != surfaceDownsample_ && !positions_.empty()) {
144 ensureThicknessRenderer();
145 thicknessRenderer_->positions_ = positions_;
146 thicknessRenderer_->anisotropicFrame_ = anisotropicFrame_;
147 if (anisotropicFrame_) {
148 thicknessRenderer_->particleRadii_ = particleRadii_;
149 thicknessRenderer_->particleOrientations_ = particleOrientations_;
150 thicknessRenderer_->buildAnisotropicSplats();
151 }
152 thicknessRenderer_->renderInternal(thicknessRenderer_->positions_, radius);
153 replaceThicknessFromReduced();
154 }
155 const bool gpuShadedVolume = (uniformVolumeGpuShade_ || multicolorVolumeGpuShade_) && gpuOk_;
156 uniformVolumeGpuShade_ = false;
157 multicolorVolumeGpuShade_ = false;
158 if (gpuShadedVolume) auxiliaryCurrent_ = false;
159 if (positions_.empty() || gpuShadedVolume) return;
160 const int width = params_.width, height = params_.height;
161 const auto view = glm::lookAtRH(params_.eye, params_.target, params_.up);
162 const float projectionScale =
163 params_.orthographic ? params_.orthographicSize : std::tan(glm::radians(params_.fovYDeg) * 0.5f);
164 const float aspect = float(width) / float(height);
165 const size_t pixelCount = size_t(width) * size_t(height);
166
167 // Most liquid actors use one material color. Avoid re-splatting every particle
168 // over the CPU image after the SSF pass in that common case: the reconstructed
169 // depth already identifies the covered pixels, so tinting is linear in output
170 // resolution instead of particle count times projected disc area.
171 const glm::vec4 uniformColor = volumeColors_.front();
172 if (uniform) {
173 const auto lightDirection = glm::normalize(glm::vec3(0.35f, 0.65f, 0.55f));
174 for (size_t i = 0; i < pixelCount; ++i) {
175 if (color_[i * 4u + 3u] == 0 || thickness_[i] * 10.f < thicknessCutoff_) {
176 color_[i * 4u + 0u] = color_[i * 4u + 1u] = color_[i * 4u + 2u] = color_[i * 4u + 3u] = 0;
177 continue;
178 }
179 const auto n = normals_[i];
180 const float diff = std::max(0.f, glm::dot(n, lightDirection));
181 const float light =
182 lighting_ ? std::clamp(ambientMultiplier_ + (1.f - std::min(ambientMultiplier_, 1.f)) * diff, 0.f, 6.f)
183 : 1.f;
184 const float exponent = 4.f + 124.f * smoothness_;
185 const float spec = lighting_ ? std::pow(std::max(n.z, 0.f), exponent) * smoothness_ * .56f : 0.f;
186 const float fresnel = .04f + .96f * std::pow(1.f - std::max(n.z, 0.f), 5.f);
187 const glm::vec3 reflected = glm::mix(reflectionColor_, glm::vec3(uniformColor), metalness_);
188 const float reflectionStrength = reflectionEnabled_ ? reflection_ : 0.f;
189 const glm::vec3 shaded =
190 glm::vec3(uniformColor) * light + reflected * fresnel * reflectionStrength + glm::vec3(spec);
191 for (int c = 0; c < 3; ++c) color_[i * 4u + size_t(c)] = uint8_t(255.f * std::clamp(shaded[c], 0.f, 1.f));
192 color_[i * 4u + 3u] = uint8_t(255.f * std::clamp(thickness_[i] * opacity_, 0.f, 1.f) * uniformColor.a);
193 }
194 return;
195 }
196
197 volumeTint_.assign(pixelCount, particleBlendConfigured_ ? glm::vec4(1.f) : glm::vec4(0.f));
198 volumeTintWeights_.assign(pixelCount, 0.f);
199 for (size_t particle = 0; particle < positions_.size(); ++particle) {
200 const auto v = view * glm::vec4(positions_[particle], 1.f);
201 const float z = -v.z;
202 if (z <= params_.nearZ || z >= params_.farZ) continue;
203 const float divisor = params_.orthographic ? 1.f : z;
204 const float cx = (0.5f + 0.5f * v.x / (divisor * projectionScale * aspect)) * float(width);
205 const float cy = (0.5f - 0.5f * v.y / (divisor * projectionScale)) * float(height);
206 const float r = std::max(1.f, radius * float(height) / (2.f * divisor * projectionScale));
207 const int x0 = std::max(0, int(std::floor(cx - r))), x1 = std::min(width - 1, int(std::ceil(cx + r)));
208 const int y0 = std::max(0, int(std::floor(cy - r))), y1 = std::min(height - 1, int(std::ceil(cy + r)));
209 for (int y = y0; y <= y1; ++y)
210 for (int x = x0; x <= x1; ++x) {
211 const float dx = (float(x) + 0.5f - cx) / r, dy = (float(y) + 0.5f - cy) / r;
212 const float q = 1.f - dx * dx - dy * dy;
213 if (q <= 0.f) continue;
214 const size_t at = size_t(y) * size_t(width) + size_t(x);
215 const float fragmentDepth = z - radius * std::sqrt(q);
216 if (fragmentDepth > depth_[at] + radius) continue;
217 if (particleBlendConfigured_) {
218 if (particleDepthWrite_ && volumeTintWeights_[at] > 0.f && fragmentDepth >= volumeTintWeights_[at])
219 continue;
220 const glm::vec4 source = volumeColors_[particle];
221 const glm::vec4 destination = volumeTint_[at];
222 glm::vec4 blended =
223 glm::clamp(source * blendFluidColor(particleBlendSource_, source, destination) +
224 destination * blendFluidColor(particleBlendDestination_, source, destination),
225 glm::vec4(0.f), glm::vec4(1.f));
226 blended.a = destination.a; // Fluid3D's FluidColorsBlend pass uses ColorMask RGB.
227 volumeTint_[at] = blended;
228 volumeTintWeights_[at] = particleDepthWrite_ ? fragmentDepth : 1.f;
229 } else {
230 volumeTint_[at] += volumeColors_[particle] * q;
231 volumeTintWeights_[at] += q;
232 }
233 }
234 }
235 for (size_t i = 0; i < volumeTint_.size(); ++i) {
236 if (volumeTintWeights_[i] <= 0.f || color_[i * 4u + 3u] == 0 || thickness_[i] * 10.f < thicknessCutoff_) {
237 if (thickness_[i] * 10.f < thicknessCutoff_)
238 color_[i * 4u + 0u] = color_[i * 4u + 1u] = color_[i * 4u + 2u] = color_[i * 4u + 3u] = 0;
239 continue;
240 }
241 const auto base = particleBlendConfigured_
242 ? volumeTint_[i]
243 : glm::clamp(volumeTint_[i] / volumeTintWeights_[i], glm::vec4(0.f), glm::vec4(1.f));
244 const auto n = normals_[i];
245 const float diff = std::max(0.f, glm::dot(n, glm::normalize(glm::vec3(.35f, .65f, .55f))));
246 const float light =
247 lighting_ ? std::clamp(ambientMultiplier_ + (1.f - std::min(ambientMultiplier_, 1.f)) * diff, 0.f, 6.f)
248 : 1.f;
249 const float exponent = 4.f + 124.f * smoothness_;
250 const float spec = lighting_ ? std::pow(std::max(n.z, 0.f), exponent) * smoothness_ * .56f : 0.f;
251 const float fresnel = .04f + .96f * std::pow(1.f - std::max(n.z, 0.f), 5.f);
252 const glm::vec3 reflected = glm::mix(reflectionColor_, glm::vec3(base), metalness_);
253 const float reflectionStrength = reflectionEnabled_ ? reflection_ : 0.f;
254 const glm::vec3 shaded = glm::vec3(base) * light + reflected * fresnel * reflectionStrength + glm::vec3(spec);
255 for (int c = 0; c < 3; ++c) color_[i * 4u + size_t(c)] = uint8_t(255.f * std::clamp(shaded[c], 0.f, 1.f));
256 color_[i * 4u + 3u] = uint8_t(255.f * std::clamp(thickness_[i] * opacity_, 0.f, 1.f) * base.a);
257 }
258}
259
261 return renderVolumeCloud(sim, absorption, true);
262}
263
265 return renderVolumeCloud(sim, absorption, false);
266}
267
269 if (!surfaceEnabled_) return renderVolumeWithoutSurface(sim, refractionAbsorption_);
270 renderVolumeInternal(sim, false, 1.f);
271 return Result<void>::success();
272}
273
274Result<void> FluidSurfaceRenderer::renderVolumeCloud(const VolumeFluid& sim, float absorption, bool gasOnly) {
275 residentColorCurrent_ = false;
276 if (!std::isfinite(absorption) || absorption < .01f || absorption > 30.f)
278 "Volume absorption must be finite and within [0.01,30]",
279 "fluids.surface.volume-cloud"));
280
281 const auto particles = sim.particleView();
282 size_t selectedCount = particles.size();
283 if (gasOnly)
284 selectedCount =
285 size_t(std::count_if(particles.begin(), particles.end(), [](const VolumeFluidParticle& particle) {
286 return particle.material.phase == VolumeFluidPhase::Gas;
287 }));
288 if (selectedCount > kMaxGasParticles)
290 "Volume frame exceeds the 65536-particle render budget",
291 "fluids.surface.volume-cloud"));
292 positions_.clear();
293 volumeColors_.clear();
294 positions_.reserve(selectedCount);
295 volumeColors_.reserve(selectedCount);
296 for (const auto& particle : particles) {
297 if (gasOnly && particle.material.phase != VolumeFluidPhase::Gas) continue;
298 positions_.push_back(particle.position);
299 volumeColors_.push_back(particle.color);
300 }
301
302 const int width = params_.width, height = params_.height;
303 // Fluid3D permits thicknessDownsample up to 4. Use that performance-safe level
304 // for the CPU reference, then expand once into the public output buffer.
305 const int renderWidth = std::max(8, (width + 3) / 4);
306 const int renderHeight = std::max(8, (height + 3) / 4);
307 const size_t renderPixelCount = size_t(renderWidth) * size_t(renderHeight);
308 const size_t pixelCount = size_t(width) * size_t(height);
309 const float radius = sim.spacing() * .9f;
310 const float projectionScale =
311 params_.orthographic ? params_.orthographicSize : std::tan(glm::radians(params_.fovYDeg) * .5f);
312 const float aspect = float(renderWidth) / float(renderHeight);
313 const auto view = glm::lookAtRH(params_.eye, params_.target, params_.up);
314 gasSplats_.clear();
315 gasSplats_.reserve(positions_.size());
316 uint64_t coveredPixels = 0;
317 for (const auto& position : positions_) {
318 const auto v = view * glm::vec4(position, 1.f);
319 const float z = -v.z;
320 if (z <= params_.nearZ || z >= params_.farZ) {
321 gasSplats_.push_back({});
322 continue;
323 }
324 GasSplat splat;
325 splat.z = z;
326 const float divisor = params_.orthographic ? 1.f : z;
327 splat.cx = (.5f + .5f * v.x / (divisor * projectionScale * aspect)) * float(renderWidth);
328 splat.cy = (.5f - .5f * v.y / (divisor * projectionScale)) * float(renderHeight);
329 splat.r = std::max(1.f, radius * float(renderHeight) / (2.f * divisor * projectionScale));
330 splat.x0 = std::max(0, int(std::floor(splat.cx - splat.r)));
331 splat.x1 = std::min(renderWidth - 1, int(std::ceil(splat.cx + splat.r)));
332 splat.y0 = std::max(0, int(std::floor(splat.cy - splat.r)));
333 splat.y1 = std::min(renderHeight - 1, int(std::ceil(splat.cy + splat.r)));
334 if (splat.x1 >= splat.x0 && splat.y1 >= splat.y0)
335 coveredPixels += uint64_t(splat.x1 - splat.x0 + 1) * uint64_t(splat.y1 - splat.y0 + 1);
336 if (coveredPixels > kMaxGasCoveredPixels)
338 "Volume frame exceeds the four-million-pixel splat budget",
339 "fluids.surface.volume-cloud"));
340 gasSplats_.push_back(splat);
341 }
342
343 depthScratch_.assign(renderPixelCount, 1e30f);
344 gasDensityScratch_.assign(renderPixelCount, 0.f);
345 gasTintScratch_.assign(renderPixelCount, glm::vec4(0.f));
346 for (size_t particle = 0; particle < gasSplats_.size(); ++particle) {
347 const auto& splat = gasSplats_[particle];
348 if (splat.x1 < splat.x0 || splat.y1 < splat.y0) continue;
349 const float r2 = splat.r * splat.r;
350 for (int y = splat.y0; y <= splat.y1; ++y)
351 for (int x = splat.x0; x <= splat.x1; ++x) {
352 const float dx = float(x) + .5f - splat.cx, dy = float(y) + .5f - splat.cy;
353 const float q = 1.f - (dx * dx + dy * dy) / r2;
354 if (q <= 0.f) continue;
355 const size_t at = size_t(y) * size_t(renderWidth) + size_t(x);
356 const float chord = 2.f * radius * std::sqrt(q) * params_.thicknessScale;
357 gasDensityScratch_[at] += chord;
358 gasTintScratch_[at] += volumeColors_[particle] * chord;
359 depthScratch_[at] = std::min(depthScratch_[at], splat.z - radius * std::sqrt(q));
360 }
361 }
362
363 thickness_.resize(renderPixelCount);
364 volumeTint_.resize(renderPixelCount);
365 auto blur = [&](bool horizontal, const std::vector<float>& srcDensity, const std::vector<glm::vec4>& srcTint,
366 std::vector<float>& dstDensity, std::vector<glm::vec4>& dstTint) {
367 for (int y = 0; y < renderHeight; ++y)
368 for (int x = 0; x < renderWidth; ++x) {
369 float density = 0.f;
370 glm::vec4 tint(0.f);
371 for (int tap = -3; tap <= 3; ++tap) {
372 const int sx = std::clamp(x + (horizontal ? tap : 0), 0, renderWidth - 1);
373 const int sy = std::clamp(y + (horizontal ? 0 : tap), 0, renderHeight - 1);
374 const size_t at = size_t(sy) * size_t(renderWidth) + size_t(sx);
375 const float weight = kBlurWeights[tap + 3];
376 density += srcDensity[at] * weight;
377 tint += srcTint[at] * weight;
378 }
379 const size_t at = size_t(y) * size_t(renderWidth) + size_t(x);
380 dstDensity[at] = density;
381 dstTint[at] = tint;
382 }
383 };
384 blur(true, gasDensityScratch_, gasTintScratch_, thickness_, volumeTint_);
385 blur(false, thickness_, volumeTint_, gasDensityScratch_, gasTintScratch_);
386
387 depth_.resize(pixelCount);
388 normals_.assign(pixelCount, glm::vec3(0.f));
389 thickness_.resize(pixelCount);
390 volumeTint_.resize(pixelCount);
391 color_.resize(pixelCount * 4u);
392 for (size_t i = 0; i < pixelCount; ++i) {
393 const int x = int(i % size_t(width)), y = int(i / size_t(width));
394 const int sx = std::min(renderWidth - 1, x * renderWidth / width);
395 const int sy = std::min(renderHeight - 1, y * renderHeight / height);
396 const size_t source = size_t(sy) * size_t(renderWidth) + size_t(sx);
397 depth_[i] = depthScratch_[source];
398 thickness_[i] = gasDensityScratch_[source];
399 volumeTint_[i] = gasTintScratch_[source];
400 const float density = thickness_[i];
401 if (density <= 1e-7f) {
402 color_[i * 4u + 0] = color_[i * 4u + 1] = color_[i * 4u + 2] = color_[i * 4u + 3] = 0;
403 continue;
404 }
405 const glm::vec4 tint = glm::clamp(volumeTint_[i] / density, glm::vec4(0.f), glm::vec4(1.f));
406 const float alpha = (1.f - std::exp(-absorption * density)) * tint.a;
407 const float glow = .72f + .28f * std::clamp(density * absorption, 0.f, 1.f);
408 for (size_t channel = 0; channel < 3; ++channel)
409 color_[i * 4u + channel] = uint8_t(255.f * std::clamp(
410 tint[static_cast<glm::vec4::length_type>(channel)] * glow,
411 0.f, 1.f));
412 color_[i * 4u + 3] = uint8_t(255.f * std::clamp(alpha, 0.f, 1.f));
413 }
414 auxiliaryCurrent_ = true;
415 return Result<void>::success();
416}
417} // 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
float py
glm::vec4 tint
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
std::array< double, 10 > q
double r
float v
std::int32_t c
std::vector< Colorf > px
std::uint32_t height
std::uint32_t width
std::array< float, 3 > position
float radius
#define EV_PROFILE_MODULE(module, name)
Profile the enclosing scope, tagged with a module for grouping.
Definition Profile.h:140
uint8_t * pixels
glm::mat4 view
Light3D::Data * light
float dy
float dx
float sourceX
int spacing
const UnitySourceAsset & source
std::size_t at
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Result< void > renderGasVolume(const VolumeFluid &sim, float absorption=5.f)
Renders gas particles as a blurred, translucent thickness volume.
const std::vector< uint8_t > & color() const
RGBA8 shaded output.
Result< void > configureRefraction(float transparency, float absorption, float coefficient, int downsample)
Configure Fluid3D-compatible scene refraction controls atomically.
Result< void > compositeConfiguredSceneRefraction(std::span< const uint8_t > sceneColor)
Apply the configured Fluid3D-compatible refraction to a matching opaque RGBA8 scene.
void renderVolume(const VolumeFluid &sim)
Reconstructs a volume-fluid snapshot and shades its diffusing particle colors.
Result< void > renderVolumeInterpolated(const VolumeFluid &sim, float alpha)
Reconstructs from fixed-step interpolated volume positions using the existing SSF pipeline.
Result< void > renderVolumeWithoutSurface(const VolumeFluid &sim, float absorption=5.f)
Renders every material phase as a blurred color/thickness volume without reconstructing a surface.
Result< void > configureRefractionEnabled(bool enabled)
Enables or disables Fluid3D's configured refraction stage without losing its controls.
void renderVolumeColorOnly(const VolumeFluid &sim)
Reconstructs only the RGBA output for display, skipping auxiliary GPU readback when color is uniform.
Result< void > renderConfiguredVolume(const VolumeFluid &sim)
Renders through Fluid3D's configured generateSurface policy.
CPU position-based free-volume fluid with a bounded spatial grid. @ownership Owns all particle state;...
std::span< const VolumeFluidParticle > particleView() const
Borrows the current dense particle pool without copying it.
float spacing() const
Returns the rest lattice spacing without copying state.
Result< float > copyInterpolatedRenderData(float alpha, std::vector< glm::vec3 > &positions, std::vector< glm::vec4 > &colors) const
Fuses interpolated positions and colors into one allocation-reusing renderer pass.
Result< float > copyInterpolatedSurfaceRenderData(float alpha, std::vector< glm::vec3 > &positions, std::vector< glm::vec4 > &colors, std::vector< glm::vec3 > &radii, std::vector< glm::vec4 > &orientations) const
Fuses interpolated positions, colors and ellipsoid shape into one renderer pass.
GLSL compute kernels for the GPU surface-flow solver.
Definition FluidTarget.h:12
bool enabled
Owning particle value with world position and velocity in SI units.
Definition VolumeFluid.h:96