载入中...
搜索中...
未找到
AtmosphereVolume.cpp
浏览该文件的文档.
5
6#include <algorithm>
7#include <cmath>
8
9#include <glm/common.hpp>
10#include <glm/geometric.hpp>
11
12namespace eve::graphics {
13
15 width_ = std::max(width, 1);
16 height_ = std::max(height, 1);
17 depth_ = std::max(depth, 1);
18 media_.assign(std::size_t(width_) * std::size_t(height_) * std::size_t(depth_), {});
19 integrated_.assign(media_.size(), glm::vec4(0.f, 0.f, 0.f, 1.f));
20}
21
23 std::fill(media_.begin(), media_.end(), FogFroxel{});
24 std::fill(integrated_.begin(), integrated_.end(), glm::vec4(0.f, 0.f, 0.f, 1.f));
25}
26
27void AtmosphereVolume::setDepthRange(float nearDistance, float farDistance) {
28 nearDistance_ = std::max(nearDistance, 1e-3f);
29 farDistance_ = std::max(farDistance, nearDistance_ + 1e-3f);
30}
31
33 if (depth_ <= 0) return nearDistance_;
34 const float t = (float(std::clamp(z, 0, depth_ - 1)) + 0.5f) / float(depth_);
35 return nearDistance_ * std::pow(farDistance_ / nearDistance_, t);
36}
37
39 if (depth_ <= 1) return 0;
40 const float d = std::clamp(distance, nearDistance_, farDistance_);
41 const float t = std::log(d / nearDistance_) / std::log(farDistance_ / nearDistance_);
42 return std::clamp(int(std::floor(t * float(depth_))), 0, depth_ - 1);
43}
44
45std::size_t AtmosphereVolume::index(int x, int y, int z) const {
46 x = std::clamp(x, 0, width_ - 1);
47 y = std::clamp(y, 0, height_ - 1);
48 z = std::clamp(z, 0, depth_ - 1);
49 return (std::size_t(z) * std::size_t(height_) + std::size_t(y)) * std::size_t(width_) +
50 std::size_t(x);
51}
52
53FogFroxel &AtmosphereVolume::at(int x, int y, int z) { return media_[index(x, y, z)]; }
54const FogFroxel &AtmosphereVolume::at(int x, int y, int z) const { return media_[index(x, y, z)]; }
55
56void AtmosphereVolume::injectHeightFog(float baseExtinction, const glm::vec3 &albedo,
57 float baseHeight, float heightFalloff, float minWorldY,
58 float maxWorldY) {
59 const float sigma = std::max(baseExtinction, 0.f);
60 const glm::vec3 omega = glm::clamp(albedo, glm::vec3(0.f), glm::vec3(1.f));
61 const float falloff = std::max(heightFalloff, 0.f);
62 for (int y = 0; y < height_; ++y) {
63 const float ty = (float(y) + 0.5f) / float(height_);
64 const float worldY = minWorldY + (maxWorldY - minWorldY) * ty;
65 const float extinction = sigma * std::exp(-falloff * std::max(worldY - baseHeight, 0.f));
66 for (int z = 0; z < depth_; ++z) {
67 for (int x = 0; x < width_; ++x) {
68 FogFroxel &f = at(x, y, z);
69 f.extinction += extinction;
70 f.scattering += omega * extinction;
71 }
72 }
73 }
74}
75
76void AtmosphereVolume::injectHeightFogFrustum(float baseExtinction, const glm::vec3 &albedo,
77 float baseHeight, float heightFalloff,
78 float minWorldY, float maxWorldY,
79 const glm::mat4 &invViewProj) {
80 const float sigma = std::max(baseExtinction, 0.f);
81 const glm::vec3 omega = glm::clamp(albedo, glm::vec3(0.f), glm::vec3(1.f));
82 const float falloff = std::max(heightFalloff, 0.f);
83 auto unproject = [&](float ndcX, float ndcY, float ndcZ) {
84 const glm::vec4 homogeneous = invViewProj * glm::vec4(ndcX, ndcY, ndcZ, 1.f);
85 return glm::vec3(homogeneous) / homogeneous.w;
86 };
87 for (int y = 0; y < height_; ++y) {
88 const float ndcY = ((float(y) + 0.5f) / float(height_)) * 2.f - 1.f;
89 for (int x = 0; x < width_; ++x) {
90 const float ndcX = ((float(x) + 0.5f) / float(width_)) * 2.f - 1.f;
91 const glm::vec3 nearPoint = unproject(ndcX, ndcY, 0.f);
92 const glm::vec3 farPoint = unproject(ndcX, ndcY, 1.f);
93 for (int z = 0; z < depth_; ++z) {
94 const float depth01 = (sliceDistance(z) - nearDistance_) /
95 (farDistance_ - nearDistance_);
96 const glm::vec3 world = glm::mix(nearPoint, farPoint, depth01);
97 if (world.y < minWorldY || world.y > maxWorldY) continue;
98 const float extinction = sigma * std::exp(-falloff * std::max(world.y - baseHeight, 0.f));
99 FogFroxel &froxel = at(x, y, z);
100 froxel.extinction += extinction;
101 froxel.scattering += omega * extinction;
102 }
103 }
104 }
105}
106
107void AtmosphereVolume::injectLocalVolume(const FogVolume &volume, const glm::vec3 &worldMin,
108 const glm::vec3 &worldMax) {
109 for (int z = 0; z < depth_; ++z) {
110 for (int y = 0; y < height_; ++y) {
111 for (int x = 0; x < width_; ++x) {
112 const glm::vec3 uvw((float(x) + 0.5f) / float(width_),
113 (float(y) + 0.5f) / float(height_),
114 (float(z) + 0.5f) / float(depth_));
115 const glm::vec3 world = worldMin + (worldMax - worldMin) * uvw;
116 const float signedExtinction = volume.sampleExtinction(world);
117 if (std::fabs(signedExtinction) <= 1e-8f) continue;
118 FogFroxel &f = at(x, y, z);
119 const float previous = f.extinction;
120 f.extinction = std::max(0.f, previous + signedExtinction);
121 if (signedExtinction > 0.f) {
122 f.scattering += volume.getAlbedo() * signedExtinction;
123 f.emissive += volume.getEmissive() * signedExtinction;
124 f.anisotropy = volume.getAnisotropy();
125 } else if (previous > 1e-6f) {
126 const float scale = f.extinction / previous;
127 f.scattering *= scale;
128 f.emissive *= scale;
129 }
130 }
131 }
132 }
133}
134
136 const glm::mat4 &invViewProj) {
137 auto unproject = [&](float ndcX, float ndcY, float ndcZ) {
138 glm::vec4 world = invViewProj * glm::vec4(ndcX, ndcY, ndcZ, 1.f);
139 return glm::vec3(world) / world.w;
140 };
141 for (int y = 0; y < height_; ++y) {
142 const float v = (float(y) + 0.5f) / float(height_);
143 for (int x = 0; x < width_; ++x) {
144 const float u = (float(x) + 0.5f) / float(width_);
145 const glm::vec3 nearPoint = unproject(u * 2.f - 1.f, v * 2.f - 1.f, 0.f);
146 const glm::vec3 farPoint = unproject(u * 2.f - 1.f, v * 2.f - 1.f, 1.f);
147 for (int z = 0; z < depth_; ++z) {
148 const float depth01 = (sliceDistance(z) - nearDistance_) /
149 (farDistance_ - nearDistance_);
150 const glm::vec3 world = glm::mix(nearPoint, farPoint, depth01);
151 const float signedExtinction = volume.sampleExtinction(world);
152 if (std::fabs(signedExtinction) <= 1e-8f) continue;
153 FogFroxel &f = at(x, y, z);
154 const float previous = f.extinction;
155 f.extinction = std::max(0.f, previous + signedExtinction);
156 if (signedExtinction > 0.f) {
157 f.scattering += volume.getAlbedo() * signedExtinction;
158 f.emissive += volume.getEmissive() * signedExtinction;
159 f.anisotropy = volume.getAnisotropy();
160 } else if (previous > 1e-6f) {
161 const float scale = f.extinction / previous;
162 f.scattering *= scale;
163 f.emissive *= scale;
164 }
165 }
166 }
167 }
168}
169
171 const glm::vec3 &worldMin,
172 const glm::vec3 &worldMax, float extinctionScale,
173 const glm::vec3 &albedo, float time) {
174 const glm::vec3 omega = glm::clamp(albedo, glm::vec3(0.f), glm::vec3(1.f));
175 for (int z = 0; z < depth_; ++z) {
176 for (int y = 0; y < height_; ++y) {
177 for (int x = 0; x < width_; ++x) {
178 const glm::vec3 uvw((float(x) + 0.5f) / float(width_),
179 (float(y) + 0.5f) / float(height_),
180 (float(z) + 0.5f) / float(depth_));
181 const glm::vec3 world = worldMin + (worldMax - worldMin) * uvw;
182 const float extinction = graph.evaluate(world, time) * extinctionScale;
183 FogFroxel &f = at(x, y, z);
184 const float previous = f.extinction;
185 f.extinction = std::max(0.f, previous + extinction);
186 if (extinction >= 0.f)
187 f.scattering += omega * extinction;
188 else if (previous > 1e-6f)
189 f.scattering *= f.extinction / previous;
190 }
191 }
192 }
193}
194
196 float extinctionScale) {
197 for (int z = 0; z < depth_; ++z) {
198 for (int y = 0; y < height_; ++y) {
199 for (int x = 0; x < width_; ++x) {
200 const FogFroxel source = texture.sample((float(x) + 0.5f) / float(width_),
201 (float(y) + 0.5f) / float(height_), (float(z) + 0.5f) / float(depth_));
202 const float scale = std::max(extinctionScale, 0.f);
203 FogFroxel &target = at(x, y, z);
204 target.extinction += source.extinction * scale;
205 target.scattering += source.scattering * scale;
206 target.emissive += source.emissive * scale;
207 target.anisotropy = source.anisotropy;
208 }
209 }
210 }
211}
212
213void AtmosphereVolume::integrate(const glm::vec3 &lightColor, float phaseScale) {
214 const glm::vec3 light = glm::max(lightColor, glm::vec3(0.f)) * std::max(phaseScale, 0.f);
215 for (int y = 0; y < height_; ++y) {
216 for (int x = 0; x < width_; ++x) {
217 glm::vec3 radiance(0.f);
218 float transmittance = 1.f;
219 float previousDistance = nearDistance_;
220 for (int z = 0; z < depth_; ++z) {
221 const float distance = sliceDistance(z);
222 const float stepLength = std::max(distance - previousDistance, 0.f);
223 previousDistance = distance;
224 const FogFroxel &f = at(x, y, z);
225 const float opticalDepth = f.extinction * stepLength;
226 const float stepTransmittance = std::exp(-opticalDepth);
227 const float integral = f.extinction > 1e-6f
228 ? (1.f - stepTransmittance) / f.extinction
229 : stepLength;
230 radiance += transmittance *
231 (f.scattering * light * f.lightVisibility + f.emissive) * integral;
232 transmittance *= stepTransmittance;
233 integrated_[index(x, y, z)] = glm::vec4(radiance, transmittance);
234 }
235 }
236 }
237}
238
239void AtmosphereVolume::integrateLocalLights(const std::vector<VolumetricLight> &lights,
240 const glm::vec3 &worldMin,
241 const glm::vec3 &worldMax,
242 const glm::vec3 &ambientLight) {
243 for (int y = 0; y < height_; ++y) {
244 for (int x = 0; x < width_; ++x) {
245 glm::vec3 radiance(0.f);
246 float transmittance = 1.f;
247 float previousDistance = nearDistance_;
248 for (int z = 0; z < depth_; ++z) {
249 const glm::vec3 uvw((float(x) + 0.5f) / float(width_),
250 (float(y) + 0.5f) / float(height_),
251 (float(z) + 0.5f) / float(depth_));
252 const glm::vec3 world = worldMin + (worldMax - worldMin) * uvw;
253 glm::vec3 incident = glm::max(ambientLight, glm::vec3(0.f));
254 for (const VolumetricLight &light : lights) {
255 if (!light.enabled || light.radius <= 0.f || light.intensity <= 0.f) continue;
256 const float d = glm::length(world - light.position);
257 if (d >= light.radius) continue;
258 const float radial = 1.f - d / light.radius;
259 incident += glm::max(light.color, glm::vec3(0.f)) * light.intensity *
260 radial * radial;
261 }
262 const float distance = sliceDistance(z);
263 const float stepLength = std::max(distance - previousDistance, 0.f);
264 previousDistance = distance;
265 const FogFroxel &f = at(x, y, z);
266 const float opticalDepth = f.extinction * stepLength;
267 const float stepTransmittance = std::exp(-opticalDepth);
268 const float integral = f.extinction > 1e-6f
269 ? (1.f - stepTransmittance) / f.extinction
270 : stepLength;
271 radiance += transmittance *
272 (f.scattering * incident * f.lightVisibility + f.emissive) * integral;
273 transmittance *= stepTransmittance;
274 integrated_[index(x, y, z)] = glm::vec4(radiance, transmittance);
275 }
276 }
277 }
278}
279
280void AtmosphereVolume::setLightVisibility(int x, int y, int z, float visibility) {
281 at(x, y, z).lightVisibility = std::clamp(visibility, 0.f, 1.f);
282}
283
284std::size_t AtmosphereVolume::blendHistory(const AtmosphereVolume &history, float historyWeight,
285 float rejectionThreshold) {
286 if (history.width_ != width_ || history.height_ != height_ || history.depth_ != depth_)
287 return integrated_.size();
288 const float baseWeight = std::clamp(historyWeight, 0.f, 1.f);
289 const float threshold = std::max(rejectionThreshold, 0.f);
290 std::size_t rejected = 0;
291 for (std::size_t i = 0; i < integrated_.size(); ++i) {
292 const glm::vec4 current = integrated_[i];
293 const glm::vec4 previous = history.integrated_[i];
294 const float currentLuma = glm::dot(glm::vec3(current), glm::vec3(0.2126f, 0.7152f, 0.0722f));
295 const float previousLuma = glm::dot(glm::vec3(previous), glm::vec3(0.2126f, 0.7152f, 0.0722f));
296 const float relativeDelta = std::fabs(currentLuma - previousLuma) /
297 std::max(std::max(currentLuma, previousLuma), 1e-4f);
298 const bool reject = relativeDelta > threshold;
299 if (reject) ++rejected;
300 const float weight = reject ? 0.f : baseWeight;
301 integrated_[i] = glm::mix(current, previous, weight);
302 }
303 return rejected;
304}
305
306const glm::vec4 &AtmosphereVolume::integratedAt(int x, int y, int z) const {
307 return integrated_[index(x, y, z)];
308}
309
310glm::vec4 AtmosphereVolume::sampleIntegrated(float u, float v, float distance) const {
311 if (integrated_.empty()) return glm::vec4(0.f, 0.f, 0.f, 1.f);
312 const int x = std::clamp(int(u * float(width_)), 0, width_ - 1);
313 const int y = std::clamp(int(v * float(height_)), 0, height_ - 1);
315}
316
317FogFroxel AtmosphereVolume::sampleMedia(float u, float v, float w) const {
318 if (media_.empty()) return {};
319 const int x = std::clamp(int(u * float(width_)), 0, width_ - 1);
320 const int y = std::clamp(int(v * float(height_)), 0, height_ - 1);
321 const int z = std::clamp(int(w * float(depth_)), 0, depth_ - 1);
322 return at(x, y, z);
323}
324
325} // namespace eve::graphics
LogicalId target
float w
Definition AnimClip.cpp:738
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
double volume
graphics::Texture * albedo
std::vector< eve::ProcgenProbeDesc > lights
float u
Definition Grass.cpp:233
float v
std::uint32_t height
std::uint32_t width
std::array< float, 3 > scale
float distance
graphics::Canvas * previous
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
float f
World3D * world
float d
float t
double current
Light3D::Data * light
Vec extinction
uint32_t index
const UnitySourceAsset & source
std::uint32_t depth
std::size_t at
Camera-frustum volume used by the volumetric fog passes and CPU references.
void clear()
Remove all media and integrated lighting without changing dimensions.
void setDepthRange(float nearDistance, float farDistance)
Configure the world-distance range represented by logarithmic Z slices.
void setLightVisibility(int x, int y, int z, float visibility)
Set the main-light shadow visibility for one froxel.
FogFroxel & at(int x, int y, int z)
Read or write one froxel; indices are clamped to the grid.
void injectSparseVolume(const SparseVolumeTexture &texture, float extinctionScale=1.f)
Resample a pre-baked sparse volume into this working volume.
void injectHeightFogFrustum(float baseExtinction, const glm::vec3 &albedo, float baseHeight, float heightFalloff, float minWorldY, float maxWorldY, const glm::mat4 &invViewProj)
Add exponential-height fog by reconstructing each froxel in the active camera frustum.
void injectLocalVolumeFrustum(const FogVolume &volume, const glm::mat4 &invViewProj)
Voxelize one analytic local volume in the active camera frustum.
void injectHeightFog(float baseExtinction, const glm::vec3 &albedo, float baseHeight, float heightFalloff, float minWorldY, float maxWorldY)
Add a global exponential-height medium to every froxel.
const glm::vec4 & integratedAt(int x, int y, int z) const
Cumulative RGB in-scattering and A transmittance at a froxel.
std::size_t blendHistory(const AtmosphereVolume &history, float historyWeight, float rejectionThreshold)
Blend integrated lighting with a reprojected history volume.
void resize(int width, int height, int depth)
Resize the froxel grid and clear all media.
float sliceDistance(int z) const
World distance at the center of a Z slice.
void injectDensityGraph(const VolumeDensityGraph &graph, const glm::vec3 &worldMin, const glm::vec3 &worldMax, float extinctionScale, const glm::vec3 &albedo, float time=0.f)
Voxelize a procedural density graph into this volume.
void injectLocalVolume(const FogVolume &volume, const glm::vec3 &worldMin, const glm::vec3 &worldMax)
Voxelize one analytic local volume over the supplied world bounds.
FogFroxel sampleMedia(float u, float v, float w) const
Sample raw media with normalized volume coordinates.
int sliceForDistance(float distance) const
Convert a world distance into a clamped slice index.
glm::vec4 sampleIntegrated(float u, float v, float distance) const
Query cumulative fog for a transparent fragment.
void integrateLocalLights(const std::vector< VolumetricLight > &lights, const glm::vec3 &worldMin, const glm::vec3 &worldMax, const glm::vec3 &ambientLight)
Inject local lights and integrate the volume in one reference pass.
void integrate(const glm::vec3 &lightColor, float phaseScale=1.f)
Integrate scattering and transmittance along every view ray.
Analytic local participating-media volume injected into atmospheric fog.
Definition FogVolume.h:12
Brick-sparse, pre-bakeable participating-media texture.
FogFroxel sample(float u, float v, float w) const
Nearest sample using normalized coordinates.
Compact acyclic program that evaluates procedural volume density.
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
Participating-media coefficients stored in one froxel.
Local light injected into participating media.
glm::vec4 lightColor
glm::mat4 invViewProj