载入中...
搜索中...
未找到
Volumetric.cpp
浏览该文件的文档.
2
5
6#include "common/Exception.h"
7#include "graphics/Canvas.h"
9#include "graphics/Drawable.h"
10#include "graphics/Graphics.h"
12#include "graphics/Shader.h"
13#include "graphics/Texture.h"
14#include "graphics/shaders/volumetric_post_frag_spv.inc"
15#include "graphics/shaders/volumetric_raymarch_frag_spv.inc"
16#include "graphics/shaders/volumetric_fog_frag_spv.inc"
17#include "graphics/shaders/volumetric_froxel_frag_spv.inc"
18#include "graphics/shaders/volumetric_cloud_frag_spv.inc"
19#include "graphics/shaders/volumetric_directional_cookie_frag_spv.inc"
21
22#include <algorithm>
23#include <cmath>
24#include <cstdint>
25#include <limits>
26#include <optional>
27#include <vector>
28
29#include <glm/gtc/matrix_transform.hpp>
30#include <glm/gtc/type_ptr.hpp>
31
32namespace eve::graphics {
33namespace {
34
35Shader *newVolShader(Graphics *gfx, const std::vector<uint32_t> &frag, const char *wgsl) {
36 if (gfx->getBackendName() == "webgpu")
37 return gfx->newShaderFromWgsl({}, std::string(shaders::kVolCommon) + wgsl);
38 return gfx->newShaderFromSpv({}, frag);
39}
40
41Shader *createScreenspaceShader(Graphics *gfx) {
42 if (!gfx) throw eve::Exception("Volumetric: null graphics");
43 std::vector<uint32_t> frag(volumetric_post_frag_spv,
44 volumetric_post_frag_spv + volumetric_post_frag_spv_count);
45 std::vector<uint32_t> vert;
46 Shader *sh = newVolShader(gfx, frag, shaders::kVolScreen);
47 sh->declareFloat("lightX");
48 sh->declareFloat("lightY");
49 sh->declareFloat("exposure");
50 sh->declareFloat("decay");
51 sh->declareFloat("density");
52 sh->declareFloat("weight");
53 sh->declareFloat("sampleCount");
54 sh->declareFloat("dustAmount");
55 sh->declareFloat("fogAmount");
56 sh->declareFloat("fogR");
57 sh->declareFloat("fogG");
58 sh->declareFloat("fogB");
59 sh->declareFloat("shaftR");
60 sh->declareFloat("shaftG");
61 sh->declareFloat("shaftB");
62 sh->declareFloat("time");
63 sh->declareFloat("compositeMode");
64 sh->declareFloat("intensity");
65 sh->declareFloat("spotDx");
66 sh->declareFloat("spotDy");
67 sh->declareFloat("spotCosOuter");
68 sh->declareFloat("spotCosInner");
69
70 sh->sendFloat("lightX", 0.5f);
71 sh->sendFloat("lightY", 0.35f);
72 sh->sendFloat("exposure", 0.35f);
73 sh->sendFloat("decay", 0.96f);
74 sh->sendFloat("density", 0.85f);
75 sh->sendFloat("weight", 0.55f);
76 sh->sendFloat("sampleCount", 48.f);
77 sh->sendFloat("dustAmount", 0.25f);
78 sh->sendFloat("fogAmount", 0.18f);
79 sh->sendFloat("fogR", 0.55f);
80 sh->sendFloat("fogG", 0.62f);
81 sh->sendFloat("fogB", 0.75f);
82 sh->sendFloat("shaftR", 1.f);
83 sh->sendFloat("shaftG", 0.95f);
84 sh->sendFloat("shaftB", 0.85f);
85 sh->sendFloat("time", 0.f);
86 sh->sendFloat("compositeMode", 0.f);
87 sh->sendFloat("intensity", 1.f);
88 // Spot cone off by default (matches lit2d sentinel cos <= -1.5).
89 sh->sendFloat("spotDx", 0.f);
90 sh->sendFloat("spotDy", -1.f);
91 sh->sendFloat("spotCosOuter", -2.f);
92 sh->sendFloat("spotCosInner", -2.f);
93 return sh;
94}
95
96Shader *createRayMarchShader(Graphics *gfx) {
97 if (!gfx) throw eve::Exception("Volumetric: null graphics");
98 std::vector<uint32_t> frag(volumetric_raymarch_frag_spv,
99 volumetric_raymarch_frag_spv + volumetric_raymarch_frag_spv_count);
100 std::vector<uint32_t> vert;
101 Shader *sh = newVolShader(gfx, frag, shaders::kVolRay);
102 sh->declareMatrix("invViewProj");
103 sh->declareFloat("lightDx");
104 sh->declareFloat("lightDy");
105 sh->declareFloat("lightDz");
106 sh->declareFloat("density");
107 sh->declareFloat("shaftR");
108 sh->declareFloat("shaftG");
109 sh->declareFloat("shaftB");
110 sh->declareFloat("intensity");
111 sh->declareFloat("nearZ");
112 sh->declareFloat("farZ");
113 sh->declareFloat("sampleCount");
114 sh->declareFloat("dustAmount");
115 sh->declareFloat("fogAmount");
116 sh->declareFloat("shadowAnisoPack");
117 sh->declareFloat("lightU");
118 sh->declareFloat("lightV");
119
120 sh->sendMatrix("invViewProj", glm::mat4(1.f));
121 sh->sendFloat("lightDx", 0.4f);
122 sh->sendFloat("lightDy", 1.f);
123 sh->sendFloat("lightDz", 0.3f);
124 sh->sendFloat("density", 0.35f);
125 sh->sendFloat("shaftR", 1.f);
126 sh->sendFloat("shaftG", 0.95f);
127 sh->sendFloat("shaftB", 0.85f);
128 sh->sendFloat("intensity", 1.f);
129 sh->sendFloat("nearZ", 0.1f);
130 sh->sendFloat("farZ", 100.f);
131 sh->sendFloat("sampleCount", 24.f);
132 sh->sendFloat("dustAmount", 0.25f);
133 sh->sendFloat("fogAmount", 0.2f);
134 sh->sendFloat("shadowAnisoPack", 8.6f); // 8 steps + default g≈0.6
135 sh->sendFloat("lightU", 0.7f);
136 sh->sendFloat("lightV", 0.2f);
137 return sh;
138}
139
140Shader *createFogShader(Graphics *gfx) {
141 if (!gfx) throw eve::Exception("Volumetric: null graphics");
142 std::vector<uint32_t> frag(volumetric_fog_frag_spv,
143 volumetric_fog_frag_spv + volumetric_fog_frag_spv_count);
144 std::vector<uint32_t> vert;
145 Shader *sh = newVolShader(gfx, frag, shaders::kVolFog);
146 sh->declareMatrix("invViewProj");
147 sh->declareFloat("lightDx");
148 sh->declareFloat("lightDy");
149 sh->declareFloat("lightDz");
150 sh->declareFloat("density");
151 sh->declareFloat("fogR");
152 sh->declareFloat("fogG");
153 sh->declareFloat("fogB");
154 sh->declareFloat("intensity");
155 sh->declareFloat("nearZ");
156 sh->declareFloat("farZ");
157 sh->declareFloat("sampleCount");
158 sh->declareFloat("fogHeight");
159 sh->declareFloat("heightFalloff");
160 sh->declareFloat("fogStart");
161 sh->declareFloat("fogEnd");
162 sh->declareFloat("noiseAmount");
163
164 sh->sendMatrix("invViewProj", glm::mat4(1.f));
165 sh->sendFloat("lightDx", 0.4f);
166 sh->sendFloat("lightDy", 1.f);
167 sh->sendFloat("lightDz", 0.3f);
168 sh->sendFloat("density", 0.25f);
169 sh->sendFloat("fogR", 0.55f);
170 sh->sendFloat("fogG", 0.62f);
171 sh->sendFloat("fogB", 0.75f);
172 sh->sendFloat("intensity", 1.f);
173 sh->sendFloat("nearZ", 0.1f);
174 sh->sendFloat("farZ", 100.f);
175 sh->sendFloat("sampleCount", 24.f);
176 sh->sendFloat("fogHeight", 0.f);
177 sh->sendFloat("heightFalloff", 0.15f);
178 sh->sendFloat("fogStart", 2.f);
179 sh->sendFloat("fogEnd", 40.f);
180 sh->sendFloat("noiseAmount", 0.35f);
181 return sh;
182}
183
184Shader *createFroxelShader(Graphics *gfx) {
185 if (!gfx) throw eve::Exception("Volumetric: null graphics");
186 std::vector<uint32_t> frag(volumetric_froxel_frag_spv,
187 volumetric_froxel_frag_spv + volumetric_froxel_frag_spv_count);
188 std::vector<uint32_t> vert;
189 Shader *sh = newVolShader(gfx, frag, shaders::kVolFroxel);
190 sh->declareFloat("atlasCols");
191 sh->declareFloat("atlasRows");
192 sh->declareFloat("sliceCount");
193 sh->declareFloat("nearDistance");
194 sh->declareFloat("farDistance");
195 sh->sendFloat("atlasCols", 1.f);
196 sh->sendFloat("atlasRows", 1.f);
197 sh->sendFloat("sliceCount", 1.f);
198 sh->sendFloat("nearDistance", 0.1f);
199 sh->sendFloat("farDistance", 100.f);
200 return sh;
201}
202
203Shader *createDirectionalCookieShader(Graphics *gfx) {
204 std::vector<uint32_t> frag(volumetric_directional_cookie_frag_spv,
205 volumetric_directional_cookie_frag_spv +
206 volumetric_directional_cookie_frag_spv_count);
207 Shader *sh = newVolShader(gfx, frag, shaders::kVolDirectionalCookie);
208 sh->declareMatrix("invViewProj");
209 sh->declareFloat("worldSize");
210 sh->declareFloat("intensity");
211 sh->declareFloat("lightDx");
212 sh->declareFloat("lightDy");
213 sh->declareFloat("lightDz");
214 return sh;
215}
216
217Shader *createCloudShader(Graphics *gfx) {
218 if (!gfx) throw eve::Exception("Volumetric: null graphics");
219 std::vector<uint32_t> frag(volumetric_cloud_frag_spv,
220 volumetric_cloud_frag_spv + volumetric_cloud_frag_spv_count);
221 std::vector<uint32_t> vert;
222 Shader *sh = newVolShader(gfx, frag, shaders::kVolCloud);
223 sh->declareMatrix("invViewProj");
224 sh->declareFloat("lightDx");
225 sh->declareFloat("lightDy");
226 sh->declareFloat("lightDz");
227 sh->declareFloat("time");
228 sh->declareFloat("cloudBottom");
229 sh->declareFloat("cloudTop");
230 sh->declareFloat("cloudCoverage");
231 sh->declareFloat("cloudDensity");
232 sh->declareFloat("cloudScale");
233 sh->declareFloat("cloudWindX");
234 sh->declareFloat("cloudWindZ");
235 sh->declareFloat("sampleCount");
236 sh->declareFloat("shadowSteps");
237 sh->declareFloat("cloudLightR");
238 sh->declareFloat("cloudLightG");
239 sh->declareFloat("cloudLightB");
240 sh->sendMatrix("invViewProj", glm::mat4(1.f));
241 sh->sendFloat("lightDx", 0.4f);
242 sh->sendFloat("lightDy", 1.f);
243 sh->sendFloat("lightDz", 0.3f);
244 sh->sendFloat("time", 0.f);
245 sh->sendFloat("cloudBottom", 8.f);
246 sh->sendFloat("cloudTop", 16.f);
247 sh->sendFloat("cloudCoverage", 0.55f);
248 sh->sendFloat("cloudDensity", 1.f);
249 sh->sendFloat("cloudScale", 18.f);
250 sh->sendFloat("cloudWindX", 1.5f);
251 sh->sendFloat("cloudWindZ", 0.4f);
252 sh->sendFloat("sampleCount", 32.f);
253 sh->sendFloat("shadowSteps", 6.f);
254 sh->sendFloat("cloudLightR", 1.f);
255 sh->sendFloat("cloudLightG", 0.92f);
256 sh->sendFloat("cloudLightB", 0.78f);
257 return sh;
258}
259
260} // namespace
261
263 shader_ = createScreenspaceShader(gfx);
264 rayShader_ = createRayMarchShader(gfx);
265 fogShader_ = createFogShader(gfx);
266 directionalCookieShader_ = createDirectionalCookieShader(gfx);
267 cloudShader_ = createCloudShader(gfx);
268 froxelShader_ = createFroxelShader(gfx);
269 atmosphereVolume_ = std::make_unique<AtmosphereVolume>();
270 applyQualityDefaults();
271}
272
273Volumetric::~Volumetric() = default;
274
275void Volumetric::applyQualityDefaults() {
276 auto sendRayShadow = [this](float steps) {
277 const float g = std::clamp(anisotropy_, -0.99f, 0.99f);
278 const float frac = std::clamp((g + 0.99f) / 1.98f, 0.005f, 0.995f);
279 rayShader_->sendFloat("shadowAnisoPack", std::floor(steps) + frac);
280 };
281 if (quality_ == "low") {
282 downscale_ = 4.f;
283 if (mode_ == "raymarch") {
284 rayShader_->sendFloat("sampleCount", 8.f);
285 sendRayShadow(4.f);
286 rayShader_->sendFloat("dustAmount", 0.12f);
287 rayShader_->sendFloat("fogAmount", 0.12f);
288 } else if (mode_ == "fog") {
289 fogShader_->sendFloat("sampleCount", 8.f);
290 fogShader_->sendFloat("noiseAmount", 0.15f);
291 fogShader_->sendFloat("density", 0.18f);
292 } else if (mode_ == "cloud") {
293 cloudShader_->sendFloat("sampleCount", 16.f);
294 cloudShader_->sendFloat("shadowSteps", 3.f);
295 } else {
296 setFloat("sampleCount", 16.f);
297 setFloat("dustAmount", 0.12f);
298 setFloat("fogAmount", 0.10f);
299 setFloat("exposure", 0.40f);
300 }
301 } else if (quality_ == "high") {
302 downscale_ = 1.f;
303 if (mode_ == "raymarch") {
304 rayShader_->sendFloat("sampleCount", 48.f);
305 sendRayShadow(16.f);
306 rayShader_->sendFloat("dustAmount", 0.35f);
307 rayShader_->sendFloat("fogAmount", 0.25f);
308 } else if (mode_ == "fog") {
309 fogShader_->sendFloat("sampleCount", 48.f);
310 fogShader_->sendFloat("noiseAmount", 0.45f);
311 fogShader_->sendFloat("density", 0.32f);
312 } else if (mode_ == "cloud") {
313 cloudShader_->sendFloat("sampleCount", 64.f);
314 cloudShader_->sendFloat("shadowSteps", 10.f);
315 } else {
316 setFloat("sampleCount", 96.f);
317 setFloat("dustAmount", 0.35f);
318 setFloat("fogAmount", 0.22f);
319 setFloat("exposure", 0.32f);
320 }
321 } else {
322 quality_ = "medium";
323 downscale_ = 2.f;
324 if (mode_ == "raymarch") {
325 rayShader_->sendFloat("sampleCount", 24.f);
326 sendRayShadow(8.f);
327 rayShader_->sendFloat("dustAmount", 0.25f);
328 rayShader_->sendFloat("fogAmount", 0.2f);
329 } else if (mode_ == "fog") {
330 fogShader_->sendFloat("sampleCount", 24.f);
331 fogShader_->sendFloat("noiseAmount", 0.35f);
332 fogShader_->sendFloat("density", 0.25f);
333 } else if (mode_ == "cloud") {
334 cloudShader_->sendFloat("sampleCount", 32.f);
335 cloudShader_->sendFloat("shadowSteps", 6.f);
336 } else {
337 setFloat("sampleCount", 48.f);
338 setFloat("dustAmount", 0.25f);
339 setFloat("fogAmount", 0.18f);
340 setFloat("exposure", 0.35f);
341 }
342 }
343}
344
345void Volumetric::setQuality(const std::string &quality) {
346 quality_ = quality;
347 applyQualityDefaults();
348}
349
350void Volumetric::setMode(const std::string &mode) {
351 if (mode == "raymarch")
352 mode_ = "raymarch";
353 else if (mode == "fog")
354 mode_ = "fog";
355 else if (mode == "froxel")
356 mode_ = "froxel";
357 else if (mode == "cloud")
358 mode_ = "cloud";
359 else
360 mode_ = "screenspace";
361 applyQualityDefaults();
362}
363
364void Volumetric::setLightScreenUV(float u, float v) {
365 setFloat("lightX", u);
366 setFloat("lightY", v);
367 rayShader_->sendFloat("lightU", u);
368 rayShader_->sendFloat("lightV", v);
369}
370
371float Volumetric::getLightScreenU() const { return getFloat("lightX"); }
372float Volumetric::getLightScreenV() const { return getFloat("lightY"); }
373
374void Volumetric::setLightScreenPos(float x, float y, float width, float height) {
375 if (width < 1.f) width = 1.f;
376 if (height < 1.f) height = 1.f;
378}
379
380void Volumetric::setLightDirection(float dx, float dy, float dz) {
381 glm::vec3 d(dx, dy, dz);
382 if (glm::length(d) < 1e-6f) d = glm::vec3(0.f, 1.f, 0.f);
383 else d = glm::normalize(d);
384 lightDir_ = d;
385 rayShader_->sendFloat("lightDx", d.x);
386 rayShader_->sendFloat("lightDy", d.y);
387 rayShader_->sendFloat("lightDz", d.z);
388 fogShader_->sendFloat("lightDx", d.x);
389 fogShader_->sendFloat("lightDy", d.y);
390 fogShader_->sendFloat("lightDz", d.z);
391 cloudShader_->sendFloat("lightDx", d.x);
392 cloudShader_->sendFloat("lightDy", d.y);
393 cloudShader_->sendFloat("lightDz", d.z);
394}
395
397 invViewProj_ = invViewProj;
398 rayShader_->sendMatrix("invViewProj", invViewProj_);
399 fogShader_->sendMatrix("invViewProj", invViewProj_);
400 cloudShader_->sendMatrix("invViewProj", invViewProj_);
401}
402
403void Volumetric::setCamera(float eyeX, float eyeY, float eyeZ, float targetX, float targetY,
404 float targetZ, float upX, float upY, float upZ, float fovYDeg,
405 float aspect, float nearZ, float farZ) {
406 nearZ_ = nearZ > 1e-4f ? nearZ : 0.1f;
407 farZ_ = farZ > nearZ_ ? farZ : nearZ_ + 1.f;
408 const float aspectSafe = aspect > 1e-4f ? aspect : 1.f;
409 const float fovRad = fovYDeg * 0.017453292519943295f;
410 const glm::mat4 view =
411 glm::lookAtRH(glm::vec3(eyeX, eyeY, eyeZ), glm::vec3(targetX, targetY, targetZ),
412 glm::vec3(upX, upY, upZ));
413 const glm::mat4 proj = perspectiveVulkanRH_ZO(fovRad, aspectSafe, nearZ_, farZ_);
414 setInvViewProj(glm::inverse(proj * view));
415 rayShader_->sendFloat("nearZ", nearZ_);
416 rayShader_->sendFloat("farZ", farZ_);
417 fogShader_->sendFloat("nearZ", nearZ_);
418 fogShader_->sendFloat("farZ", farZ_);
419}
420
421void Volumetric::setShaftColor(float r, float g, float b) {
422 setFloat("shaftR", r);
423 setFloat("shaftG", g);
424 setFloat("shaftB", b);
425 rayShader_->sendFloat("shaftR", r);
426 rayShader_->sendFloat("shaftG", g);
427 rayShader_->sendFloat("shaftB", b);
428}
429
430void Volumetric::setFogColor(float r, float g, float b) {
431 setFloat("fogR", r);
432 setFloat("fogG", g);
433 setFloat("fogB", b);
434 fogShader_->sendFloat("fogR", r);
435 fogShader_->sendFloat("fogG", g);
436 fogShader_->sendFloat("fogB", b);
437}
438
439void Volumetric::setIntensity(float intensity) {
440 setFloat("intensity", intensity);
441 rayShader_->sendFloat("intensity", intensity);
442 fogShader_->sendFloat("intensity", intensity);
443}
444
445void Volumetric::setTime(float seconds) { setFloat("time", seconds); }
446
447void Volumetric::setDensity(float density) {
448 setFloat("density", density);
449 rayShader_->sendFloat("density", density);
450 fogShader_->sendFloat("density", density);
451}
452
453void Volumetric::setFogHeight(float worldY) {
454 fogHeight_ = worldY;
455 fogShader_->sendFloat("fogHeight", fogHeight_);
456}
457
459 fogHeightFalloff_ = falloff < 0.f ? 0.f : falloff;
460 fogShader_->sendFloat("heightFalloff", fogHeightFalloff_);
461}
462
463void Volumetric::setFogStart(float startDistance) {
464 fogStart_ = startDistance < 0.f ? 0.f : startDistance;
465 fogShader_->sendFloat("fogStart", fogStart_);
466}
467
468void Volumetric::setFogEnd(float endDistance) {
469 fogEnd_ = endDistance <= fogStart_ ? fogStart_ + 1.f : endDistance;
470 fogShader_->sendFloat("fogEnd", fogEnd_);
471}
472
473void Volumetric::setFogNoise(float amount) {
474 fogNoise_ = amount < 0.f ? 0.f : amount;
475 fogShader_->sendFloat("noiseAmount", fogNoise_);
476}
477
479 cloudBottom_ = bottom;
480 cloudTop_ = top > bottom + 0.01f ? top : bottom + 0.01f;
481 cloudShader_->sendFloat("cloudBottom", cloudBottom_);
482 cloudShader_->sendFloat("cloudTop", cloudTop_);
483}
484
485void Volumetric::setCloudCoverage(float coverage) {
486 cloudCoverage_ = std::clamp(coverage, 0.f, 1.f);
487 cloudShader_->sendFloat("cloudCoverage", cloudCoverage_);
488}
489
490void Volumetric::setCloudDensity(float density) {
491 cloudDensity_ = std::max(density, 0.f);
492 cloudShader_->sendFloat("cloudDensity", cloudDensity_);
493}
494
495void Volumetric::setCloudScale(float worldScale) {
496 cloudScale_ = std::max(worldScale, 0.01f);
497 cloudShader_->sendFloat("cloudScale", cloudScale_);
498}
499
500void Volumetric::setCloudWind(float x, float z) {
501 cloudWind_ = glm::vec3(x, 0.f, z);
502 cloudShader_->sendFloat("cloudWindX", x);
503 cloudShader_->sendFloat("cloudWindZ", z);
504}
505
506void Volumetric::setCloudLightColor(float r, float g, float b) {
507 cloudLightColor_ = glm::vec3(std::max(r, 0.f), std::max(g, 0.f), std::max(b, 0.f));
508 cloudShader_->sendFloat("cloudLightR", cloudLightColor_.x);
509 cloudShader_->sendFloat("cloudLightG", cloudLightColor_.y);
510 cloudShader_->sendFloat("cloudLightB", cloudLightColor_.z);
511}
512
513bool Volumetric::hasParam(const std::string &name) const {
514 return (shader_ && shader_->hasUniform(name)) || (rayShader_ && rayShader_->hasUniform(name)) ||
515 (fogShader_ && fogShader_->hasUniform(name)) ||
516 (cloudShader_ && cloudShader_->hasUniform(name));
517}
518
519void Volumetric::setFloat(const std::string &name, float value) {
520 if (!shader_) throw eve::Exception("Volumetric.setFloat: null shader");
521 if (shader_->hasUniform(name)) shader_->sendFloat(name, value);
522 if (rayShader_ && rayShader_->hasUniform(name)) rayShader_->sendFloat(name, value);
523 if (fogShader_ && fogShader_->hasUniform(name)) fogShader_->sendFloat(name, value);
524 if (cloudShader_ && cloudShader_->hasUniform(name)) cloudShader_->sendFloat(name, value);
525}
526
527float Volumetric::getFloat(const std::string &name) const {
528 // Prefer the active mode's shader so quality/sample queries match setMode.
529 if (mode_ == "raymarch" && rayShader_ && rayShader_->hasUniform(name)) {
530 float v = 0.f;
531 if (rayShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
532 }
533 if (mode_ == "fog" && fogShader_ && fogShader_->hasUniform(name)) {
534 float v = 0.f;
535 if (fogShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
536 }
537 if (mode_ == "cloud" && cloudShader_ && cloudShader_->hasUniform(name)) {
538 float v = 0.f;
539 if (cloudShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
540 }
541 if (shader_ && shader_->hasUniform(name)) {
542 float v = 0.f;
543 if (shader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
544 }
545 if (rayShader_ && rayShader_->hasUniform(name)) {
546 float v = 0.f;
547 if (rayShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
548 }
549 if (fogShader_ && fogShader_->hasUniform(name)) {
550 float v = 0.f;
551 if (fogShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
552 }
553 if (cloudShader_ && cloudShader_->hasUniform(name)) {
554 float v = 0.f;
555 if (cloudShader_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
556 }
557 throw eve::Exception("Volumetric.getFloat: missing param '%s'", name.c_str());
558}
559
560int Volumetric::getSampleCount() const { return int(std::lround(getFloat("sampleCount"))); }
561
562int Volumetric::resolutionFor(int fullSize) const {
563 if (fullSize < 1) return 1;
564 const float ds = std::max(downscale_, 1.f);
565 return std::max(1, int(std::floor(float(fullSize) / ds)));
566}
567
568void Volumetric::uploadCommon(bool compositeFromScene) {
569 setFloat("compositeMode", compositeFromScene ? 1.f : 0.f);
570}
571
572void Volumetric::uploadRayMarchCommon() {
573 rayShader_->sendMatrix("invViewProj", invViewProj_);
574 rayShader_->sendFloat("lightDx", lightDir_.x);
575 rayShader_->sendFloat("lightDy", lightDir_.y);
576 rayShader_->sendFloat("lightDz", lightDir_.z);
577 rayShader_->sendFloat("nearZ", nearZ_);
578 rayShader_->sendFloat("farZ", farZ_);
579 uploadRayMarchShadowAnisotropy();
580}
581
582void Volumetric::uploadFogCommon() {
583 fogShader_->sendMatrix("invViewProj", invViewProj_);
584 fogShader_->sendFloat("lightDx", lightDir_.x);
585 fogShader_->sendFloat("lightDy", lightDir_.y);
586 fogShader_->sendFloat("lightDz", lightDir_.z);
587 fogShader_->sendFloat("nearZ", nearZ_);
588 fogShader_->sendFloat("farZ", farZ_);
589 fogShader_->sendFloat("fogHeight", fogHeight_);
590 fogShader_->sendFloat("heightFalloff", fogHeightFalloff_);
591 fogShader_->sendFloat("fogStart", fogStart_);
592 fogShader_->sendFloat("fogEnd", fogEnd_);
593 fogShader_->sendFloat("noiseAmount", fogNoise_);
594}
595
596void Volumetric::uploadCloudCommon() {
597 cloudShader_->sendMatrix("invViewProj", invViewProj_);
598 cloudShader_->sendFloat("lightDx", lightDir_.x);
599 cloudShader_->sendFloat("lightDy", lightDir_.y);
600 cloudShader_->sendFloat("lightDz", lightDir_.z);
601 cloudShader_->sendFloat("cloudBottom", cloudBottom_);
602 cloudShader_->sendFloat("cloudTop", cloudTop_);
603 cloudShader_->sendFloat("cloudCoverage", cloudCoverage_);
604 cloudShader_->sendFloat("cloudDensity", cloudDensity_);
605 cloudShader_->sendFloat("cloudScale", cloudScale_);
606 cloudShader_->sendFloat("cloudWindX", cloudWind_.x);
607 cloudShader_->sendFloat("cloudWindZ", cloudWind_.z);
608 cloudShader_->sendFloat("cloudLightR", cloudLightColor_.x);
609 cloudShader_->sendFloat("cloudLightG", cloudLightColor_.y);
610 cloudShader_->sendFloat("cloudLightB", cloudLightColor_.z);
611}
612
613void Volumetric::drawFullscreen(Graphics *gfx, Texture *source, Shader *shader) {
614 if (!gfx) throw eve::Exception("Volumetric: null graphics");
615 if (!source) throw eve::Exception("Volumetric: null source texture");
616 if (!shader) throw eve::Exception("Volumetric: null shader");
617
618 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
619 const float dh =
620 gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
621 gfx->drawTexturedRectShader(source, shader, 0.f, 0.f, dw, dh, Color(1.f, 1.f, 1.f, 1.f));
622}
623
624void Volumetric::beginOcclusionMap(Graphics *gfx, float lightPixelX, float lightPixelY,
625 float lightRadiusPixels) {
626 if (!gfx) throw eve::Exception("Volumetric.beginOcclusionMap: null graphics");
627 gfx->clear(Color(0.f, 0.f, 0.f, 1.f), std::nullopt, std::nullopt);
628 const float r = std::max(lightRadiusPixels, 1.f);
629 gfx->drawSolidRect(lightPixelX - r, lightPixelY - r, r * 2.f, r * 2.f,
630 Color(1.f, 1.f, 1.f, 1.f));
631 const float w = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
632 const float h = gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
633 setLightScreenPos(lightPixelX, lightPixelY, w, h);
634}
635
636void Volumetric::drawOccluder(Graphics *gfx, Drawable *drawable, const glm::mat4 &matrix) {
637 if (!gfx || !drawable) return;
638 gfx->drawOcclusion(drawable, matrix);
639}
640
641void Volumetric::drawOccluderSolid(Graphics *gfx, float x, float y, float w, float h) {
642 if (!gfx) return;
643 gfx->drawOcclusionSolid(x, y, w, h);
644}
645
646void Volumetric::drawOccluderTexture(Graphics *gfx, Texture *texture, float x, float y, float w,
647 float h) {
648 if (!gfx) return;
649 gfx->drawOcclusionTexture(texture, x, y, w, h);
650}
651
653 if (!gfx) return;
654 if (ecs::current()->getManager<Renderable2D>() == nullptr) return;
655 auto view = ecs::View<Renderable2D, Renderable2D::Transform2D, Renderable2D::Sprite>();
656 for (auto it = view.begin(); it != view.end(); ++it) {
657 auto [xf, sp] = *it;
658 if (!sp->visible || !sp->castOcclusion) continue;
659 const float w = sp->width * (xf->sx == 0.f ? 1.f : xf->sx);
660 const float h = sp->height * (xf->sy == 0.f ? 1.f : xf->sy);
661 if (sp->texture)
662 gfx->drawOcclusionTexture(sp->texture, xf->x, xf->y, w, h);
663 else
664 gfx->drawOcclusionSolid(xf->x, xf->y, w, h);
665 }
666}
667
668void Volumetric::scatter(Graphics *gfx, Texture *occlusion) {
669 uploadCommon(false);
670 drawFullscreen(gfx, occlusion, shader_);
671}
672
673void Volumetric::scatterTo(Graphics *gfx, Texture *occlusion, Canvas *dest) {
674 if (!gfx) throw eve::Exception("Volumetric.scatterTo: null graphics");
675 if (!dest) throw eve::Exception("Volumetric.scatterTo: null dest");
676 Canvas *prev = gfx->getCanvas();
677 gfx->setCanvas(dest);
678 scatter(gfx, occlusion);
679 gfx->setCanvas(prev);
680}
681
683 uploadCommon(true);
684 drawFullscreen(gfx, scene, shader_);
685}
686
688 if (!gfx) throw eve::Exception("Volumetric.applyFromSceneTo: null graphics");
689 if (!dest) throw eve::Exception("Volumetric.applyFromSceneTo: null dest");
690 Canvas *prev = gfx->getCanvas();
691 gfx->setCanvas(dest);
692 applyFromScene(gfx, scene);
693 gfx->setCanvas(prev);
694}
695
696void Volumetric::rayMarch(Graphics *gfx, Texture *linearDepth) {
697 uploadRayMarchCommon();
698 drawFullscreen(gfx, linearDepth, rayShader_);
699}
700
701void Volumetric::rayMarchTo(Graphics *gfx, Texture *linearDepth, Canvas *dest) {
702 if (!gfx) throw eve::Exception("Volumetric.rayMarchTo: null graphics");
703 if (!dest) throw eve::Exception("Volumetric.rayMarchTo: null dest");
704 Canvas *prev = gfx->getCanvas();
705 gfx->setCanvas(dest);
706 rayMarch(gfx, linearDepth);
707 gfx->setCanvas(prev);
708}
709
710void Volumetric::applyFog(Graphics *gfx, Texture *linearDepth) {
711 uploadFogCommon();
712 drawFullscreen(gfx, linearDepth, fogShader_);
713}
714
715void Volumetric::applyFogTo(Graphics *gfx, Texture *linearDepth, Canvas *dest) {
716 if (!gfx) throw eve::Exception("Volumetric.applyFogTo: null graphics");
717 if (!dest) throw eve::Exception("Volumetric.applyFogTo: null dest");
718 Canvas *prev = gfx->getCanvas();
719 gfx->setCanvas(dest);
720 applyFog(gfx, linearDepth);
721 gfx->setCanvas(prev);
722}
723
725 float worldSize, float intensity) {
726 if (!gfx || !depth || !cookie || !std::isfinite(worldSize) || worldSize <= 0.f ||
727 !std::isfinite(intensity) || intensity < 0.f)
730 "Volumetric.projectDirectionalCookie: valid providers and finite non-negative settings required"));
731 directionalCookieShader_->sendMatrix("invViewProj", invViewProj_);
732 directionalCookieShader_->sendFloat("worldSize", worldSize);
733 directionalCookieShader_->sendFloat("intensity", intensity);
734 directionalCookieShader_->sendFloat("lightDx", lightDir_.x);
735 directionalCookieShader_->sendFloat("lightDy", lightDir_.y);
736 directionalCookieShader_->sendFloat("lightDz", lightDir_.z);
737 const float width = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
738 const float height = gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
739 gfx->drawTexturedRectShaderDepth(cookie, depth, directionalCookieShader_, 0.f, 0.f, width,
740 height, Color(1.f, 1.f, 1.f, 1.f));
741 return Result<void>::success();
742}
743
744void Volumetric::renderClouds(Graphics *gfx, Texture *linearDepth) {
745 uploadCloudCommon();
746 drawFullscreen(gfx, linearDepth, cloudShader_);
747}
748
749void Volumetric::renderCloudsTo(Graphics *gfx, Texture *linearDepth, Canvas *dest) {
750 if (!gfx) throw eve::Exception("Volumetric.renderCloudsTo: null graphics");
751 if (!dest) throw eve::Exception("Volumetric.renderCloudsTo: null dest");
752 Canvas *prev = gfx->getCanvas();
753 gfx->setCanvas(dest);
754 renderClouds(gfx, linearDepth);
755 gfx->setCanvas(prev);
756}
757
758void Volumetric::configureFroxelGrid(int width, int height, int depth, float nearDistance,
759 float farDistance) {
760 atmosphereVolume_->resize(width, height, depth);
761 atmosphereVolume_->setDepthRange(nearDistance, farDistance);
762 froxelAtlasCols_ = std::max(1, int(std::ceil(std::sqrt(float(atmosphereVolume_->getDepth())))));
763 froxelAtlasRows_ = std::max(1, (atmosphereVolume_->getDepth() + froxelAtlasCols_ - 1) /
764 froxelAtlasCols_);
765}
766
767void Volumetric::clearFroxelGrid() { atmosphereVolume_->clear(); }
768
769void Volumetric::injectFroxelHeightFog(float extinction, float albedoR, float albedoG,
770 float albedoB, float baseHeight, float heightFalloff,
771 float minWorldY, float maxWorldY) {
772 atmosphereVolume_->injectHeightFogFrustum(extinction, glm::vec3(albedoR, albedoG, albedoB),
773 baseHeight, heightFalloff, minWorldY, maxWorldY,
774 invViewProj_);
775}
776
778 if (!volume) throw eve::Exception("Volumetric.injectFroxelLocalVolume: null volume");
779 atmosphereVolume_->injectLocalVolumeFrustum(*volume, invViewProj_);
780}
781
782void Volumetric::integrateFroxel(float lightR, float lightG, float lightB, float phaseScale) {
783 if (!pendingEmissiveProxies_.empty()) {
784 const glm::vec3 ambient(std::max(lightR, 0.f), std::max(lightG, 0.f),
785 std::max(lightB, 0.f));
786 // Reconstruct a camera-frustum AABB from invViewProj so proxies track the view.
787 glm::vec3 worldMin(std::numeric_limits<float>::max());
788 glm::vec3 worldMax(std::numeric_limits<float>::lowest());
789 for (float ndcZ : {0.f, 1.f}) {
790 for (float x : {-1.f, 1.f}) {
791 for (float y : {-1.f, 1.f}) {
792 const glm::vec4 h = invViewProj_ * glm::vec4(x, y, ndcZ, 1.f);
793 if (!(std::fabs(h.w) > 1e-6f)) continue;
794 const glm::vec3 p = glm::vec3(h) / h.w;
795 if (!std::isfinite(p.x) || !std::isfinite(p.y) || !std::isfinite(p.z)) continue;
796 worldMin = glm::min(worldMin, p);
797 worldMax = glm::max(worldMax, p);
798 }
799 }
800 }
801 if (!(worldMin.x < worldMax.x && worldMin.y < worldMax.y && worldMin.z < worldMax.z)) {
802 worldMin = glm::vec3(-farZ_);
803 worldMax = glm::vec3(farZ_);
804 }
805 atmosphereVolume_->integrateLocalLights(pendingEmissiveProxies_, worldMin, worldMax,
806 ambient * std::max(phaseScale, 0.f));
807 clearPendingEmissiveProxies();
808 return;
809 }
810 atmosphereVolume_->integrate(glm::vec3(lightR, lightG, lightB), phaseScale);
811}
812
814 if (!gfx) throw eve::Exception("Volumetric.uploadFroxel: null graphics");
815 const int w = atmosphereVolume_->getWidth();
816 const int h = atmosphereVolume_->getHeight();
817 const int d = atmosphereVolume_->getDepth();
818 if (w <= 0 || h <= 0 || d <= 0)
819 throw eve::Exception("Volumetric.uploadFroxel: grid is not configured");
820 const int atlasW = w * froxelAtlasCols_;
821 const int atlasH = h * froxelAtlasRows_;
822 std::vector<uint8_t> rgba(std::size_t(atlasW) * std::size_t(atlasH) * 4u, 0u);
823 for (int z = 0; z < d; ++z) {
824 const int ox = (z % froxelAtlasCols_) * w;
825 const int oy = (z / froxelAtlasCols_) * h;
826 for (int y = 0; y < h; ++y) {
827 for (int x = 0; x < w; ++x) {
828 const glm::vec4 value = atmosphereVolume_->integratedAt(x, y, z);
829 const std::size_t offset = (std::size_t(oy + y) * std::size_t(atlasW) +
830 std::size_t(ox + x)) * 4u;
831 const glm::vec3 mapped = glm::vec3(value) / (glm::vec3(1.f) + glm::vec3(value));
832 rgba[offset + 0] = uint8_t(std::lround(glm::clamp(mapped.r, 0.f, 1.f) * 255.f));
833 rgba[offset + 1] = uint8_t(std::lround(glm::clamp(mapped.g, 0.f, 1.f) * 255.f));
834 rgba[offset + 2] = uint8_t(std::lround(glm::clamp(mapped.b, 0.f, 1.f) * 255.f));
835 rgba[offset + 3] = uint8_t(std::lround(glm::clamp(value.a, 0.f, 1.f) * 255.f));
836 }
837 }
838 }
839 if (!froxelAtlas_ || froxelAtlas_->getWidth() != atlasW || froxelAtlas_->getHeight() != atlasH)
840 froxelAtlas_ = gfx->newTexture(atlasW, atlasH, rgba.data());
841 else
842 gfx->updateTexture(froxelAtlas_, atlasW, atlasH, rgba.data());
843 froxelShader_->sendFloat("atlasCols", float(froxelAtlasCols_));
844 froxelShader_->sendFloat("atlasRows", float(froxelAtlasRows_));
845 froxelShader_->sendFloat("sliceCount", float(d));
846 froxelShader_->sendFloat("nearDistance", atmosphereVolume_->getNearDistance());
847 froxelShader_->sendFloat("farDistance", atmosphereVolume_->getFarDistance());
848}
849
850void Volumetric::applyFroxel(Graphics *gfx, Texture *linearDepth) {
851 if (!gfx) throw eve::Exception("Volumetric.applyFroxel: null graphics");
852 if (!linearDepth) throw eve::Exception("Volumetric.applyFroxel: null depth");
853 if (!froxelAtlas_) throw eve::Exception("Volumetric.applyFroxel: uploadFroxel not called");
854 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
855 const float dh = gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
856 gfx->drawTexturedRectShaderDepth(linearDepth, froxelAtlas_, froxelShader_, 0.f, 0.f, dw, dh,
857 Color(1.f, 1.f, 1.f, 1.f));
858}
859
860void Volumetric::applyFroxelTo(Graphics *gfx, Texture *linearDepth, Canvas *dest) {
861 if (!gfx || !dest) throw eve::Exception("Volumetric.applyFroxelTo: null argument");
862 Canvas *prev = gfx->getCanvas();
863 gfx->setCanvas(dest);
864 applyFroxel(gfx, linearDepth);
865 gfx->setCanvas(prev);
866}
867
869 float (*depth01)(int x, int y, void *userdata),
870 void *userdata) {
871 if (!gfx) throw eve::Exception("Volumetric.newLinearDepthTexture: null graphics");
872 if (width < 1 || height < 1)
873 throw eve::Exception("Volumetric.newLinearDepthTexture: invalid size");
874 if (!depth01) throw eve::Exception("Volumetric.newLinearDepthTexture: null depth fn");
875 std::vector<uint8_t> rgba(size_t(width) * size_t(height) * 4);
876 for (int y = 0; y < height; ++y) {
877 for (int x = 0; x < width; ++x) {
878 float d = depth01(x, y, userdata);
879 if (d < 0.f) d = 0.f;
880 if (d > 1.f) d = 1.f;
881 const uint8_t b = uint8_t(std::lround(d * 255.f));
882 const size_t i = size_t(y * width + x) * 4;
883 rgba[i + 0] = b;
884 rgba[i + 1] = b;
885 rgba[i + 2] = b;
886 rgba[i + 3] = 255;
887 }
888 }
889 return gfx->newTexture(width, height, rgba.data());
890}
891
892} // namespace eve::graphics
double value
SQInteger top
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
glm::vec4 p[6]
tensor::Graph g
Definition GpuGraph.cpp:7
vk::ShaderModule vert
vk::ShaderModule frag
float u
Definition Grass.cpp:233
double r
float v
int h
std::uint32_t height
std::uint32_t width
size_t offset
std::string name
MeleePoint3 b
Definition MeleeHit.cpp:41
std::weak_ptr< PrimitiveScene > scene
float d
int steps
float fovRad
Shader * shader
glm::mat4 view
glm::mat4 proj
float dz
float dy
float dx
Vec extinction
float targetX
const UnitySourceAsset & source
std::uint32_t depth
double oy
double ox
float bottom
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
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
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
Canvas public API.
Definition Canvas.h:17
virtual int getWidth() const =0
Returns the width.
virtual void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth)=0
Clears .
virtual int getHeight() const =0
Returns the height.
Drawable base (textures, meshes, canvases).
Definition Drawable.h:17
Analytic local participating-media volume injected into atmospheric fog.
Definition FogVolume.h:12
virtual bool updateTexture(Texture *texture, int width, int height, const uint8_t *rgba)=0
Replace an existing texture's pixels in place (pointer stays stable).
int getWidth() const
Returns the width.
Definition Graphics.h:502
virtual void drawOcclusionTexture(Texture *texture, float x, float y, float w, float h)
Draws occlusion texture.
virtual Canvas * getCanvas() const =0
Returns the canvas.
virtual void drawSolidRect(float x, float y, float w, float h, float r, float g, float b, float a=1.f)
RGBA-float overload matching the script-facing drawSolidRect name.
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
Creates a texture. @ownership Caller deletes unless documented otherwise.
virtual void setCanvas(Canvas *canvas)=0
nullptr or this → screen. Switching flushes pending draws to the previous target.
virtual void drawOcclusionSolid(float x, float y, float w, float h)
Draws occlusion solid.
virtual void drawOcclusion(Drawable *drawable, const glm::mat4 &m)
Volumetric occlusion helpers (shadow-pass analogue for light shafts). drawOcclusion skips drawables w...
int getHeight() const
Returns the height.
Definition Graphics.h:504
virtual void drawTexturedRectShaderDepth(Texture *color, Texture *depth, Shader *shader, float x, float y, float w, float h, const Color &tint)=0
Fullscreen/post draw sampling color at binding 0 and depth at binding 1 (hardware D32,...
bool hasUniform(const std::string &name) const
Definition Shader.cpp:59
void sendFloat(const std::string &name, float x)
Definition Shader.cpp:95
void sendMatrix(const std::string &name, const glm::mat4 &m)
Definition Shader.cpp:112
int getFromVar(const std::string &name, void *data, size_t size) const
Definition Shader.cpp:84
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
Definition Texture.h:18
int getWidth() const
Returns the width.
Definition Texture.cpp:28
int getHeight() const
Returns the height.
Definition Texture.cpp:33
void scatter(Graphics *gfx, Texture *occlusion)
Scatter-only: occlusion → shafts with alpha (draw over a prior scene). Writes to the currently bound ...
void scatterTo(Graphics *gfx, Texture *occlusion, Canvas *dest)
Same as scatter but into an explicit destination.
float getFloat(const std::string &name) const
Returns the float.
void setFogNoise(float amount)
Sets the fog noise.
void drawOccluder(Graphics *gfx, Drawable *drawable, const glm::mat4 &matrix)
Drawable occlusion (respects Drawable::getCastOcclusion).
void setMode(const std::string &mode)
"screenspace" | "raymarch" | "fog" | "froxel" | "cloud".
void setCloudLightColor(float r, float g, float b)
Direct sunlight color used by cloud single scattering.
int getSampleCount() const
Returns the sample count.
void setLightScreenPos(float x, float y, float width, float height)
Pixel-space helper (converts with width/height).
void setFogEnd(float endDistance)
Sets the fog end.
void setCloudScale(float worldScale)
Horizontal size of procedural cloud features in world units.
void beginOcclusionMap(Graphics *gfx, float lightPixelX, float lightPixelY, float lightRadiusPixels=24.f)
Clear the current canvas to black and draw a bright light disc (start of an occlusion map)....
Volumetric(Graphics *gfx)
Volumetric.
void setCloudLayer(float bottom, float top)
Set cloud layer world-space bottom and top heights.
void applyFromScene(Graphics *gfx, Texture *scene)
Single-pass: treat source as the scene (bright regions ≈ light), add shafts + dust/fog onto it....
void drawOccluders2D(Graphics *gfx)
Draw all visible Renderable2D with castOcclusion into the current canvas as black silhouettes (shadow...
void injectFroxelHeightFog(float extinction, float albedoR, float albedoG, float albedoB, float baseHeight, float heightFalloff, float minWorldY, float maxWorldY)
Inject an exponential global height layer into the froxel grid.
void rayMarchTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
Ray march to.
void uploadFroxel(Graphics *gfx)
Upload integrated froxels into a slice atlas sampled by applyFroxel.
void setFogHeightFalloff(float falloff)
Sets the fog height falloff.
void setCloudCoverage(float coverage)
Cloud cover fraction in [0,1].
void setLightScreenUV(float u, float v)
Light position in UV (0..1), origin top-left to match 2D UVs.
void rayMarch(Graphics *gfx, Texture *linearDepth)
Ray march participating media using a linear-depth texture (R channel, 0=near .. 1=far)....
void setCamera(float eyeX, float eyeY, float eyeZ, float targetX, float targetY, float targetZ, float upX, float upY, float upZ, float fovYDeg, float aspect, float nearZ, float farZ)
Camera for ray march reconstruction (RH + ZO). Builds inv(viewProj) and near/far used by rayMarch*.
void setInvViewProj(const glm::mat4 &invViewProj)
Raw inverse view-projection (column-major, matching glm).
void setIntensity(float intensity)
Sets the intensity.
bool hasParam(const std::string &name) const
True when param.
void injectFroxelLocalVolume(FogVolume *volume)
Inject an analytic local fog volume using the camera from setCamera().
float getLightScreenU() const
Returns the light screen u.
void renderCloudsTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
Renders clouds to.
void setLightDirection(float dx, float dy, float dz)
World-space direction toward the lit surface (ray march / phase).
void applyFroxel(Graphics *gfx, Texture *linearDepth)
Composite the uploaded froxel volume over the current target using scene depth.
void setTime(float seconds)
Sets the time.
void renderClouds(Graphics *gfx, Texture *linearDepth)
Render the bounded cloud layer using scene linear depth for occlusion.
void setCloudWind(float x, float z)
Cloud wind velocity in world X/Z units per second.
void setFloat(const std::string &name, float value)
Sets the float.
void setShaftColor(float r, float g, float b)
Sets the shaft color.
void setQuality(const std::string &quality)
"low" | "medium" | "high" (unknown → medium).
Texture * newLinearDepthTexture(Graphics *gfx, int width, int height, float(*depth01)(int x, int y, void *userdata), void *userdata)
Build an RGBA8 texture with linear depth in R (G=B=R, A=255). depth01(x,y) should return values in [0...
void setFogHeight(float worldY)
Height fog: denser near world Y = fogHeight; falloff is 1/meters scale.
void integrateFroxel(float lightR, float lightG, float lightB, float phaseScale=1.f)
Integrate the current froxel media using a uniform incident light.
void applyFromSceneTo(Graphics *gfx, Texture *scene, Canvas *dest)
Applies from scene to.
float getLightScreenV() const
Returns the light screen v.
void configureFroxelGrid(int width, int height, int depth, float nearDistance, float farDistance)
Configure the CPU reference/fallback froxel grid used by the atlas renderer. Quality presets choose s...
void applyFogTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
Applies fog to.
void applyFroxelTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
Composite the uploaded froxel volume into an explicit destination.
void setDensity(float density)
Sets the density.
void drawOccluderTexture(Graphics *gfx, Texture *texture, float x, float y, float w, float h)
Draws occluder texture.
void setFogStart(float startDistance)
View-distance ramp where fog appears (world units along the ray).
void applyFog(Graphics *gfx, Texture *linearDepth)
Volumetric height/distance fog from a linear-depth texture. Writes fog RGB with alpha = 1-transmittan...
void setCloudDensity(float density)
Cloud density multiplier, clamped non-negative.
void clearFroxelGrid()
Clear all media in the configured froxel grid.
void drawOccluderSolid(Graphics *gfx, float x, float y, float w, float h)
Convenience 2D black silhouette (solid or textured alpha).
Result< void > projectDirectionalCookie(Graphics *gfx, Texture *depth, Texture *cookie, float worldSize, float intensity=1.f)
Project a repeating directional-light cookie over visible scene surfaces.
int resolutionFor(int fullSize) const
Downscale helper: returns floor(dim / downscale), at least 1. Callers create occlusion / scatter canv...
void setFogColor(float r, float g, float b)
Sets the fog color.
constexpr const char * kVolScreen
constexpr const char * kVolCommon
constexpr const char * kVolRay
constexpr const char * kVolCloud
constexpr char kVolDirectionalCookie[]
constexpr const char * kVolFroxel
constexpr const char * kVolFog
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
Definition Animation.h:25
glm::mat4 perspectiveVulkanRH_ZO(float fovyRad, float aspect, float zNear, float zFar)
Right-handed, zero-to-one depth perspective for Vulkan swapchains.
Definition ClipSpace.h:22
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...
Definition Color.h:13
glm::vec4 ambient
glm::mat4 invViewProj