载入中...
搜索中...
未找到
ScreenSpaceReflection.cpp
浏览该文件的文档.
2
3#include "common/Exception.h"
4#include "graphics/Canvas.h"
6#include "graphics/Graphics.h"
7#include "graphics/Shader.h"
8#include "graphics/Texture.h"
9#include "graphics/shaders/ssr_frag_spv.inc"
10#include "graphics/shaders/ssr_temporal_frag_spv.inc"
12
13#include <algorithm>
14#include <cmath>
15
16namespace eve::graphics {
17
18namespace {
19Shader *createSsrShader(Graphics *gfx) {
20 std::vector<uint32_t> frag(ssr_frag_spv, ssr_frag_spv + ssr_frag_spv_count);
21 Shader *sh = gfx->getBackendName() == "webgpu"
22 ? gfx->newShaderFromWgsl({}, std::string(shaders::kScreenEffectCommon) +
24 : gfx->newShaderFromSpv({}, frag);
25 sh->declareMatrix("invViewProj");
26 sh->declareVec3("cameraPos");
27 sh->declareFloat("nearZ");
28 sh->declareFloat("farZ");
29 sh->declareFloat("texelW");
30 sh->declareFloat("texelH");
31 sh->declareFloat("maxDist");
32 sh->declareFloat("stepLen");
33 sh->declareFloat("maxSteps");
34 sh->declareFloat("thickness");
35 sh->declareFloat("strength");
36 sh->declareFloat("enabled");
37 sh->declareFloat("bias");
38 sh->declareFloat("maxRoughness");
39 sh->declareFloat("depthPyramidLevels");
40 return sh;
41}
42
43Shader *createSsrTemporalShader(Graphics *gfx) {
44 std::vector<uint32_t> frag(ssr_temporal_frag_spv,
45 ssr_temporal_frag_spv + ssr_temporal_frag_spv_count);
46 Shader *sh = gfx->getBackendName() == "webgpu"
47 ? gfx->newShaderFromWgsl({}, std::string(shaders::kScreenEffectCommon) +
49 : gfx->newShaderFromSpv({}, frag);
50 for (int i = 0; i < 16; ++i) sh->declareFloat("reprojection" + std::to_string(i));
51 sh->declareFloat("temporalTexelW");
52 sh->declareFloat("temporalTexelH");
53 sh->declareFloat("temporalHistoryValid");
54 sh->declareFloat("temporalNearZ");
55 sh->declareFloat("temporalFarZ");
56 sh->declareFloat("temporalCurrentWeight");
57 sh->declareFloat("spatialFilterStrength");
58 sh->declareFloat("temporalEnergyEncoded");
59 sh->declareFloat("spatialStep");
60 return sh;
61}
62} // namespace
63
65 ssr_ = createSsrShader(gfx);
66 temporal_ = createSsrTemporalShader(gfx);
67}
68
70 // Shader is owned by Graphics.
71 gfx_ = nullptr;
72 ssr_ = nullptr;
73 temporal_ = nullptr;
74}
75
77 const glm::mat4 nextViewProj = glm::inverse(invViewProj);
78 if (viewProjValid_) {
79 float maxDelta = 0.f;
80 for (int c = 0; c < 4; ++c)
81 for (int r = 0; r < 4; ++r)
82 maxDelta = std::max(maxDelta,
83 std::abs(nextViewProj[c][r] - previousViewProj_[c][r]));
84 if (maxDelta > 2.f) historyValid_ = false;
85 }
86 invViewProj_ = invViewProj;
87 ssr_->sendMatrix("invViewProj", invViewProj_);
88}
89
90void ScreenSpaceReflection::ensureHistory(int width, int height) {
91 width = std::max(width, 1);
92 height = std::max(height, 1);
93 if (!historyA_ || !historyB_ || historyWidth_ != width || historyHeight_ != height) {
94 historyA_ = gfx_->newHDRCanvas(width, height);
95 historyB_ = gfx_->newHDRCanvas(width, height);
96 spatialA_ = gfx_->newHDRCanvas(width, height);
97 spatialB_ = gfx_->newHDRCanvas(width, height);
98 historyRead_ = historyA_;
99 historyWrite_ = historyB_;
100 historyWidth_ = width;
101 historyHeight_ = height;
102 historyValid_ = false;
103 }
104}
105
106void ScreenSpaceReflection::resolveTemporal(Graphics *gfx, Texture *current,
107 Texture *motionDepth) {
108 if (!current || !motionDepth || !temporal_) return;
109 ensureHistory(current->getWidth(), current->getHeight());
110 const glm::mat4 currentViewProj = glm::inverse(invViewProj_);
111 const glm::mat4 reprojection = previousViewProj_ * invViewProj_;
112 const float *matrix = &reprojection[0][0];
113 for (int i = 0; i < 16; ++i)
114 temporal_->sendFloat("reprojection" + std::to_string(i), matrix[i]);
115 temporal_->sendFloat("temporalTexelW", 1.f / float(historyWidth_));
116 temporal_->sendFloat("temporalTexelH", 1.f / float(historyHeight_));
117 temporal_->sendFloat("temporalNearZ", nearZ_);
118 temporal_->sendFloat("temporalFarZ", farZ_);
119 temporal_->sendFloat("temporalCurrentWeight", temporalCurrentWeight_);
120 temporal_->sendFloat("temporalEnergyEncoded", 0.f);
121
122 const int spatialPasses = getSpatialPassCount();
123 Texture *filtered = current;
124 for (int pass = 0; pass < spatialPasses; ++pass) {
125 Canvas *target = (pass & 1) == 0 ? spatialA_ : spatialB_;
126 temporal_->sendFloat("temporalHistoryValid", 0.f);
127 temporal_->sendFloat("spatialFilterStrength", spatialFilterStrength_);
128 temporal_->sendFloat("spatialStep", float(1 << pass));
129 gfx->setCanvas(target);
130 gfx->drawTexturedRectShaderDepthMotion(
131 filtered, filtered, motionDepth, temporal_, 0.f, 0.f, float(historyWidth_),
132 float(historyHeight_), Color(1.f, 1.f, 1.f, 1.f));
133 filtered = target->getTexture();
134 }
135
136 temporal_->sendFloat("temporalHistoryValid", historyValid_ && viewProjValid_ ? 1.f : 0.f);
137 temporal_->sendFloat("spatialFilterStrength", spatialPasses == 0 ? spatialFilterStrength_ : 0.f);
138 temporal_->sendFloat("spatialStep", 1.f);
139 gfx->setCanvas(historyWrite_);
140 Texture *history = historyValid_ ? historyRead_->getTexture() : filtered;
141 gfx->drawTexturedRectShaderDepthMotion(
142 filtered, history, motionDepth, temporal_, 0.f, 0.f, float(historyWidth_),
143 float(historyHeight_), Color(1.f, 1.f, 1.f, 1.f));
144 std::swap(historyRead_, historyWrite_);
145 historyValid_ = true;
146 viewProjValid_ = true;
147 previousViewProj_ = currentViewProj;
148}
149
150void ScreenSpaceReflection::setCamera(float eyeX, float eyeY, float eyeZ, float targetX,
151 float targetY, float targetZ, float upX, float upY,
152 float upZ, float fovYDeg, float aspect, float nearZ,
153 float farZ) {
154 nearZ_ = nearZ > 1e-4f ? nearZ : 0.1f;
155 farZ_ = farZ > nearZ_ ? farZ : nearZ_ + 1.f;
156 const float aspectSafe = aspect > 1e-4f ? aspect : 1.f;
157 const float fovRad = fovYDeg * 0.017453292519943295f;
158 const glm::vec3 eye(eyeX, eyeY, eyeZ);
159 glm::vec3 forward = glm::vec3(targetX, targetY, targetZ) - eye;
160 forward = glm::length(forward) > 1e-5f ? glm::normalize(forward)
161 : glm::vec3(0.f, 0.f, -1.f);
162 if (cameraValid_ &&
163 (glm::distance(eye, previousEye_) > std::max(1.f, farZ_ * 0.1f) ||
164 glm::dot(forward, previousForward_) < 0.65f ||
165 std::abs(fovYDeg - previousFovY_) > 3.f ||
166 std::abs(aspectSafe - previousAspect_) > std::max(0.05f, previousAspect_ * 0.1f)))
167 historyValid_ = false;
168 previousEye_ = eye;
169 previousForward_ = forward;
170 previousFovY_ = fovYDeg;
171 previousAspect_ = aspectSafe;
172 cameraValid_ = true;
173 const glm::mat4 view =
174 glm::lookAtRH(eye, glm::vec3(targetX, targetY, targetZ),
175 glm::vec3(upX, upY, upZ));
176 const glm::mat4 proj = perspectiveVulkanRH_ZO(fovRad, aspectSafe, nearZ_, farZ_);
177 setInvViewProj(glm::inverse(proj * view));
178 ssr_->sendFloat("nearZ", nearZ_);
179 ssr_->sendFloat("farZ", farZ_);
180}
181
183 if (enabled_ != enabled) historyValid_ = false;
184 enabled_ = enabled;
185 ssr_->sendFloat("enabled", enabled_ ? 1.f : 0.f);
186}
187
188void ScreenSpaceReflection::setQuality(const std::string &quality) {
189 quality_ = quality == "low" || quality == "medium" || quality == "high" ||
190 quality == "ultra"
191 ? quality
192 : "medium";
193 historyValid_ = false;
194 if (quality_ == "low") {
195 setMaxSteps(48);
196 setStepLength(0.7f);
197 setThickness(0.75f);
198 setMaxRoughness(0.65f);
199 setResolutionScale(0.5f);
200 temporalCurrentWeight_ = 0.3f;
201 spatialFilterStrength_ = 0.8f;
202 } else if (quality_ == "high") {
203 setMaxSteps(160);
204 setStepLength(0.25f);
205 setThickness(0.35f);
206 setMaxRoughness(0.9f);
208 temporalCurrentWeight_ = 0.12f;
209 spatialFilterStrength_ = 0.4f;
210 } else if (quality_ == "ultra") {
211 setMaxSteps(256);
212 setStepLength(0.15f);
213 setThickness(0.22f);
214 setMaxRoughness(1.f);
216 temporalCurrentWeight_ = 0.08f;
217 spatialFilterStrength_ = 0.3f;
218 } else {
219 setMaxSteps(96);
220 setStepLength(0.4f);
221 setThickness(0.5f);
222 setMaxRoughness(0.8f);
224 temporalCurrentWeight_ = 0.16f;
225 spatialFilterStrength_ = 0.5f;
226 }
227}
228
229void ScreenSpaceReflection::setMaxDistance(float v) { setFloat("maxDist", std::max(v, 0.1f)); }
230void ScreenSpaceReflection::setStepLength(float v) { setFloat("stepLen", std::max(v, 0.01f)); }
231void ScreenSpaceReflection::setMaxSteps(int v) { setFloat("maxSteps", float(std::clamp(v, 1, 512))); }
232void ScreenSpaceReflection::setThickness(float v) { setFloat("thickness", std::max(v, 0.0f)); }
233void ScreenSpaceReflection::setStrength(float v) { setFloat("strength", std::max(v, 0.f)); }
235 setFloat("maxRoughness", std::clamp(v, 0.f, 1.f));
236}
238 const float next = std::clamp(scale, 0.25f, 1.f);
239 if (std::abs(next - resolutionScale_) < 1e-4f) return;
240 resolutionScale_ = next;
241 reflection_ = nullptr;
242 historyValid_ = false;
243}
244
245bool ScreenSpaceReflection::hasParam(const std::string &name) const {
246 return ssr_ && ssr_->hasUniform(name);
247}
248
249void ScreenSpaceReflection::setFloat(const std::string &name, float value) {
250 if (!ssr_ || !ssr_->hasUniform(name))
251 throw eve::Exception("ScreenSpaceReflection.setFloat: missing param '%s'", name.c_str());
252 ssr_->sendFloat(name, value);
253 if (name == "maxDist" || name == "stepLen" || name == "maxSteps" ||
254 name == "thickness" || name == "strength" || name == "bias" ||
255 name == "maxRoughness")
256 historyValid_ = false;
257 if (name == "maxDist") maxDist_ = value;
258 if (name == "stepLen") stepLen_ = value;
259 if (name == "maxSteps") maxSteps_ = value;
260 if (name == "thickness") thickness_ = value;
261 if (name == "strength") strength_ = value;
262 if (name == "maxRoughness") maxRoughness_ = value;
263}
264
265float ScreenSpaceReflection::getFloat(const std::string &name) const {
266 if (!ssr_ || !ssr_->hasUniform(name))
267 throw eve::Exception("ScreenSpaceReflection.getFloat: missing param '%s'", name.c_str());
268 float v = 0.f;
269 if (ssr_->getFromVar(name, &v, sizeof(v)) == int(sizeof(v))) return v;
270 throw eve::Exception("ScreenSpaceReflection.getFloat: missing param '%s'", name.c_str());
271}
272
273void ScreenSpaceReflection::uploadUniforms(int width, int height) {
274 if (width < 1) width = 1;
275 if (height < 1) height = 1;
276 ssr_->sendMatrix("invViewProj", invViewProj_);
277 ssr_->sendFloat("nearZ", nearZ_);
278 ssr_->sendFloat("farZ", farZ_);
279 ssr_->sendFloat("texelW", 1.f / float(width));
280 ssr_->sendFloat("texelH", 1.f / float(height));
281 ssr_->sendFloat("maxDist", maxDist_);
282 ssr_->sendFloat("stepLen", stepLen_);
283 ssr_->sendFloat("maxSteps", maxSteps_);
284 ssr_->sendFloat("thickness", thickness_);
285 ssr_->sendFloat("strength", strength_);
286 ssr_->sendFloat("enabled", enabled_ ? 1.f : 0.f);
287 ssr_->sendFloat("bias", bias_);
288 ssr_->sendFloat("maxRoughness", maxRoughness_);
289 ssr_->sendFloat("depthPyramidLevels", float(std::max(depthPyramidLevels_, 0)));
290}
291
293 Texture *worldNormal, Texture *albedo, Canvas *dest) {
294 if (!gfx) throw eve::Exception("ScreenSpaceReflection: null graphics");
295 if (!dest) throw eve::Exception("ScreenSpaceReflection.applyFromSceneTo: null dest");
296 Canvas *prev = gfx->getCanvas();
297 gfx->setCanvas(dest);
298 applyFromScene(gfx, sceneColor, hwDepth, worldNormal, albedo);
299 if (temporalMotionDepth_)
300 resolveTemporal(gfx, dest->getTexture(), temporalMotionDepth_);
301 else
302 historyValid_ = false;
303 gfx->setCanvas(prev);
304}
305
307 if (gfx_) {
308 const int fullW =
309 std::max(1, gfx_->getPixelWidth() > 0 ? gfx_->getPixelWidth() : gfx_->getWidth());
310 const int fullH =
311 std::max(1, gfx_->getPixelHeight() > 0 ? gfx_->getPixelHeight() : gfx_->getHeight());
312 const int targetW = std::max(1, int(std::lround(float(fullW) * resolutionScale_)));
313 const int targetH = std::max(1, int(std::lround(float(fullH) * resolutionScale_)));
314 if (!reflection_ || reflection_->getWidth() != targetW ||
315 reflection_->getHeight() != targetH)
316 reflection_ = gfx_->newHDRCanvas(targetW, targetH);
317 }
318 return reflection_;
319}
320
323 return historyValid_ && historyRead_ ? historyRead_->getTexture()
324 : (reflection_ ? reflection_->getTexture() : nullptr);
325}
326
328 Texture *worldNormal, Texture *albedo) {
329 if (!gfx) throw eve::Exception("ScreenSpaceReflection: null graphics");
330 if (!sceneColor) throw eve::Exception("ScreenSpaceReflection: null scene color");
331 if (!hwDepth) throw eve::Exception("ScreenSpaceReflection: null depth");
332 uploadUniforms(sceneColor->getWidth(), sceneColor->getHeight());
333 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
334 const float dh =
335 gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
336 Texture *traceDepth = depthPyramid_ && depthPyramidLevels_ > 0 ? depthPyramid_ : hwDepth;
337 gfx->drawTexturedRectShader4(sceneColor, traceDepth,
338 worldNormal ? worldNormal : sceneColor,
339 albedo ? albedo : sceneColor, ssr_, 0.f, 0.f, dw, dh,
340 Color(1.f, 1.f, 1.f, 1.f));
341}
342
343} // namespace eve::graphics
LogicalId target
double value
graphics::Texture * albedo
vk::ShaderModule frag
double r
float v
std::int32_t c
std::uint32_t height
std::uint32_t width
std::array< float, 3 > scale
std::string name
glm::vec3 eye
float fovRad
glm::mat4 view
glm::mat4 proj
double current
float targetX
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Canvas public API.
Definition Canvas.h:17
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.
int getPixelHeight() const
Returns the pixel height.
Definition Graphics.h:508
virtual void drawTexturedRectShader4(Texture *color, Texture *depth, Texture *motion, Texture *extra, Shader *shader, float x, float y, float w, float h, const Color &tint)=0
Post draw with four sampled textures at bindings 0, 1, 2 and 3.
int getWidth() const
Returns the width.
Definition Graphics.h:502
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.
Definition Graphics.h:506
int getHeight() const
Returns the height.
Definition Graphics.h:504
void applyFromSceneTo(Graphics *gfx, Texture *sceneColor, Texture *hwDepth, Texture *worldNormal, Canvas *dest)
Write the SSR result into the currently bound canvas / dest.
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) + near/far.
~ScreenSpaceReflection()
Screen space reflection.
void setQuality(const std::string &quality)
Set "low", "medium", "high" or "ultra" SSR quality preset.
Texture * getReflectionTexture()
The reflection texture from the owned canvas, or nullptr before first apply.
void setMaxDistance(float meters)
Sets the max distance.
void setEnabled(bool enabled)
Enable/disable the pass. When disabled it emits transparent (0 hit).
void setResolutionScale(float scale)
Set internal SSR resolution scale in the range 0.25 to 1.0.
void setThickness(float meters)
Sets the thickness.
void setMaxSteps(int steps)
Sets the max steps.
Canvas * getReflectionCanvas()
Owned reflection canvas (created on first use at the current target size).
void setStepLength(float meters)
Sets the step length.
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 setStrength(float strength)
Sets the strength.
ScreenSpaceReflection(Graphics *gfx)
Screen space reflection.
float getFloat(const std::string &name) const
Returns the float.
void setFloat(const std::string &name, float value)
Sets the float.
void applyFromScene(Graphics *gfx, Texture *sceneColor, Texture *hwDepth, Texture *worldNormal, Texture *albedo=nullptr)
Write SSR into the currently bound canvas / screen.
void setMaxRoughness(float roughness)
Skip SSR above this material roughness and rely on reflection backup.
bool hasParam(const std::string &name) const
True when param.
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
constexpr const char * kSsr
constexpr const char * kSsrTemporal
constexpr const char * kScreenEffectCommon
卡牌游戏 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
bool enabled
glm::mat4 invViewProj