9#include "graphics/shaders/aa_fxaa_frag_spv.inc"
10#include "graphics/shaders/aa_nfaa_frag_spv.inc"
11#include "graphics/shaders/aa_smaa_frag_spv.inc"
12#include "graphics/shaders/aa_ssaa_frag_spv.inc"
13#include "graphics/shaders/aa_taa_frag_spv.inc"
21#include <glm/vec4.hpp>
22#include <glm/geometric.hpp>
23#include <glm/gtc/matrix_inverse.hpp>
24#include <glm/gtc/type_ptr.hpp>
29std::vector<uint32_t> copySpv(
const uint32_t *data,
size_t count) {
30 return std::vector<uint32_t>(data, data +
count);
33Shader *newPostShader(Graphics *gfx,
const std::vector<uint32_t> &
frag,
const char *wgsl) {
34 if (gfx->getBackendName() ==
"webgpu")
36 return gfx->newShaderFromSpv({},
frag);
39Shader *createFxaaShader(Graphics *gfx) {
41 auto frag = copySpv(aa_fxaa_frag_spv, aa_fxaa_frag_spv_count);
43 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"AntiAliasing: failed to create FXAA shader");
44 sh->declareFloat(
"texelW");
45 sh->declareFloat(
"texelH");
46 sh->declareFloat(
"edgeThreshold");
47 sh->declareFloat(
"edgeThresholdMin");
48 sh->declareFloat(
"subpix");
49 sh->declareFloat(
"quality");
50 sh->sendFloat(
"texelW", 1.f / 256.f);
51 sh->sendFloat(
"texelH", 1.f / 256.f);
52 sh->sendFloat(
"edgeThreshold", 0.166f);
53 sh->sendFloat(
"edgeThresholdMin", 0.0833f);
54 sh->sendFloat(
"subpix", 0.75f);
55 sh->sendFloat(
"quality", 1.f);
59Shader *createSmaaShader(Graphics *gfx) {
61 auto frag = copySpv(aa_smaa_frag_spv, aa_smaa_frag_spv_count);
63 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"AntiAliasing: failed to create SMAA shader");
64 sh->declareFloat(
"texelW");
65 sh->declareFloat(
"texelH");
66 sh->declareFloat(
"threshold");
67 sh->declareFloat(
"localContrast");
68 sh->declareFloat(
"maxSearch");
69 sh->declareFloat(
"cornerRounding");
70 sh->sendFloat(
"texelW", 1.f / 256.f);
71 sh->sendFloat(
"texelH", 1.f / 256.f);
72 sh->sendFloat(
"threshold", 0.1f);
73 sh->sendFloat(
"localContrast", 0.5f);
74 sh->sendFloat(
"maxSearch", 8.f);
75 sh->sendFloat(
"cornerRounding", 0.25f);
79Shader *createSsaaShader(Graphics *gfx) {
81 auto frag = copySpv(aa_ssaa_frag_spv, aa_ssaa_frag_spv_count);
83 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"AntiAliasing: failed to create SSAA shader");
84 sh->declareFloat(
"texelW");
85 sh->declareFloat(
"texelH");
86 sh->declareFloat(
"scale");
87 sh->declareFloat(
"kernel");
88 sh->declareFloat(
"sampleRadius");
89 sh->sendFloat(
"texelW", 1.f / 512.f);
90 sh->sendFloat(
"texelH", 1.f / 512.f);
91 sh->sendFloat(
"scale", 2.f);
92 sh->sendFloat(
"kernel", 1.f);
93 sh->sendFloat(
"sampleRadius", 1.f);
97Shader *createNfaaShader(Graphics *gfx) {
99 auto frag = copySpv(aa_nfaa_frag_spv, aa_nfaa_frag_spv_count);
101 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"AntiAliasing: failed to create NFAA shader");
102 sh->declareFloat(
"texelW");
103 sh->declareFloat(
"texelH");
104 sh->declareFloat(
"strength");
105 sh->declareFloat(
"power");
106 sh->declareFloat(
"blurScale");
107 sh->sendFloat(
"texelW", 1.f / 256.f);
108 sh->sendFloat(
"texelH", 1.f / 256.f);
109 sh->sendFloat(
"strength", 1.f);
110 sh->sendFloat(
"power", 1.f);
111 sh->sendFloat(
"blurScale", 1.f);
115Shader *createTaaShader(Graphics *gfx) {
117 auto frag = copySpv(aa_taa_frag_spv, aa_taa_frag_spv_count);
119 if (!sh || !sh->gpuHandle)
throw eve::Exception(
"AntiAliasing: failed to create TAA shader");
120 sh->declareFloat(
"texelW");
121 sh->declareFloat(
"texelH");
122 sh->declareFloat(
"blendCurrent");
123 sh->declareFloat(
"clampAmount");
124 sh->declareFloat(
"historyValid");
125 sh->declareFloat(
"jitterDeltaX");
126 sh->declareFloat(
"jitterDeltaY");
127 for (
int i = 0; i < 16; ++i) sh->declareFloat(
"reprojection" + std::to_string(i));
128 sh->declareFloat(
"temporalNearZ");
129 sh->declareFloat(
"temporalFarZ");
130 sh->declareFloat(
"reprojectionValid");
131 sh->declareFloat(
"motionValid");
132 sh->sendFloat(
"texelW", 1.f / 256.f);
133 sh->sendFloat(
"texelH", 1.f / 256.f);
134 sh->sendFloat(
"blendCurrent", 0.92f);
135 sh->sendFloat(
"clampAmount", 0.85f);
136 sh->sendFloat(
"historyValid", 0.f);
137 sh->sendFloat(
"jitterDeltaX", 0.f);
138 sh->sendFloat(
"jitterDeltaY", 0.f);
139 for (
int i = 0; i < 16; ++i)
140 sh->sendFloat(
"reprojection" + std::to_string(i), i % 5 == 0 ? 1.f : 0.f);
141 sh->sendFloat(
"temporalNearZ", 0.1f);
142 sh->sendFloat(
"temporalFarZ", 100.f);
143 sh->sendFloat(
"reprojectionValid", 0.f);
144 sh->sendFloat(
"motionValid", 0.f);
151 fxaa_ = createFxaaShader(gfx);
152 smaa_ = createSmaaShader(gfx);
153 ssaa_ = createSsaaShader(gfx);
154 nfaa_ = createNfaaShader(gfx);
155 taa_ = createTaaShader(gfx);
156 applyQualityDefaults();
161void AntiAliasing::applyQualityDefaults() {
162 if (quality_ ==
"low") {
163 fxaa_->
sendFloat(
"edgeThreshold", 0.25f);
164 fxaa_->
sendFloat(
"edgeThresholdMin", 0.0833f);
168 smaa_->
sendFloat(
"localContrast", 0.55f);
170 smaa_->
sendFloat(
"cornerRounding", 0.1f);
181 if (quality_ ==
"high") {
182 fxaa_->
sendFloat(
"edgeThreshold", 0.125f);
183 fxaa_->
sendFloat(
"edgeThresholdMin", 0.0312f);
189 smaa_->
sendFloat(
"cornerRounding", 0.4f);
202 fxaa_->
sendFloat(
"edgeThreshold", 0.166f);
203 fxaa_->
sendFloat(
"edgeThresholdMin", 0.0833f);
209 smaa_->
sendFloat(
"cornerRounding", 0.25f);
221 if (quality ==
"low" || quality ==
"medium" || quality ==
"high")
225 applyQualityDefaults();
229 const std::string next =
230 (mode ==
"fxaa" || mode ==
"smaa" || mode ==
"ssaa" || mode ==
"nfaa" || mode ==
"taa")
233 if (next ==
"taa" && mode_ !=
"taa") resetHistory();
237void AntiAliasing::resetHistory() {
238 taaHistoryValid_ =
false;
241void AntiAliasing::ensureHistoryCanvases(
int width,
int height) {
244 if (!taaHistoryA_ || !taaHistoryB_ || historyW_ !=
width || historyH_ !=
height) {
247 if (!taaHistoryA_ || !taaHistoryB_)
248 throw eve::Exception(
"AntiAliasing: failed to allocate temporal history canvases");
249 historyRead_ = taaHistoryA_;
250 historyWrite_ = taaHistoryB_;
255 if (!historyRead_) historyRead_ = taaHistoryA_;
256 if (!historyWrite_) historyWrite_ = taaHistoryB_;
259void AntiAliasing::swapHistoryBuffers() {
260 std::swap(historyRead_, historyWrite_);
263Shader *AntiAliasing::shaderForMode()
const {
264 if (mode_ ==
"taa")
return taa_;
265 if (mode_ ==
"smaa")
return smaa_;
266 if (mode_ ==
"ssaa")
return ssaa_;
267 if (mode_ ==
"nfaa")
return nfaa_;
274 Shader *sh = shaderForMode();
279 Shader *sh = shaderForMode();
280 if (!sh)
throw eve::Exception(
"AntiAliasing.setFloat: null shader");
285 Shader *sh = shaderForMode();
286 if (!sh)
throw eve::Exception(
"AntiAliasing.getFloat: null shader");
294 if (mode_ !=
"ssaa")
return 1.f;
295 if (quality_ ==
"high")
return 4.f;
300 if (destSize < 1) destSize = 1;
302 return std::max(1,
int(std::floor(
float(destSize) *
s)));
307 const int w = std::max(1,
source->getWidth());
308 const int h = std::max(1,
source->getHeight());
309 const float tw = 1.f / float(
w);
310 const float th = 1.f / float(
h);
311 Shader *sh = shaderForMode();
314 if (mode_ ==
"ssaa" && sh->
hasUniform(
"scale")) {
323void AntiAliasing::drawFullscreen(Graphics *gfx, Texture *
source, Shader *
shader) {
327 uploadScreenUniforms(
source);
328 const float dw = gfx->getCanvas() ? float(gfx->getCanvas()->getWidth()) : float(gfx->getWidth());
330 gfx->getCanvas() ? float(gfx->getCanvas()->getHeight()) : float(gfx->getHeight());
331 gfx->drawTexturedRectShader(
source,
shader, 0.f, 0.f, dw, dh, glm::vec4(1.f, 1.f, 1.f, 1.f));
338 ensureHistoryCanvases(std::max(1,
source->getWidth()), std::max(1,
source->getHeight()));
339 if (!historyRead_ || !historyWrite_)
340 throw eve::Exception(
"AntiAliasing.beginTemporalFrame: history canvases missing");
341 uploadScreenUniforms(
source);
342 taa_->
sendFloat(
"historyValid", taaHistoryValid_ ? 1.f : 0.f);
343 const glm::vec2 jitterDelta = (previousJitterNdc_ - temporalJitterNdc_) * 0.5f;
344 taa_->
sendFloat(
"jitterDeltaX", jitterDelta.x);
345 taa_->
sendFloat(
"jitterDeltaY", jitterDelta.y);
346 const glm::mat4 reprojection = previousViewProj_ * glm::inverse(temporalViewProj_);
347 const float *matrixData = glm::value_ptr(reprojection);
348 for (
int i = 0; i < 16; ++i)
349 taa_->
sendFloat(
"reprojection" + std::to_string(i), matrixData[i]);
350 taa_->
sendFloat(
"temporalNearZ", temporalNearZ_);
351 taa_->
sendFloat(
"temporalFarZ", temporalFarZ_);
353 taaHistoryValid_ && temporalViewValid_ ? 1.f : 0.f);
354 taa_->
sendFloat(
"motionValid", motion ? 1.f : 0.f);
355 return historyWrite_;
359 return taaHistoryValid_ && historyRead_ ? historyRead_->
getTexture() :
nullptr;
363 taaHistoryValid_ =
true;
364 previousJitterNdc_ = temporalJitterNdc_;
365 previousViewProj_ = temporalViewProj_;
366 temporalViewValid_ =
true;
367 temporalObjectHistory_ = std::move(temporalObjectPending_);
368 temporalObjectPending_.clear();
369 ++temporalFrameIndex_;
370 swapHistoryBuffers();
374 auto halton = [](uint32_t
index, uint32_t base) {
376 float fraction = 1.f / float(base);
378 result += fraction * float(
index % base);
380 fraction /= float(base);
384 const uint32_t sample = temporalFrameIndex_ % 8u + 1u;
385 temporalJitterNdc_.x = 2.f * (halton(sample, 2u) - 0.5f) /
float(std::max(1,
width));
386 temporalJitterNdc_.y = 2.f * (halton(sample, 3u) - 0.5f) /
float(std::max(1,
height));
387 return temporalJitterNdc_;
393 const float forwardLength = glm::length(forward);
394 if (forwardLength > 1e-5f) forward /= forwardLength;
395 if (temporalCameraValid_) {
396 const float positionCut = std::max(1.f, forwardLength * 0.25f);
397 const bool cut = glm::length(
eye - temporalEye_) > positionCut ||
398 glm::dot(forward, temporalForward_) < 0.94f ||
399 std::fabs(fovYDeg - temporalFovY_) > 5.f;
406 temporalForward_ = forward;
407 temporalFovY_ = fovYDeg;
408 temporalCameraValid_ =
true;
418 temporalViewProj_ = viewProjection;
419 temporalNearZ_ = std::max(nearZ, 1e-4f);
420 temporalFarZ_ = std::max(farZ, temporalNearZ_ + 1e-3f);
424 const glm::mat4 &
model) {
425 if (!objectKey)
return glm::vec2(0.f);
426 temporalObjectPending_[objectKey] =
model;
427 const auto previous = temporalObjectHistory_.find(objectKey);
428 if (!taaHistoryValid_ || !temporalViewValid_ ||
previous == temporalObjectHistory_.end())
429 return glm::vec2(0.f);
430 const glm::vec4
origin(0.f, 0.f, 0.f, 1.f);
431 const glm::vec4 staticPrevious = previousViewProj_ *
model *
origin;
432 const glm::vec4 actualPrevious = previousViewProj_ *
previous->second *
origin;
433 if (std::fabs(staticPrevious.w) < 1e-6f || std::fabs(actualPrevious.w) < 1e-6f)
434 return glm::vec2(0.f);
435 const glm::vec2 staticUv = glm::vec2(staticPrevious) / staticPrevious.w * 0.5f + 0.5f;
436 const glm::vec2 actualUv = glm::vec2(actualPrevious) / actualPrevious.w * 0.5f + 0.5f;
437 return glm::clamp(actualUv - staticUv, glm::vec2(-1.f), glm::vec2(1.f));
441 if (!gfx)
throw eve::Exception(
"AntiAliasing.applyTemporal: null graphics");
445 const float dw = finalCanvas ? float(finalCanvas->
getWidth()) : float(gfx->
getWidth());
446 const float dh = finalCanvas ? float(finalCanvas->
getHeight()) : float(gfx->
getHeight());
450 if (!sh)
throw eve::Exception(
"AntiAliasing.applyTemporal: missing taa shader");
456 glm::vec4(1.f, 1.f, 1.f, 1.f));
459 if (!resolved)
throw eve::Exception(
"AntiAliasing.applyTemporal: resolved texture missing");
461 gfx->
drawTexturedRectShaderUV(resolved,
nullptr, 0.f, 0.f, dw, dh, 0.f, 0.f, 1.f, 1.f,
462 glm::vec4(1.f, 1.f, 1.f, 1.f),
false,
468 if (!gfx)
throw eve::Exception(
"AntiAliasing.resolveTemporal: null graphics");
479 glm::vec4(1.f, 1.f, 1.f, 1.f));
481 if (!resolved)
throw eve::Exception(
"AntiAliasing.resolveTemporal: result missing");
488 float height,
float r,
float g,
float b,
float a) {
489 if (!gfx)
throw eve::Exception(
"AntiAliasing.applyTemporalRect: null graphics");
496 if (!sh)
throw eve::Exception(
"AntiAliasing.applyTemporal: missing taa shader");
503 glm::vec4(1.f, 1.f, 1.f, 1.f));
507 if (!resolved)
throw eve::Exception(
"AntiAliasing.applyTemporal: resolved texture missing");
509 gfx->
drawTexturedRectShaderUV(resolved,
nullptr,
x,
y,
width,
height, 0.f, 0.f, 1.f, 1.f,
519 drawFullscreen(gfx,
source, shaderForMode());
523 if (!gfx)
throw eve::Exception(
"AntiAliasing.applyTo: null graphics");
524 if (!dest)
throw eve::Exception(
"AntiAliasing.applyTo: null dest");
534 if (!tex)
throw eve::Exception(
"AntiAliasing.applyCanvas: source has no sampleable texture");
541 if (!tex)
throw eve::Exception(
"AntiAliasing.applyCanvasTo: source has no sampleable texture");
graphics::Canvas * previous
const UnitySourceAsset & source
EVENGINE_API_FOUNDATION public API.
void setTemporalCamera(const glm::vec3 &eye, const glm::vec3 &target, float fovYDeg)
Detect camera cuts and invalidate incompatible temporal history.
bool hasParam(const std::string &name) const
True when param.
void apply(Graphics *gfx, Texture *source)
Apply current mode to source, writing into the currently bound canvas / screen. Uploads texelW/texelH...
Texture * getTemporalReadTexture() const
Previous resolved frame, or nullptr when history is invalid. @lifetime Returned texture is borrowed f...
glm::vec2 prepareTemporalJitter(int width, int height)
Return this frame's Halton projection jitter in NDC units.
Canvas * beginTemporalFrame(Texture *source, Texture *motion=nullptr)
Prepare uniforms/resources for a backend-recorded temporal pass. @lifetime Returned canvas is borrowe...
void setMode(const std::string &mode)
"fxaa" | "smaa" | "ssaa" | "nfaa" | "taa" (unknown → fxaa).
void invalidateTemporalHistory()
Discard TAA history after a render-chain discontinuity.
void prepareSource(Texture *source)
Upload texel uniforms from source without drawing (3D scene-color resolve).
Shader * getTaaShader() const
Returns the taa shader.
glm::vec2 prepareTemporalObjectMotion(const void *objectKey, const glm::mat4 &model)
Per-object UV correction relative to static-world camera reprojection.
Texture * resolveTemporal(Graphics *gfx, Texture *source, Texture *motion=nullptr)
Resolve TAA into history and return the linear HDR result without compositing it. @lifetime Returned ...
float suggestScale() const
Suggested supersample scale for the active quality when using "ssaa" (2 for low/medium,...
void applyTo(Graphics *gfx, Texture *source, Canvas *dest)
Applies to.
void setTemporalViewProjection(const glm::mat4 &viewProjection, float nearZ, float farZ)
Set the jittered camera matrix used for depth-based history reprojection.
~AntiAliasing()
Anti aliasing.
float getFloat(const std::string &name) const
Returns the float.
void applyTemporal(Graphics *gfx, Texture *source, Texture *motion=nullptr)
Applies temporal.
Shader * getShader() const
Returns the shader.
void setFloat(const std::string &name, float value)
Sets the float.
AntiAliasing(Graphics *gfx)
Anti aliasing.
void applyCanvas(Graphics *gfx, Canvas *source)
Applies canvas.
int resolutionFor(int destSize) const
Resolution for.
void applyCanvasTo(Graphics *gfx, Canvas *source, Canvas *dest)
Applies canvas to.
void endTemporalFrame()
Mark the prepared write target valid and rotate temporal buffers.
void applyTemporalRect(Graphics *gfx, Texture *source, float x, float y, float width, float height, float r=1.f, float g=1.f, float b=1.f, float a=1.f)
Resolve TAA history and composite it into an arbitrary destination rectangle.
void setQuality(const std::string &quality)
"low" | "medium" | "high" (unknown → medium).
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 invalidateHistory()
Invalidate eye-adaptation history, for camera cuts and mode changes.
int getWidth() const
Returns the width.
virtual void drawTexturedRectShaderUV(Texture *texture, Shader *shader, float x, float y, float w, float h, float u0, float v0, float u1, float v1, const Color &color, bool rotatedUV=false, BlendMode blend=BlendMode::Alpha)=0
UV draw with an explicit Shader (nullptr = default textured pipeline).
Exposure * pipelineExposure()
Pipeline-owned GPU exposure metering and eye adaptation. @lifetime Returned effect remains valid unti...
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 void drawTexturedRectShaderDepthMotion(Texture *color, Texture *depth, Texture *motion, Shader *shader, float x, float y, float w, float h, const Color &tint)=0
Post draw with color, depth/history and motion/reactive textures.
virtual Canvas * newHDRCanvas(int width, int height)=0
Create an engine-internal linear RGBA16F post-process target.
int getHeight() const
Returns the height.
bool hasUniform(const std::string &name) const
void sendFloat(const std::string &name, float x)
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.
constexpr const char * kSmaa
constexpr const char * kPostCommon
constexpr const char * kTaa
constexpr const char * kFxaa
constexpr const char * kNfaa
constexpr const char * kSsaa
卡牌游戏 UI 工具模块:工厂 + 脚本绑定入口。 功能参考 ycarowr/UiCard:扇形手牌布局、抽牌/洗牌、悬浮放大、拖拽到落牌区、 敌方手牌(背面/偷看)、费用不足置灰,以及可实时调节的布局...