载入中...
搜索中...
未找到
FogDensityField.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <algorithm>
6#include <cmath>
7
8#include <glm/common.hpp>
9#include <glm/geometric.hpp>
10
11namespace eve::graphics::fog {
12namespace {
13
14[[nodiscard]] float hash31(int x, int y, int z, std::uint32_t seed) noexcept {
15 std::uint32_t n = static_cast<std::uint32_t>(x) * 374761393u +
16 static_cast<std::uint32_t>(y) * 668265263u +
17 static_cast<std::uint32_t>(z) * 2147483647u + seed * 1013904223u;
18 n = (n ^ (n >> 13)) * 1274126177u;
19 n ^= n >> 16;
20 return static_cast<float>(n & 0x00FFFFFFu) / static_cast<float>(0x01000000u);
21}
22
23} // namespace
24
26 if (width < 2 || height < 2 || depth < 2 || width > 256 || height > 256 || depth > 256) {
28 DiagnosticCode::InvalidArgument, "density field dimensions must be in [2,256]", "size", {},
29 "graphics.fog"));
30 }
31 const glm::vec3 size = bounds.maximum - bounds.minimum;
32 if (!(size.x > 0.f && size.y > 0.f && size.z > 0.f) || !std::isfinite(size.x) ||
33 !std::isfinite(size.y) || !std::isfinite(size.z)) {
35 DiagnosticCode::InvalidArgument, "world bounds must be finite with positive extent",
36 "bounds", {}, "graphics.fog"));
37 }
38
39 width_ = width;
40 height_ = height;
41 depth_ = depth;
42 bounds_ = bounds;
43 cellSize_ = glm::vec3(size.x / static_cast<float>(width), size.y / static_cast<float>(height),
44 size.z / static_cast<float>(depth));
45 const std::size_t count = static_cast<std::size_t>(width) * static_cast<std::size_t>(height) *
46 static_cast<std::size_t>(depth);
47 density_.assign(count, 0.f);
48 curl_.assign(count, glm::vec3(0.f));
49 lightAssist_.assign(count, glm::vec3(0.f));
50 ++revision_;
51 return Result<void>::success();
52}
53
55 std::fill(density_.begin(), density_.end(), 0.f);
56 std::fill(curl_.begin(), curl_.end(), glm::vec3(0.f));
57 std::fill(lightAssist_.begin(), lightAssist_.end(), glm::vec3(0.f));
58 ++revision_;
59}
60
61std::size_t FogDensityField::index(int x, int y, int z) const noexcept {
62 x = std::clamp(x, 0, std::max(width_ - 1, 0));
63 y = std::clamp(y, 0, std::max(height_ - 1, 0));
64 z = std::clamp(z, 0, std::max(depth_ - 1, 0));
65 return (static_cast<std::size_t>(z) * static_cast<std::size_t>(height_) +
66 static_cast<std::size_t>(y)) *
67 static_cast<std::size_t>(width_) +
68 static_cast<std::size_t>(x);
69}
70
71glm::vec3 FogDensityField::cellCenter(int x, int y, int z) const noexcept {
72 return bounds_.minimum +
73 glm::vec3((static_cast<float>(x) + 0.5f) * cellSize_.x,
74 (static_cast<float>(y) + 0.5f) * cellSize_.y,
75 (static_cast<float>(z) + 0.5f) * cellSize_.z);
76}
77
78void FogDensityField::setDensity(int x, int y, int z, float density) {
79 if (density_.empty()) return;
80 density_[index(x, y, z)] = std::max(density, 0.f);
81 ++revision_;
82}
83
84float FogDensityField::densityAt(int x, int y, int z) const noexcept {
85 if (density_.empty()) return 0.f;
86 return density_[index(x, y, z)];
87}
88
89void FogDensityField::setCurlVelocity(int x, int y, int z, const glm::vec3& velocity) {
90 if (curl_.empty()) return;
91 curl_[index(x, y, z)] = velocity;
92 ++revision_;
93}
94
95glm::vec3 FogDensityField::curlVelocityAt(int x, int y, int z) const noexcept {
96 if (curl_.empty()) return {};
97 return curl_[index(x, y, z)];
98}
99
100void FogDensityField::setLightAssist(int x, int y, int z, const glm::vec3& assist) {
101 if (lightAssist_.empty()) return;
102 lightAssist_[index(x, y, z)] = assist;
103 ++revision_;
104}
105
106glm::vec3 FogDensityField::lightAssistAt(int x, int y, int z) const noexcept {
107 if (lightAssist_.empty()) return {};
108 return lightAssist_[index(x, y, z)];
109}
110
111void FogDensityField::sampleLattice(const glm::vec3& world, int& x0, int& y0, int& z0, int& x1, int& y1,
112 int& z1, float& fx, float& fy, float& fz) const noexcept {
113 const glm::vec3 local = (world - bounds_.minimum) / cellSize_ - glm::vec3(0.5f);
114 const float lx = std::clamp(local.x, 0.f, static_cast<float>(std::max(width_ - 1, 0)));
115 const float ly = std::clamp(local.y, 0.f, static_cast<float>(std::max(height_ - 1, 0)));
116 const float lz = std::clamp(local.z, 0.f, static_cast<float>(std::max(depth_ - 1, 0)));
117 x0 = static_cast<int>(std::floor(lx));
118 y0 = static_cast<int>(std::floor(ly));
119 z0 = static_cast<int>(std::floor(lz));
120 x1 = std::min(x0 + 1, width_ - 1);
121 y1 = std::min(y0 + 1, height_ - 1);
122 z1 = std::min(z0 + 1, depth_ - 1);
123 fx = lx - static_cast<float>(x0);
124 fy = ly - static_cast<float>(y0);
125 fz = lz - static_cast<float>(z0);
126}
127
128float FogDensityField::sampleDensity(const glm::vec3& world) const noexcept {
129 if (density_.empty() || !bounds_.contains(world)) {
130 // Soft falloff just outside bounds keeps ray edges continuous.
131 if (density_.empty()) return 0.f;
132 const glm::vec3 c = glm::clamp(world, bounds_.minimum, bounds_.maximum);
133 if (glm::length(world - c) > cellSize_.x) return 0.f;
134 }
135 int x0, y0, z0, x1, y1, z1;
136 float fx, fy, fz;
137 sampleLattice(world, x0, y0, z0, x1, y1, z1, fx, fy, fz);
138 const auto lerp = [](float a, float b, float t) { return a + (b - a) * t; };
139 const float c00 = lerp(densityAt(x0, y0, z0), densityAt(x1, y0, z0), fx);
140 const float c10 = lerp(densityAt(x0, y1, z0), densityAt(x1, y1, z0), fx);
141 const float c01 = lerp(densityAt(x0, y0, z1), densityAt(x1, y0, z1), fx);
142 const float c11 = lerp(densityAt(x0, y1, z1), densityAt(x1, y1, z1), fx);
143 return lerp(lerp(c00, c10, fy), lerp(c01, c11, fy), fz);
144}
145
146glm::vec3 FogDensityField::sampleCurlVelocity(const glm::vec3& world) const noexcept {
147 if (curl_.empty()) return {};
148 int x0, y0, z0, x1, y1, z1;
149 float fx, fy, fz;
150 sampleLattice(world, x0, y0, z0, x1, y1, z1, fx, fy, fz);
151 const auto lerp3 = [](const glm::vec3& a, const glm::vec3& b, float t) { return a + (b - a) * t; };
152 const glm::vec3 c00 = lerp3(curlVelocityAt(x0, y0, z0), curlVelocityAt(x1, y0, z0), fx);
153 const glm::vec3 c10 = lerp3(curlVelocityAt(x0, y1, z0), curlVelocityAt(x1, y1, z0), fx);
154 const glm::vec3 c01 = lerp3(curlVelocityAt(x0, y0, z1), curlVelocityAt(x1, y0, z1), fx);
155 const glm::vec3 c11 = lerp3(curlVelocityAt(x0, y1, z1), curlVelocityAt(x1, y1, z1), fx);
156 return lerp3(lerp3(c00, c10, fy), lerp3(c01, c11, fy), fz);
157}
158
159glm::vec3 FogDensityField::sampleLightAssist(const glm::vec3& world) const noexcept {
160 if (lightAssist_.empty()) return {};
161 int x0, y0, z0, x1, y1, z1;
162 float fx, fy, fz;
163 sampleLattice(world, x0, y0, z0, x1, y1, z1, fx, fy, fz);
164 const auto lerp3 = [](const glm::vec3& a, const glm::vec3& b, float t) { return a + (b - a) * t; };
165 const glm::vec3 c00 = lerp3(lightAssistAt(x0, y0, z0), lightAssistAt(x1, y0, z0), fx);
166 const glm::vec3 c10 = lerp3(lightAssistAt(x0, y1, z0), lightAssistAt(x1, y1, z0), fx);
167 const glm::vec3 c01 = lerp3(lightAssistAt(x0, y0, z1), lightAssistAt(x1, y0, z1), fx);
168 const glm::vec3 c11 = lerp3(lightAssistAt(x0, y1, z1), lightAssistAt(x1, y1, z1), fx);
169 return lerp3(lerp3(c00, c10, fy), lerp3(c01, c11, fy), fz);
170}
171
172Result<void> FogDensityField::seedHeightBand(float baseDensity, float baseHeight, float falloff,
173 float curlScale, std::uint32_t seed) {
174 if (density_.empty()) {
176 "density field is not allocated", "resize", {},
177 "graphics.fog"));
178 }
179 if (!std::isfinite(baseDensity) || baseDensity < 0.f || !std::isfinite(falloff) || falloff < 0.f ||
180 !std::isfinite(curlScale)) {
182 DiagnosticCode::InvalidArgument, "height-band parameters must be finite and non-negative",
183 "seedHeightBand", {}, "graphics.fog"));
184 }
185
186 for (int z = 0; z < depth_; ++z) {
187 for (int y = 0; y < height_; ++y) {
188 for (int x = 0; x < width_; ++x) {
189 const glm::vec3 p = cellCenter(x, y, z);
190 const float h = std::max(p.y - baseHeight, 0.f);
191 float d = baseDensity * std::exp(-falloff * h);
192 const float n = 0.65f + 0.35f * hash31(x, y, z, seed);
193 d *= n;
194 setDensity(x, y, z, d);
195
196 // Analytic curl-noise style assist: divergence-free on average.
197 const float a = hash31(x + 17, y, z, seed) * 6.2831853f;
198 const float b = hash31(x, y + 31, z, seed) * 6.2831853f;
199 const glm::vec3 curl(curlScale * std::sin(a) * std::cos(b),
200 curlScale * 0.35f * std::sin(a + b),
201 curlScale * std::cos(a) * std::sin(b));
202 setCurlVelocity(x, y, z, curl);
203 }
204 }
205 }
206 // setDensity already bumps revision many times; collapse to one bump.
207 ++revision_;
208 return Result<void>::success();
209}
210
211bool FogDensityField::isOccupied(int x, int y, int z, float threshold) const noexcept {
212 return densityAt(x, y, z) > threshold;
213}
214
215std::span<float> FogDensityField::densitySpan() noexcept { return density_; }
216std::span<const float> FogDensityField::densitySpan() const noexcept { return density_; }
217
218} // namespace eve::graphics::fog
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
glm::vec4 p[6]
Stable, structured diagnostics shared by engine modules.
glm::vec3 n
Definition Grass.cpp:63
std::int32_t c
int h
std::uint32_t height
std::uint32_t width
std::string local
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
World3D * world
std::uint32_t seed
Definition PointSet.cpp:807
float d
float t
std::uint32_t count
float size
Definition TreeMesh.cpp:156
uint32_t index
std::uint32_t depth
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
const FogWorldBounds & bounds() const noexcept
float densityAt(int x, int y, int z) const noexcept
Density at.
bool isOccupied(int x, int y, int z, float threshold=1e-4f) const noexcept
Occupancy bit: true when density exceeds the threshold.
std::span< float > densitySpan() noexcept
Borrow density storage for MAC coupling (same resolution).
glm::vec3 sampleLightAssist(const glm::vec3 &world) const noexcept
Trilinear world-space light-assist sample.
glm::vec3 curlVelocityAt(int x, int y, int z) const noexcept
Curl velocity at.
glm::vec3 sampleCurlVelocity(const glm::vec3 &world) const noexcept
Trilinear world-space curl velocity sample.
void clear()
Clear density / curl / light-assist bands and bump revision.
glm::vec3 lightAssistAt(int x, int y, int z) const noexcept
Light assist at.
Result< void > seedHeightBand(float baseDensity, float baseHeight, float falloff, float curlScale, std::uint32_t seed)
Seed a height-banded density layer with optional curl noise.
Result< void > resize(int width, int height, int depth, const FogWorldBounds &bounds)
Allocate a regular lattice over world bounds.
void setDensity(int x, int y, int z, float density)
Write cell-centered density; indices are clamped.
void setCurlVelocity(int x, int y, int z, const glm::vec3 &velocity)
Write curl-assist velocity (m/s) at a cell center.
float sampleDensity(const glm::vec3 &world) const noexcept
Trilinear world-space density sample.
void setLightAssist(int x, int y, int z, const glm::vec3 &assist)
Accumulated lighting assist (pre-multiplied irradiance hint).
Axis-aligned world bounds for a fog simulation domain.
Definition FogTypes.h:96
glm::vec4 bounds