9#include "graphics/shaders/gi_ssgi_frag_spv.inc"
10#include "graphics/shaders/ssr_temporal_frag_spv.inc"
19#include <glm/gtc/matrix_transform.hpp>
24Shader *createSsgiShader(Graphics *gfx) {
25 if (!gfx)
throw eve::Exception(
"GlobalIllumination: null graphics");
26 std::vector<uint32_t>
frag(gi_ssgi_frag_spv, gi_ssgi_frag_spv + gi_ssgi_frag_spv_count);
27 Shader *sh = gfx->getBackendName() ==
"webgpu"
30 : gfx->newShaderFromSpv({},
frag);
31 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"GlobalIllumination: failed to create SSGI shader");
32 sh->declareMatrix(
"invViewProj");
33 sh->declareFloat(
"nearZ");
34 sh->declareFloat(
"farZ");
35 sh->declareFloat(
"radius");
36 sh->declareFloat(
"intensity");
37 sh->declareFloat(
"sampleCount");
38 sh->declareFloat(
"lightDirX");
39 sh->declareFloat(
"lightDirY");
40 sh->declareFloat(
"lightDirZ");
41 sh->declareFloat(
"lightR");
42 sh->declareFloat(
"lightG");
43 sh->declareFloat(
"lightB");
44 sh->declareFloat(
"texelW");
45 sh->declareFloat(
"texelH");
46 sh->declareFloat(
"useNdcDepth");
47 sh->declareFloat(
"normalValid");
48 sh->declareFloat(
"thickness");
49 sh->sendMatrix(
"invViewProj", glm::mat4(1.f));
50 sh->sendFloat(
"nearZ", 0.1f);
51 sh->sendFloat(
"farZ", 100.f);
52 sh->sendFloat(
"radius", 1.25f);
53 sh->sendFloat(
"intensity", 0.45f);
54 sh->sendFloat(
"sampleCount", 12.f);
55 sh->sendFloat(
"lightDirX", 0.4f);
56 sh->sendFloat(
"lightDirY", 1.f);
57 sh->sendFloat(
"lightDirZ", 0.3f);
58 sh->sendFloat(
"lightR", 1.f);
59 sh->sendFloat(
"lightG", 1.f);
60 sh->sendFloat(
"lightB", 1.f);
61 sh->sendFloat(
"texelW", 1.f / 256.f);
62 sh->sendFloat(
"texelH", 1.f / 256.f);
63 sh->sendFloat(
"useNdcDepth", 0.f);
64 sh->sendFloat(
"normalValid", 0.f);
65 sh->sendFloat(
"thickness", 0.2f);
69Shader *createTemporalShader(Graphics *gfx) {
70 std::vector<uint32_t>
frag(ssr_temporal_frag_spv,
71 ssr_temporal_frag_spv + ssr_temporal_frag_spv_count);
72 Shader *sh = gfx->getBackendName() ==
"webgpu"
75 : gfx->newShaderFromSpv({},
frag);
76 for (
int i = 0; i < 16; ++i) sh->declareFloat(
"reprojection" + std::to_string(i));
77 sh->declareFloat(
"temporalTexelW");
78 sh->declareFloat(
"temporalTexelH");
79 sh->declareFloat(
"temporalHistoryValid");
80 sh->declareFloat(
"temporalNearZ");
81 sh->declareFloat(
"temporalFarZ");
82 sh->declareFloat(
"temporalCurrentWeight");
83 sh->declareFloat(
"spatialFilterStrength");
84 sh->declareFloat(
"temporalEnergyEncoded");
85 sh->declareFloat(
"spatialStep");
92 ssgi_ = createSsgiShader(gfx);
93 temporal_ = createTemporalShader(gfx);
94 applyQualityDefaults();
99void GlobalIllumination::applyQualityDefaults() {
100 if (quality_ ==
"low") {
103 temporalCurrentWeight_ = 0.3f;
104 spatialFilterStrength_ = 0.8f;
105 resolutionScale_ = 0.5f;
107 }
else if (quality_ ==
"high") {
110 temporalCurrentWeight_ = 0.12f;
111 spatialFilterStrength_ = 0.4f;
112 resolutionScale_ = 1.f;
114 }
else if (quality_ ==
"ultra") {
117 temporalCurrentWeight_ = 0.08f;
118 spatialFilterStrength_ = 0.3f;
119 resolutionScale_ = 1.f;
124 temporalCurrentWeight_ = 0.16f;
125 spatialFilterStrength_ = 0.5f;
126 resolutionScale_ = 1.f;
134 if (quality ==
"low" || quality ==
"medium" || quality ==
"high" || quality ==
"ultra")
138 applyQualityDefaults();
139 historyValid_ =
false;
143 const glm::mat4 nextViewProj = glm::inverse(
invViewProj);
144 if (viewProjValid_) {
145 float maxDelta = 0.f;
146 for (
int c = 0;
c < 4; ++
c)
147 for (
int r = 0;
r < 4; ++
r)
148 maxDelta = std::max(maxDelta,
149 std::abs(nextViewProj[
c][
r] - previousViewProj_[
c][
r]));
150 if (maxDelta > 2.f) historyValid_ =
false;
153 ssgi_->
sendMatrix(
"invViewProj", invViewProj_);
157 float targetZ,
float upX,
float upY,
float upZ,
float fovYDeg,
158 float aspect,
float nearZ,
float farZ) {
159 nearZ_ = nearZ > 1e-4f ? nearZ : 0.1f;
160 farZ_ = farZ > nearZ_ ? farZ : nearZ_ + 1.f;
161 const float aspectSafe = aspect > 1e-4f ? aspect : 1.f;
162 const float fovRad = fovYDeg * 0.017453292519943295f;
163 const glm::vec3
eye(eyeX, eyeY, eyeZ);
164 glm::vec3 forward = glm::vec3(
targetX, targetY, targetZ) -
eye;
165 forward = glm::length(forward) > 1e-5f ? glm::normalize(forward)
166 : glm::vec3(0.f, 0.f, -1.f);
168 (glm::distance(
eye, previousEye_) > std::max(1.f, farZ_ * 0.1f) ||
169 glm::dot(forward, previousForward_) < 0.65f ||
170 std::abs(fovYDeg - previousFovY_) > 3.f ||
171 std::abs(aspectSafe - previousAspect_) > std::max(0.05f, previousAspect_ * 0.1f)))
172 historyValid_ =
false;
174 previousForward_ = forward;
175 previousFovY_ = fovYDeg;
176 previousAspect_ = aspectSafe;
178 const glm::mat4
view =
179 glm::lookAtRH(
eye, glm::vec3(
targetX, targetY, targetZ),
180 glm::vec3(upX, upY, upZ));
193 intensity_ = std::max(intensity, 0.f);
198 thickness_ = std::clamp(
thickness, 0.01f, 0.99f);
199 historyValid_ =
false;
203 const float next = std::clamp(
scale, 0.25f, 1.f);
204 if (std::abs(next - resolutionScale_) < 1e-4f)
return;
205 resolutionScale_ = next;
207 historyValid_ =
false;
212 if (glm::length(
d) < 1e-6f)
d = glm::vec3(0.f, 1.f, 0.f);
213 else d = glm::normalize(
d);
215 historyValid_ =
false;
216 ssgi_->
sendFloat(
"lightDirX", lightDir_.x);
217 ssgi_->
sendFloat(
"lightDirY", lightDir_.y);
218 ssgi_->
sendFloat(
"lightDirZ", lightDir_.z);
222 lightColor_ = glm::vec3(std::max(
r, 0.f), std::max(
g, 0.f), std::max(
b, 0.f));
223 historyValid_ =
false;
224 ssgi_->
sendFloat(
"lightR", lightColor_.x);
225 ssgi_->
sendFloat(
"lightG", lightColor_.y);
226 ssgi_->
sendFloat(
"lightB", lightColor_.z);
235 throw eve::Exception(
"GlobalIllumination.setFloat: missing param '%s'",
name.c_str());
236 if (
name ==
"thickness") {
238 ssgi_->
sendFloat(
"thickness",
float(samplingFrame_ & 7u) + thickness_);
242 if (
name ==
"radius" ||
name ==
"intensity" ||
name ==
"sampleCount")
243 historyValid_ =
false;
245 if (
name ==
"intensity") intensity_ =
value;
250 throw eve::Exception(
"GlobalIllumination.getFloat: missing param '%s'",
name.c_str());
251 if (
name ==
"thickness")
return thickness_;
254 throw eve::Exception(
"GlobalIllumination.getFloat: missing param '%s'",
name.c_str());
258 return int(std::lround(
getFloat(
"sampleCount")));
261Canvas *GlobalIllumination::getGiCanvas() {
265 const int targetW = std::max(1,
int(std::lround(
float(fullW) * resolutionScale_)));
266 const int targetH = std::max(1,
int(std::lround(
float(fullH) * resolutionScale_)));
267 if (!giCanvas_ || giCanvas_->
getWidth() != targetW || giCanvas_->
getHeight() != targetH)
273void GlobalIllumination::uploadUniforms(
int width,
int height) {
276 ssgi_->
sendMatrix(
"invViewProj", invViewProj_);
280 ssgi_->
sendFloat(
"intensity", intensity_);
281 ssgi_->
sendFloat(
"lightDirX", lightDir_.x);
282 ssgi_->
sendFloat(
"lightDirY", lightDir_.y);
283 ssgi_->
sendFloat(
"lightDirZ", lightDir_.z);
284 ssgi_->
sendFloat(
"lightR", lightColor_.x);
285 ssgi_->
sendFloat(
"lightG", lightColor_.y);
286 ssgi_->
sendFloat(
"lightB", lightColor_.z);
289 ssgi_->
sendFloat(
"normalValid", worldNormal_ ? 1.f : 0.f);
292 const uint32_t packedPhase = (samplingFrame_ & 7u) +
293 uint32_t(std::max(depthPyramidLevels_, 0)) * 8u;
294 ssgi_->
sendFloat(
"thickness",
float(packedPhase) +
295 std::clamp(thickness_, 0.01f, 0.99f));
299void GlobalIllumination::drawFullscreen(Graphics *gfx, Texture *
source, Shader *
shader,
301 if (!gfx)
throw eve::Exception(
"GlobalIllumination: null graphics");
304 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
306 gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
308 gfx->drawTexturedRectShader4(
source, hwDepth, worldNormal_ ? worldNormal_ :
source,
310 Color(1.f, 1.f, 1.f, 1.f));
312 gfx->drawTexturedRectShader(
source,
shader, 0.f, 0.f, dw, dh,
Color(1.f, 1.f, 1.f, 1.f));
316 if (!packedAlbedo)
throw eve::Exception(
"GlobalIllumination.applyFromDepth: null packedAlbedo");
320 drawFullscreen(gfx, packedAlbedo, ssgi_,
nullptr);
325 if (!hwDepth)
throw eve::Exception(
"GlobalIllumination.applyFromScene: null hwDepth");
327 uploadUniforms(
color->getWidth(),
color->getHeight());
328 ssgi_->
sendFloat(
"normalValid", worldNormal_ ? 1.f : 0.f);
329 Texture *traceDepth = depthPyramid_ && depthPyramidLevels_ > 0 ? depthPyramid_ : hwDepth;
330 drawFullscreen(gfx,
color, ssgi_, traceDepth);
334 if (!gfx)
throw eve::Exception(
"GlobalIllumination.applyFromDepthTo: null graphics");
335 if (!dest)
throw eve::Exception(
"GlobalIllumination.applyFromDepthTo: null dest");
343 return getGiCanvas();
348 return historyValid_ && historyRead_ ? historyRead_->
getTexture()
349 : (giCanvas_ ? giCanvas_->
getTexture() :
nullptr);
352void GlobalIllumination::ensureHistory(
int width,
int height) {
355 if (!historyA_ || !historyB_ || historyWidth_ !=
width || historyHeight_ !=
height) {
360 historyRead_ = historyA_;
361 historyWrite_ = historyB_;
362 historyWidth_ =
width;
364 historyValid_ =
false;
368void GlobalIllumination::resolveTemporal(Graphics *gfx, Texture *
current) {
369 if (!gfx || !
current || !temporalMotionDepth_ || !temporal_) {
370 historyValid_ =
false;
374 const glm::mat4 currentViewProj = glm::inverse(invViewProj_);
375 const glm::mat4 reprojection = previousViewProj_ * invViewProj_;
376 const float *matrix = &reprojection[0][0];
377 for (
int i = 0; i < 16; ++i)
378 temporal_->
sendFloat(
"reprojection" + std::to_string(i), matrix[i]);
379 temporal_->
sendFloat(
"temporalTexelW", 1.f /
float(historyWidth_));
380 temporal_->
sendFloat(
"temporalTexelH", 1.f /
float(historyHeight_));
381 temporal_->
sendFloat(
"temporalNearZ", nearZ_);
382 temporal_->
sendFloat(
"temporalFarZ", farZ_);
383 temporal_->
sendFloat(
"temporalCurrentWeight", temporalCurrentWeight_);
384 temporal_->
sendFloat(
"temporalEnergyEncoded", 1.f);
388 for (
int pass = 0; pass < spatialPasses; ++pass) {
389 Canvas *
target = (pass & 1) == 0 ? spatialA_ : spatialB_;
390 temporal_->
sendFloat(
"temporalHistoryValid", 0.f);
391 temporal_->
sendFloat(
"spatialFilterStrength", spatialFilterStrength_);
392 temporal_->
sendFloat(
"spatialStep",
float(1 << pass));
394 gfx->drawTexturedRectShaderDepthMotion(
395 filtered, filtered, temporalMotionDepth_, temporal_, 0.f, 0.f,
float(historyWidth_),
396 float(historyHeight_),
Color(1.f, 1.f, 1.f, 1.f));
397 filtered =
target->getTexture();
400 temporal_->
sendFloat(
"temporalHistoryValid", historyValid_ && viewProjValid_ ? 1.f : 0.f);
401 temporal_->
sendFloat(
"spatialFilterStrength", spatialPasses == 0 ? spatialFilterStrength_ : 0.f);
402 temporal_->
sendFloat(
"spatialStep", 1.f);
403 gfx->setCanvas(historyWrite_);
405 gfx->drawTexturedRectShaderDepthMotion(
406 filtered, history, temporalMotionDepth_, temporal_, 0.f, 0.f,
float(historyWidth_),
407 float(historyHeight_),
Color(1.f, 1.f, 1.f, 1.f));
408 std::swap(historyRead_, historyWrite_);
409 historyValid_ =
true;
410 viewProjValid_ =
true;
411 previousViewProj_ = currentViewProj;
415 if (!gfx)
throw eve::Exception(
"GlobalIllumination.applyFromSceneTo: null graphics");
416 if (!dest)
throw eve::Exception(
"GlobalIllumination.applyFromSceneTo: null dest");
426 if (!hwDepth)
throw eve::Exception(
"GlobalIllumination.applyFromSceneTo: null hwDepth");
427 Canvas *canvas = getGiCanvas();
428 if (!canvas)
throw eve::Exception(
"GlobalIllumination.applyFromSceneTo: failed to create canvas");
std::array< float, 3 > scale
const UnitySourceAsset & source
EVENGINE_API_FOUNDATION public API.
virtual int getWidth() const =0
Returns the width.
virtual Texture * getTexture()=0
Sampleable color buffer; screen Canvas returns nullptr.
virtual int getHeight() const =0
Returns the height.
void applyFromDepth(Graphics *gfx, Texture *packedAlbedo)
Overlay bounced light onto the currently bound canvas / screen. Packed path (tests): RGB=albedo/lit,...
Canvas * getWorkingCanvas()
Returns the working canvas.
void applyFromScene(Graphics *gfx, Texture *color, Texture *hwDepth)
Applies from scene.
void setResolutionScale(float scale)
Set internal GI resolution scale in the range 0.25 to 1.0.
bool hasParam(const std::string &name) const
True when param.
void applyFromSceneTo(Graphics *gfx, Texture *color, Texture *hwDepth, Canvas *dest)
Applies from scene to.
void setLightDirection(float dx, float dy, float dz)
Sets the light direction.
Texture * getWorkingTexture()
Temporally resolved GI texture, or the raw working texture before history exists. @lifetime Returned ...
~GlobalIllumination()
Global illumination.
void setQuality(const std::string &quality)
"low" | "medium" | "high" | "ultra" (unknown becomes medium).
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 depth reconstruction (RH + ZO). Builds inv(viewProj) and near/far used by applyFromDepth.
int getSampleCount() const
Returns the sample count.
void setFloat(const std::string &name, float value)
Sets the float.
void setRadius(float radius)
Sets the radius.
GlobalIllumination(Graphics *gfx)
Global illumination.
void setThickness(float thickness)
Set world-space hit thickness used to reject screen-space GI leaks.
void setIntensity(float intensity)
Sets the intensity.
void setInvViewProj(const glm::mat4 &invViewProj)
Sets the inv view proj.
int getSpatialPassCount() const
Number of explicit A-trous prefilter passes for the active quality.
void setLightColor(float r, float g, float b)
Sets the light color.
void applyFromDepthTo(Graphics *gfx, Texture *packedAlbedo, Canvas *dest)
Applies from depth to.
float getFloat(const std::string &name) const
Returns the float.
int getPixelHeight() const
Returns the pixel height.
virtual Canvas * getCanvas() const =0
Returns the canvas.
virtual void setCanvas(Canvas *canvas)=0
nullptr or this → screen. Switching flushes pending draws to the previous target.
virtual Canvas * newHDRCanvas(int width, int height)=0
Create an engine-internal linear RGBA16F post-process target.
int getPixelWidth() const
Returns the pixel width.
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.
constexpr const char * kSsrTemporal
constexpr const char * kSsgi
constexpr const char * kScreenEffectCommon
卡牌游戏 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...