11#include "graphics/shaders/volumetric_post_frag_spv.inc"
12#include "graphics/shaders/volumetric_raymarch_frag_spv.inc"
13#include "graphics/shaders/volumetric_fog_frag_spv.inc"
21#include <glm/gtc/matrix_transform.hpp>
22#include <glm/gtc/type_ptr.hpp>
27Shader *createScreenspaceShader(Graphics *gfx) {
29 std::vector<uint32_t>
frag(volumetric_post_frag_spv,
30 volumetric_post_frag_spv + volumetric_post_frag_spv_count);
31 std::vector<uint32_t>
vert;
32 Shader *sh = gfx->newShaderFromSpv(
vert,
frag);
33 sh->declareFloat(
"lightX");
34 sh->declareFloat(
"lightY");
35 sh->declareFloat(
"exposure");
36 sh->declareFloat(
"decay");
37 sh->declareFloat(
"density");
38 sh->declareFloat(
"weight");
39 sh->declareFloat(
"sampleCount");
40 sh->declareFloat(
"dustAmount");
41 sh->declareFloat(
"fogAmount");
42 sh->declareFloat(
"fogR");
43 sh->declareFloat(
"fogG");
44 sh->declareFloat(
"fogB");
45 sh->declareFloat(
"shaftR");
46 sh->declareFloat(
"shaftG");
47 sh->declareFloat(
"shaftB");
48 sh->declareFloat(
"time");
49 sh->declareFloat(
"compositeMode");
50 sh->declareFloat(
"intensity");
52 sh->sendFloat(
"lightX", 0.5f);
53 sh->sendFloat(
"lightY", 0.35f);
54 sh->sendFloat(
"exposure", 0.35f);
55 sh->sendFloat(
"decay", 0.96f);
56 sh->sendFloat(
"density", 0.85f);
57 sh->sendFloat(
"weight", 0.55f);
58 sh->sendFloat(
"sampleCount", 48.f);
59 sh->sendFloat(
"dustAmount", 0.25f);
60 sh->sendFloat(
"fogAmount", 0.18f);
61 sh->sendFloat(
"fogR", 0.55f);
62 sh->sendFloat(
"fogG", 0.62f);
63 sh->sendFloat(
"fogB", 0.75f);
64 sh->sendFloat(
"shaftR", 1.f);
65 sh->sendFloat(
"shaftG", 0.95f);
66 sh->sendFloat(
"shaftB", 0.85f);
67 sh->sendFloat(
"time", 0.f);
68 sh->sendFloat(
"compositeMode", 0.f);
69 sh->sendFloat(
"intensity", 1.f);
73Shader *createRayMarchShader(Graphics *gfx) {
75 std::vector<uint32_t>
frag(volumetric_raymarch_frag_spv,
76 volumetric_raymarch_frag_spv + volumetric_raymarch_frag_spv_count);
77 std::vector<uint32_t>
vert;
78 Shader *sh = gfx->newShaderFromSpv(
vert,
frag);
79 sh->declareMatrix(
"invViewProj");
80 sh->declareFloat(
"lightDx");
81 sh->declareFloat(
"lightDy");
82 sh->declareFloat(
"lightDz");
83 sh->declareFloat(
"density");
84 sh->declareFloat(
"shaftR");
85 sh->declareFloat(
"shaftG");
86 sh->declareFloat(
"shaftB");
87 sh->declareFloat(
"intensity");
88 sh->declareFloat(
"nearZ");
89 sh->declareFloat(
"farZ");
90 sh->declareFloat(
"sampleCount");
91 sh->declareFloat(
"dustAmount");
92 sh->declareFloat(
"fogAmount");
93 sh->declareFloat(
"shadowSteps");
94 sh->declareFloat(
"lightU");
95 sh->declareFloat(
"lightV");
97 sh->sendMatrix(
"invViewProj", glm::mat4(1.f));
98 sh->sendFloat(
"lightDx", 0.4f);
99 sh->sendFloat(
"lightDy", 1.f);
100 sh->sendFloat(
"lightDz", 0.3f);
101 sh->sendFloat(
"density", 0.35f);
102 sh->sendFloat(
"shaftR", 1.f);
103 sh->sendFloat(
"shaftG", 0.95f);
104 sh->sendFloat(
"shaftB", 0.85f);
105 sh->sendFloat(
"intensity", 1.f);
106 sh->sendFloat(
"nearZ", 0.1f);
107 sh->sendFloat(
"farZ", 100.f);
108 sh->sendFloat(
"sampleCount", 24.f);
109 sh->sendFloat(
"dustAmount", 0.25f);
110 sh->sendFloat(
"fogAmount", 0.2f);
111 sh->sendFloat(
"shadowSteps", 8.f);
112 sh->sendFloat(
"lightU", 0.7f);
113 sh->sendFloat(
"lightV", 0.2f);
117Shader *createFogShader(Graphics *gfx) {
119 std::vector<uint32_t>
frag(volumetric_fog_frag_spv,
120 volumetric_fog_frag_spv + volumetric_fog_frag_spv_count);
121 std::vector<uint32_t>
vert;
122 Shader *sh = gfx->newShaderFromSpv(
vert,
frag);
123 sh->declareMatrix(
"invViewProj");
124 sh->declareFloat(
"lightDx");
125 sh->declareFloat(
"lightDy");
126 sh->declareFloat(
"lightDz");
127 sh->declareFloat(
"density");
128 sh->declareFloat(
"fogR");
129 sh->declareFloat(
"fogG");
130 sh->declareFloat(
"fogB");
131 sh->declareFloat(
"intensity");
132 sh->declareFloat(
"nearZ");
133 sh->declareFloat(
"farZ");
134 sh->declareFloat(
"sampleCount");
135 sh->declareFloat(
"fogHeight");
136 sh->declareFloat(
"heightFalloff");
137 sh->declareFloat(
"fogStart");
138 sh->declareFloat(
"fogEnd");
139 sh->declareFloat(
"noiseAmount");
141 sh->sendMatrix(
"invViewProj", glm::mat4(1.f));
142 sh->sendFloat(
"lightDx", 0.4f);
143 sh->sendFloat(
"lightDy", 1.f);
144 sh->sendFloat(
"lightDz", 0.3f);
145 sh->sendFloat(
"density", 0.25f);
146 sh->sendFloat(
"fogR", 0.55f);
147 sh->sendFloat(
"fogG", 0.62f);
148 sh->sendFloat(
"fogB", 0.75f);
149 sh->sendFloat(
"intensity", 1.f);
150 sh->sendFloat(
"nearZ", 0.1f);
151 sh->sendFloat(
"farZ", 100.f);
152 sh->sendFloat(
"sampleCount", 24.f);
153 sh->sendFloat(
"fogHeight", 0.f);
154 sh->sendFloat(
"heightFalloff", 0.15f);
155 sh->sendFloat(
"fogStart", 2.f);
156 sh->sendFloat(
"fogEnd", 40.f);
157 sh->sendFloat(
"noiseAmount", 0.35f);
164 shader_ = createScreenspaceShader(gfx);
165 rayShader_ = createRayMarchShader(gfx);
166 fogShader_ = createFogShader(gfx);
167 applyQualityDefaults();
172void Volumetric::applyQualityDefaults() {
173 if (quality_ ==
"low") {
175 if (mode_ ==
"raymarch") {
176 rayShader_->
sendFloat(
"sampleCount", 8.f);
177 rayShader_->
sendFloat(
"shadowSteps", 4.f);
178 rayShader_->
sendFloat(
"dustAmount", 0.12f);
179 rayShader_->
sendFloat(
"fogAmount", 0.12f);
180 }
else if (mode_ ==
"fog") {
181 fogShader_->
sendFloat(
"sampleCount", 8.f);
182 fogShader_->
sendFloat(
"noiseAmount", 0.15f);
190 }
else if (quality_ ==
"high") {
192 if (mode_ ==
"raymarch") {
193 rayShader_->
sendFloat(
"sampleCount", 48.f);
194 rayShader_->
sendFloat(
"shadowSteps", 16.f);
195 rayShader_->
sendFloat(
"dustAmount", 0.35f);
196 rayShader_->
sendFloat(
"fogAmount", 0.25f);
197 }
else if (mode_ ==
"fog") {
198 fogShader_->
sendFloat(
"sampleCount", 48.f);
199 fogShader_->
sendFloat(
"noiseAmount", 0.45f);
210 if (mode_ ==
"raymarch") {
211 rayShader_->
sendFloat(
"sampleCount", 24.f);
212 rayShader_->
sendFloat(
"shadowSteps", 8.f);
213 rayShader_->
sendFloat(
"dustAmount", 0.25f);
214 rayShader_->
sendFloat(
"fogAmount", 0.2f);
215 }
else if (mode_ ==
"fog") {
216 fogShader_->
sendFloat(
"sampleCount", 24.f);
217 fogShader_->
sendFloat(
"noiseAmount", 0.35f);
230 applyQualityDefaults();
234 if (mode ==
"raymarch")
236 else if (mode ==
"fog")
239 mode_ =
"screenspace";
240 applyQualityDefaults();
260 glm::vec3
d(dx, dy, dz);
261 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
262 else d = glm::normalize(
d);
273 invViewProj_ = invViewProj;
274 rayShader_->
sendMatrix(
"invViewProj", invViewProj_);
275 fogShader_->
sendMatrix(
"invViewProj", invViewProj_);
279 float targetZ,
float upX,
float upY,
float upZ,
float fovYDeg,
280 float aspect,
float nearZ,
float farZ) {
281 nearZ_ = nearZ > 1e-4f ? nearZ : 0.1f;
282 farZ_ = farZ > nearZ_ ? farZ : nearZ_ + 1.f;
283 const float aspectSafe = aspect > 1e-4f ? aspect : 1.f;
284 const float fovRad = fovYDeg * 0.017453292519943295f;
285 const glm::mat4
view =
286 glm::lookAtRH(glm::vec3(eyeX, eyeY, eyeZ), glm::vec3(targetX, targetY, targetZ),
287 glm::vec3(upX, upY, upZ));
316 rayShader_->
sendFloat(
"intensity", intensity);
317 fogShader_->
sendFloat(
"intensity", intensity);
324 rayShader_->
sendFloat(
"density", density);
325 fogShader_->
sendFloat(
"density", density);
330 fogShader_->
sendFloat(
"fogHeight", fogHeight_);
334 fogHeightFalloff_ = falloff < 0.f ? 0.f : falloff;
335 fogShader_->
sendFloat(
"heightFalloff", fogHeightFalloff_);
339 fogStart_ = startDistance < 0.f ? 0.f : startDistance;
340 fogShader_->
sendFloat(
"fogStart", fogStart_);
344 fogEnd_ = endDistance <= fogStart_ ? fogStart_ + 1.f : endDistance;
345 fogShader_->
sendFloat(
"fogEnd", fogEnd_);
349 fogNoise_ = amount < 0.f ? 0.f : amount;
350 fogShader_->
sendFloat(
"noiseAmount", fogNoise_);
359 if (!shader_)
throw eve::Exception(
"Volumetric.setFloat: null shader");
367 if (mode_ ==
"raymarch" && rayShader_ && rayShader_->
hasUniform(
name)) {
371 if (mode_ ==
"fog" && fogShader_ && fogShader_->
hasUniform(
name)) {
393 if (fullSize < 1)
return 1;
394 const float ds = std::max(downscale_, 1.f);
395 return std::max(1,
int(std::floor(
float(fullSize) / ds)));
398void Volumetric::uploadCommon(
bool compositeFromScene) {
399 setFloat(
"compositeMode", compositeFromScene ? 1.f : 0.f);
402void Volumetric::uploadRayMarchCommon() {
403 rayShader_->
sendMatrix(
"invViewProj", invViewProj_);
404 rayShader_->
sendFloat(
"lightDx", lightDir_.x);
405 rayShader_->
sendFloat(
"lightDy", lightDir_.y);
406 rayShader_->
sendFloat(
"lightDz", lightDir_.z);
411void Volumetric::uploadFogCommon() {
412 fogShader_->
sendMatrix(
"invViewProj", invViewProj_);
413 fogShader_->
sendFloat(
"lightDx", lightDir_.x);
414 fogShader_->
sendFloat(
"lightDy", lightDir_.y);
415 fogShader_->
sendFloat(
"lightDz", lightDir_.z);
418 fogShader_->
sendFloat(
"fogHeight", fogHeight_);
419 fogShader_->
sendFloat(
"heightFalloff", fogHeightFalloff_);
420 fogShader_->
sendFloat(
"fogStart", fogStart_);
421 fogShader_->
sendFloat(
"fogEnd", fogEnd_);
422 fogShader_->
sendFloat(
"noiseAmount", fogNoise_);
425void Volumetric::drawFullscreen(Graphics *gfx, Texture *source, Shader *
shader) {
427 if (!source)
throw eve::Exception(
"Volumetric: null source texture");
430 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
432 gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
433 gfx->drawTexturedRectShader(source,
shader, 0.f, 0.f, dw, dh,
Color(1.f, 1.f, 1.f, 1.f));
437 float lightRadiusPixels) {
438 if (!gfx)
throw eve::Exception(
"Volumetric.beginOcclusionMap: null graphics");
439 gfx->
clear(
Color(0.f, 0.f, 0.f, 1.f), std::nullopt, std::nullopt);
440 const float r = std::max(lightRadiusPixels, 1.f);
441 gfx->
drawSolidRect(lightPixelX - r, lightPixelY - r, r * 2.f, r * 2.f,
442 Color(1.f, 1.f, 1.f, 1.f));
449 if (!gfx || !drawable)
return;
466 if (ecs::current()->getManager<Renderable2D>() ==
nullptr)
return;
467 auto view = ecs::View<Renderable2D, Renderable2D::Transform2D, Renderable2D::Sprite>();
468 for (
auto it =
view.begin(); it !=
view.end(); ++it) {
470 if (!sp->visible || !sp->castOcclusion)
continue;
471 const float w = sp->width * (xf->sx == 0.f ? 1.f : xf->sx);
472 const float h = sp->height * (xf->sy == 0.f ? 1.f : xf->sy);
482 drawFullscreen(gfx, occlusion, shader_);
486 if (!gfx)
throw eve::Exception(
"Volumetric.scatterTo: null graphics");
487 if (!dest)
throw eve::Exception(
"Volumetric.scatterTo: null dest");
491 gfx->
setCanvas(prev == gfx ?
nullptr : prev);
496 drawFullscreen(gfx, scene, shader_);
500 if (!gfx)
throw eve::Exception(
"Volumetric.applyFromSceneTo: null graphics");
501 if (!dest)
throw eve::Exception(
"Volumetric.applyFromSceneTo: null dest");
505 gfx->
setCanvas(prev == gfx ?
nullptr : prev);
509 uploadRayMarchCommon();
510 drawFullscreen(gfx, linearDepth, rayShader_);
514 if (!gfx)
throw eve::Exception(
"Volumetric.rayMarchTo: null graphics");
515 if (!dest)
throw eve::Exception(
"Volumetric.rayMarchTo: null dest");
519 gfx->
setCanvas(prev == gfx ?
nullptr : prev);
524 drawFullscreen(gfx, linearDepth, fogShader_);
528 if (!gfx)
throw eve::Exception(
"Volumetric.applyFogTo: null graphics");
529 if (!dest)
throw eve::Exception(
"Volumetric.applyFogTo: null dest");
533 gfx->
setCanvas(prev == gfx ?
nullptr : prev);
537 float (*depth01)(
int x,
int y,
void *userdata),
539 if (!gfx)
throw eve::Exception(
"Volumetric.newLinearDepthTexture: null graphics");
541 throw eve::Exception(
"Volumetric.newLinearDepthTexture: invalid size");
542 if (!depth01)
throw eve::Exception(
"Volumetric.newLinearDepthTexture: null depth fn");
543 std::vector<uint8_t> rgba(
size_t(
width) *
size_t(
height) * 4);
546 float d = depth01(
x,
y, userdata);
547 if (
d < 0.f)
d = 0.f;
548 if (
d > 1.f)
d = 1.f;
549 const uint8_t
b = uint8_t(std::lround(
d * 255.f));
550 const size_t i = size_t(
y *
width +
x) * 4;
virtual int getWidth() const =0
virtual void clear(std::optional< Color > color, std::optional< int > stencil, std::optional< double > depth)=0
virtual int getHeight() const =0
Drawable base (textures, meshes, canvases).
virtual void drawOcclusionTexture(Texture *texture, float x, float y, float w, float h)
virtual Canvas * getCanvas() const =0
virtual Texture * newTexture(int width, int height, const uint8_t *rgba, bool repeatU=false, bool repeatV=false)=0
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)
virtual void drawOcclusion(Drawable *drawable, const glm::mat4 &m)
Volumetric occlusion helpers (shadow-pass analogue for light shafts). drawOcclusion skips drawables w...
virtual void drawSolidRect(float x, float y, float w, float h, const Color &color, BlendMode blend=BlendMode::Alpha)=0
Internal immediate-mode helper used by RenderSystem / Batcher.
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.
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
void setFogNoise(float amount)
void drawOccluder(Graphics *gfx, Drawable *drawable, const glm::mat4 &matrix)
Drawable occlusion (respects Drawable::getCastOcclusion).
void setMode(const std::string &mode)
"screenspace" | "raymarch" | "fog" — selects which shader params quality tweaks.
int getSampleCount() const
void setLightScreenPos(float x, float y, float width, float height)
Pixel-space helper (converts with width/height).
void setFogEnd(float endDistance)
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)
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 rayMarchTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
void setFogHeightFalloff(float falloff)
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)
bool hasParam(const std::string &name) const
float getLightScreenU() const
void setLightDirection(float dx, float dy, float dz)
World-space direction toward the lit surface (ray march / phase).
void setTime(float seconds)
void setFloat(const std::string &name, float value)
void setShaftColor(float r, float g, float b)
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 applyFromSceneTo(Graphics *gfx, Texture *scene, Canvas *dest)
float getLightScreenV() const
void applyFogTo(Graphics *gfx, Texture *linearDepth, Canvas *dest)
void setDensity(float density)
void drawOccluderTexture(Graphics *gfx, Texture *texture, float x, float y, float w, float h)
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 drawOccluderSolid(Graphics *gfx, float x, float y, float w, float h)
Convenience 2D black silhouette (solid or textured alpha).
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)
卡牌游戏 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.
glm::vec4 Color
RGBA color used by every graphics draw call. Lives inside eve::graphics so including a graphics heade...