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"
29#include <glm/gtc/matrix_transform.hpp>
30#include <glm/gtc/type_ptr.hpp>
35Shader *newVolShader(Graphics *gfx,
const std::vector<uint32_t> &
frag,
const char *wgsl) {
36 if (gfx->getBackendName() ==
"webgpu")
38 return gfx->newShaderFromSpv({},
frag);
41Shader *createScreenspaceShader(Graphics *gfx) {
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;
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");
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);
89 sh->sendFloat(
"spotDx", 0.f);
90 sh->sendFloat(
"spotDy", -1.f);
91 sh->sendFloat(
"spotCosOuter", -2.f);
92 sh->sendFloat(
"spotCosInner", -2.f);
96Shader *createRayMarchShader(Graphics *gfx) {
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;
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");
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);
135 sh->sendFloat(
"lightU", 0.7f);
136 sh->sendFloat(
"lightV", 0.2f);
140Shader *createFogShader(Graphics *gfx) {
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;
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");
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);
184Shader *createFroxelShader(Graphics *gfx) {
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;
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);
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);
208 sh->declareMatrix(
"invViewProj");
209 sh->declareFloat(
"worldSize");
210 sh->declareFloat(
"intensity");
211 sh->declareFloat(
"lightDx");
212 sh->declareFloat(
"lightDy");
213 sh->declareFloat(
"lightDz");
217Shader *createCloudShader(Graphics *gfx) {
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;
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);
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();
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);
281 if (quality_ ==
"low") {
283 if (mode_ ==
"raymarch") {
284 rayShader_->
sendFloat(
"sampleCount", 8.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);
292 }
else if (mode_ ==
"cloud") {
293 cloudShader_->
sendFloat(
"sampleCount", 16.f);
294 cloudShader_->
sendFloat(
"shadowSteps", 3.f);
301 }
else if (quality_ ==
"high") {
303 if (mode_ ==
"raymarch") {
304 rayShader_->
sendFloat(
"sampleCount", 48.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);
312 }
else if (mode_ ==
"cloud") {
313 cloudShader_->
sendFloat(
"sampleCount", 64.f);
314 cloudShader_->
sendFloat(
"shadowSteps", 10.f);
324 if (mode_ ==
"raymarch") {
325 rayShader_->
sendFloat(
"sampleCount", 24.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);
333 }
else if (mode_ ==
"cloud") {
334 cloudShader_->
sendFloat(
"sampleCount", 32.f);
335 cloudShader_->
sendFloat(
"shadowSteps", 6.f);
347 applyQualityDefaults();
351 if (mode ==
"raymarch")
353 else if (mode ==
"fog")
355 else if (mode ==
"froxel")
357 else if (mode ==
"cloud")
360 mode_ =
"screenspace";
361 applyQualityDefaults();
382 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
383 else d = glm::normalize(
d);
398 rayShader_->
sendMatrix(
"invViewProj", invViewProj_);
399 fogShader_->
sendMatrix(
"invViewProj", invViewProj_);
400 cloudShader_->
sendMatrix(
"invViewProj", invViewProj_);
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));
441 rayShader_->
sendFloat(
"intensity", intensity);
442 fogShader_->
sendFloat(
"intensity", intensity);
449 rayShader_->
sendFloat(
"density", density);
450 fogShader_->
sendFloat(
"density", density);
455 fogShader_->
sendFloat(
"fogHeight", fogHeight_);
459 fogHeightFalloff_ = falloff < 0.f ? 0.f : falloff;
460 fogShader_->
sendFloat(
"heightFalloff", fogHeightFalloff_);
464 fogStart_ = startDistance < 0.f ? 0.f : startDistance;
465 fogShader_->
sendFloat(
"fogStart", fogStart_);
469 fogEnd_ = endDistance <= fogStart_ ? fogStart_ + 1.f : endDistance;
470 fogShader_->
sendFloat(
"fogEnd", fogEnd_);
474 fogNoise_ = amount < 0.f ? 0.f : amount;
475 fogShader_->
sendFloat(
"noiseAmount", fogNoise_);
481 cloudShader_->
sendFloat(
"cloudBottom", cloudBottom_);
482 cloudShader_->
sendFloat(
"cloudTop", cloudTop_);
486 cloudCoverage_ = std::clamp(coverage, 0.f, 1.f);
487 cloudShader_->
sendFloat(
"cloudCoverage", cloudCoverage_);
491 cloudDensity_ = std::max(density, 0.f);
492 cloudShader_->
sendFloat(
"cloudDensity", cloudDensity_);
496 cloudScale_ = std::max(worldScale, 0.01f);
497 cloudShader_->
sendFloat(
"cloudScale", cloudScale_);
501 cloudWind_ = glm::vec3(
x, 0.f,
z);
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);
520 if (!shader_)
throw eve::Exception(
"Volumetric.setFloat: null shader");
529 if (mode_ ==
"raymarch" && rayShader_ && rayShader_->
hasUniform(
name)) {
533 if (mode_ ==
"fog" && fogShader_ && fogShader_->
hasUniform(
name)) {
537 if (mode_ ==
"cloud" && cloudShader_ && cloudShader_->
hasUniform(
name)) {
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)));
568void Volumetric::uploadCommon(
bool compositeFromScene) {
569 setFloat(
"compositeMode", compositeFromScene ? 1.f : 0.f);
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);
579 uploadRayMarchShadowAnisotropy();
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);
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_);
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);
613void Volumetric::drawFullscreen(Graphics *gfx, Texture *
source, Shader *
shader) {
618 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
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));
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);
630 Color(1.f, 1.f, 1.f, 1.f));
637 if (!gfx || !drawable)
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) {
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);
670 drawFullscreen(gfx, occlusion, shader_);
674 if (!gfx)
throw eve::Exception(
"Volumetric.scatterTo: null graphics");
675 if (!dest)
throw eve::Exception(
"Volumetric.scatterTo: null dest");
684 drawFullscreen(gfx,
scene, shader_);
688 if (!gfx)
throw eve::Exception(
"Volumetric.applyFromSceneTo: null graphics");
689 if (!dest)
throw eve::Exception(
"Volumetric.applyFromSceneTo: null dest");
697 uploadRayMarchCommon();
698 drawFullscreen(gfx, linearDepth, rayShader_);
702 if (!gfx)
throw eve::Exception(
"Volumetric.rayMarchTo: null graphics");
703 if (!dest)
throw eve::Exception(
"Volumetric.rayMarchTo: null dest");
712 drawFullscreen(gfx, linearDepth, fogShader_);
716 if (!gfx)
throw eve::Exception(
"Volumetric.applyFogTo: null graphics");
717 if (!dest)
throw eve::Exception(
"Volumetric.applyFogTo: null dest");
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);
746 drawFullscreen(gfx, linearDepth, cloudShader_);
750 if (!gfx)
throw eve::Exception(
"Volumetric.renderCloudsTo: null graphics");
751 if (!dest)
throw eve::Exception(
"Volumetric.renderCloudsTo: null dest");
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) /
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,
779 atmosphereVolume_->injectLocalVolumeFrustum(*
volume, invViewProj_);
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));
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);
801 if (!(worldMin.x < worldMax.x && worldMin.y < worldMax.y && worldMin.z < worldMax.z)) {
802 worldMin = glm::vec3(-farZ_);
803 worldMax = glm::vec3(farZ_);
805 atmosphereVolume_->integrateLocalLights(pendingEmissiveProxies_, worldMin, worldMax,
806 ambient * std::max(phaseScale, 0.f));
807 clearPendingEmissiveProxies();
810 atmosphereVolume_->integrate(glm::vec3(lightR, lightG, lightB), phaseScale);
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, 0
u);
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));
839 if (!froxelAtlas_ || froxelAtlas_->
getWidth() != atlasW || froxelAtlas_->
getHeight() != atlasH)
840 froxelAtlas_ = gfx->
newTexture(atlasW, atlasH, rgba.data());
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());
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");
857 Color(1.f, 1.f, 1.f, 1.f));
861 if (!gfx || !dest)
throw eve::Exception(
"Volumetric.applyFroxelTo: null argument");
869 float (*depth01)(
int x,
int y,
void *userdata),
871 if (!gfx)
throw eve::Exception(
"Volumetric.newLinearDepthTexture: null graphics");
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);
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;
std::weak_ptr< PrimitiveScene > scene
const UnitySourceAsset & source
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.
EVENGINE_API_FOUNDATION public API.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
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).
Analytic local participating-media volume injected into atmospheric fog.
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.
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.
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
void sendFloat(const std::string &name, float x)
void sendMatrix(const std::string &name, const glm::mat4 &m)
int getFromVar(const std::string &name, void *data, size_t size) const
GPU texture created via Graphics::newTexture. Owns GPU resources through an opaque backend handle.
int getWidth() const
Returns the width.
int getHeight() const
Returns the height.
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:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...
glm::mat4 perspectiveVulkanRH_ZO(float fovyRad, float aspect, float zNear, float zFar)
Right-handed, zero-to-one depth perspective for Vulkan swapchains.
eve::Color Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...