透明分层屏幕空间折射 + 透明排序修正 + 默认清屏色归零
默认 ClearColor 0.1 → 0(策划阶段结束,无用)。 透明分层折射(玻璃见玻璃):透明段原来单个 ForwardTrans pass,折射 采样源 SceneColor 副本只在不透明段后快照一次(不含任何透明体)→ 前 玻璃折射不出后玻璃。改为按绘制单位远→近拆成至多 kTransparentLayers 层,每层绘制前刷新 SceneColor 副本,使层 L 玻璃采样含所有更远透明体。 RenderGraph pass 编译期定序,故层数固定;超出并入末层。深度 storeOp 改 eStore(多层需 Load 承接有效深度内容)。shader 不改。 透明排序修正:排序键由"相机到实例的欧氏距离"改为视图空间深度 (-(view·p).z)——欧氏距离把侧向偏移计入距离,偏侧物体被误判为更远 (正方体右上的甜甜圈在特定视角被排到后方)。并让透明队列逐实例成批 (不合并):透明本就是每层一个绘制单位,合并成多实例 batch 会让排序 只能用批平均深度、批内实例无法各自定序。零额外 draw 代价。
This commit is contained in:
@@ -43,7 +43,7 @@ namespace Engine::ECS {
|
|||||||
// 环境类设置已迁为环境实体组件(SkyboxComponent/SkyLightComponent,
|
// 环境类设置已迁为环境实体组件(SkyboxComponent/SkyLightComponent,
|
||||||
// UE Skybox/SkyLight Actor 语义);仅保留渲Target清屏色
|
// UE Skybox/SkyLight Actor 语义);仅保留渲Target清屏色
|
||||||
struct Settings {
|
struct Settings {
|
||||||
glm::vec4 ClearColor = {0.1, 0.1, 0.1, 1.0};
|
glm::vec4 ClearColor = {0.0, 0.0, 0.0, 1.0};
|
||||||
};
|
};
|
||||||
Settings& GetSettings() { return m_Settings; }
|
Settings& GetSettings() { return m_Settings; }
|
||||||
const Settings& GetSettings() const { return m_Settings; } // 序列化只读
|
const Settings& GetSettings() const { return m_Settings; } // 序列化只读
|
||||||
|
|||||||
@@ -37,6 +37,15 @@ namespace Engine::Passes {
|
|||||||
void RecordSharedState(const RG::RenderGraphContext& ctx,
|
void RecordSharedState(const RG::RenderGraphContext& ctx,
|
||||||
vk::CommandBuffer cmd, bool translucent) const;
|
vk::CommandBuffer cmd, bool translucent) const;
|
||||||
|
|
||||||
|
// 透明队列绘制单位总数(= 各批实例数之和;排序后扁平单位序)
|
||||||
|
u32 TransparentUnitCount() const;
|
||||||
|
|
||||||
|
// 绘制透明单位区间 [start,end)(批次内连续实例合并为一次 draw):
|
||||||
|
// 透明渲染实例 + 预乘 alpha 混合 + 只测不写深度的全部状态
|
||||||
|
void DrawTransparentRange(const RG::RenderGraphContext& ctx,
|
||||||
|
vk::CommandBuffer cmd,
|
||||||
|
u32 start, u32 end) const;
|
||||||
|
|
||||||
SceneGpu& m_Gpu;
|
SceneGpu& m_Gpu;
|
||||||
FrameSceneData& m_Frame;
|
FrameSceneData& m_Frame;
|
||||||
RG::TextureHandle m_HDRColor;
|
RG::TextureHandle m_HDRColor;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// "HitProxy" —— 拾取请求帧把不透明 + 透明 batch 用 id 管线画进
|
// "HitProxy" —— 拾取请求帧把不透明 + 透明 batch 用 id 管线画进
|
||||||
// R32Uint 目标(片元值 = 实体位模式)。深度自测自写
|
// R32Uint 目标(片元值 = 实体位模式)。深度自测自写
|
||||||
// (重清主深度):遮挡语义 = 可见表面胜出;运行在
|
// (重清主深度):遮挡语义 = 可见表面胜出;运行在
|
||||||
// Forward/ForwardTrans/PhysicsDebug 之后的 WAW 链尾,
|
// Forward/ForwardTransL*/PhysicsDebug 之后的 WAW 链尾,
|
||||||
// 主链下游无人再读深度,下帧 Forward 照常重新 Clear
|
// 主链下游无人再读深度,下帧 Forward 照常重新 Clear
|
||||||
// "HitProxyCopy" —— 请求像素 1×1 CopyImageToBuffer 进 host-visible
|
// "HitProxyCopy" —— 请求像素 1×1 CopyImageToBuffer 进 host-visible
|
||||||
// 暂存。TransferSrc use 让 RG 自动完成
|
// 暂存。TransferSrc use 让 RG 自动完成
|
||||||
|
|||||||
@@ -150,7 +150,8 @@ namespace Engine {
|
|||||||
std::vector<u32> instanceNext; // 与 instances 平行:同 batch 下一实例
|
std::vector<u32> instanceNext; // 与 instances 平行:同 batch 下一实例
|
||||||
// 每 batch 实例链头(与 result.batches 平行;kNoInstance = 空)
|
// 每 batch 实例链头(与 result.batches 平行;kNoInstance = 空)
|
||||||
std::vector<u32> batchHead;
|
std::vector<u32> batchHead;
|
||||||
// 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近)
|
// 每 batch 实例视图深度累加(透明排序用:平均深度降序 = 远→近;
|
||||||
|
// 透明队列逐实例成批故 count=1,等价逐实例深度)
|
||||||
std::vector<f32> batchDistSum;
|
std::vector<f32> batchDistSum;
|
||||||
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
||||||
// batchIdx+1,0 = 未用)——逐实例 hash 在大场景是毫秒级开销
|
// batchIdx+1,0 = 未用)——逐实例 hash 在大场景是毫秒级开销
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ namespace Engine {
|
|||||||
f32 cameraFarZ = 10000.0f;
|
f32 cameraFarZ = 10000.0f;
|
||||||
bool cameraOrtho = false;
|
bool cameraOrtho = false;
|
||||||
|
|
||||||
glm::vec4 ClearColor{0.1, 0.1, 0.1, 1.0};
|
glm::vec4 ClearColor{0.0, 0.0, 0.0, 1.0};
|
||||||
bool DrawSkybox = true;
|
bool DrawSkybox = true;
|
||||||
// 本帧生效的环境贴图资产路径(来自第一个 active 的
|
// 本帧生效的环境贴图资产路径(来自第一个 active 的
|
||||||
// SkyboxComponent;空 = 无环境 → Renderer 回中性兜底 cubemap)。
|
// SkyboxComponent;空 = 无环境 → Renderer 回中性兜底 cubemap)。
|
||||||
|
|||||||
@@ -5,12 +5,14 @@
|
|||||||
// 显示变换不在本 pass——HDRColor 经 Tonemap 归一后才写 Backbuffer
|
// 显示变换不在本 pass——HDRColor 经 Tonemap 归一后才写 Backbuffer
|
||||||
// (对齐 UE/Unity:SceneColor(HDR) → tonemapper → UI)。
|
// (对齐 UE/Unity:SceneColor(HDR) → tonemapper → UI)。
|
||||||
//
|
//
|
||||||
// 透明管线对齐 UE/Unity 标准语义:
|
// 透明管线:分层屏幕空间折射(玻璃见玻璃)
|
||||||
// - 不透明("Forward"):深度测试 + 写,无混合直写
|
// - 不透明("Forward"):深度测试 + 写,无混合直写
|
||||||
// - 透明("ForwardTrans"):独立渲染实例,深度测试(对不透明)+
|
// - 透明("ForwardTransL*"):按绘制单位拆成至多 kTransparentLayers
|
||||||
// 深度写入关闭,HDR/velocity/depth 全部 Load 承接不透明段结果,
|
// 层,远 → 近(SceneRenderer 侧按 batch 平均距离排)。每层绘制前把
|
||||||
// 预乘 alpha 混合(线性空间 over),远 → 近排序(SceneRenderer
|
// HDRColor 刷新进 SceneColor 副本("SceneColorCopy"/"SceneColorCopyL*"),
|
||||||
// 侧按 batch 平均距离排)
|
// 使该层玻璃折射采样包含所有更远的透明体。深度测试(对不透明)+
|
||||||
|
// 深度写入关闭,HDR/velocity/depth 全部 Load 承接前序结果,预乘
|
||||||
|
// alpha 混合(线性空间 over)。
|
||||||
//
|
//
|
||||||
// NVIDIA shader-object 实测注意(见 .trae/documents 性能档案):
|
// NVIDIA shader-object 实测注意(见 .trae/documents 性能档案):
|
||||||
// 早期实验称 depthWriteEnable=FALSE 有灾难病态(~5fps),但该实验
|
// 早期实验称 depthWriteEnable=FALSE 有灾难病态(~5fps),但该实验
|
||||||
@@ -29,6 +31,8 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace Engine::Passes
|
namespace Engine::Passes
|
||||||
{
|
{
|
||||||
@@ -52,6 +56,15 @@ namespace Engine::Passes
|
|||||||
vk::ColorComponentFlagBits::eG |
|
vk::ColorComponentFlagBits::eG |
|
||||||
vk::ColorComponentFlagBits::eB |
|
vk::ColorComponentFlagBits::eB |
|
||||||
vk::ColorComponentFlagBits::eA;
|
vk::ColorComponentFlagBits::eA;
|
||||||
|
|
||||||
|
// 透明折射分层上限:RenderGraph 的 pass 只注册一次、编译期定序
|
||||||
|
//(声明与执行分离),层数必须编译期固定。透明队列按远→近拆成至多
|
||||||
|
// kTransparentLayers 层,每层绘制前刷新一次场景色副本,使该层
|
||||||
|
// 玻璃的折射采样包含所有更远的透明体——前层玻璃折射出后层玻璃
|
||||||
|
//(玻璃见玻璃),而非只看到不透明段快照(UE/Unity 单快照的固有
|
||||||
|
// 局限)。超出层数的单位并入最后一层(仍正常绘制,仅同层内不再
|
||||||
|
// 互相折射)。
|
||||||
|
constexpr u32 kTransparentLayers = 8;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// 两段渲染共享的命令录制:shader/描述符绑定 + 几何/光栅/视口状态。
|
// 两段渲染共享的命令录制:shader/描述符绑定 + 几何/光栅/视口状态。
|
||||||
@@ -121,7 +134,7 @@ namespace Engine::Passes
|
|||||||
|
|
||||||
vk::ClearValue clearValue;
|
vk::ClearValue clearValue;
|
||||||
clearValue.color = vk::ClearColorValue(
|
clearValue.color = vk::ClearColorValue(
|
||||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 0.0f});
|
std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f});
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo colorAttachments[2];
|
vk::RenderingAttachmentInfo colorAttachments[2];
|
||||||
// HDR 附件由 Skybox pass 清屏并画背景,这里 Load 在其上合成几何
|
// HDR 附件由 Skybox pass 清屏并画背景,这里 Load 在其上合成几何
|
||||||
@@ -197,9 +210,8 @@ namespace Engine::Passes
|
|||||||
vk::ShaderStageFlagBits::eFragment);
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
|
||||||
// ---- 场景色副本:不透明段(含天空盒背景)的快照 ----
|
// ---- 场景色副本:不透明段(含天空盒背景)的快照 ----
|
||||||
// 透射折射的采样源。必须排在 Forward 之后(RAW 链)、
|
// 第 0 层折射的采样源。必须排在 Forward 之后(RAW 链)、透明层
|
||||||
// ForwardTrans 之前(后者对 HDRColor 的写入不能进折射样本,
|
// 之前。后续层由 "SceneColorCopyL*" 逐层刷新(含前序透明体)。
|
||||||
// 且玻璃互相不可见——屏幕空间透射只含不透明场景,与 UE 一致)。
|
|
||||||
rg.AddPass("SceneColorCopy", [this](RG::RenderGraphContext& ctx) {
|
rg.AddPass("SceneColorCopy", [this](RG::RenderGraphContext& ctx) {
|
||||||
const vk::CommandBuffer cmd = ctx.cmd;
|
const vk::CommandBuffer cmd = ctx.cmd;
|
||||||
|
|
||||||
@@ -220,22 +232,89 @@ namespace Engine::Passes
|
|||||||
.Read(m_HDRColor, RG::ResourceUse::TransferSrc)
|
.Read(m_HDRColor, RG::ResourceUse::TransferSrc)
|
||||||
.Write(m_SceneColor, RG::ResourceUse::TransferDst);
|
.Write(m_SceneColor, RG::ResourceUse::TransferDst);
|
||||||
|
|
||||||
// ---- 透明段:独立渲染实例,UE/Unity 标准语义 ----
|
// ---- 透明段:分层渲染实例(玻璃见玻璃)----
|
||||||
// 深度测试对不透明深度(eLess)+ 深度写入关闭——玻璃之间
|
// 深度测试对不透明深度(eLess)+ 深度写入关闭——玻璃之间互相
|
||||||
// 互相不遮挡(谁先画都留在画面上,先后只影响 over 混合的
|
// 不遮挡(先后只影响 over 混合/折射采样的颜色数学),交叉半透明
|
||||||
// 颜色数学),交叉半透明不再整块消失。
|
// 不再整块消失。对不透明深度的 RAW 由 depth 的 Write use 链保证
|
||||||
// 对不透明深度的 RAW 由 depth 的 Write use 链保证排在
|
// 排在不透明段之后(WAW 边)。
|
||||||
// 不透明段之后(WAW 边)。
|
// alpha 语义:frag 输出未预乘线性色 + 覆盖率 alpha,预乘在 tonemap
|
||||||
// alpha 语义:frag 输出未预乘线性色 + 覆盖率 alpha,
|
// 段(rgb * a)完成 —— hard constraint。
|
||||||
// 预乘在 tonemap 段(rgb * a)完成 —— hard constraint。
|
//
|
||||||
rg.AddPass("ForwardTrans", [this](RG::RenderGraphContext& ctx) {
|
// 分层折射:透明队列按远→近(SceneRenderer 排序)。每个绘制单位
|
||||||
if (m_Frame.translucentView.batches.empty()) return;
|
// 占一层,层 L 绘制第 L 个单位;绘制前把 HDRColor 刷新进 SceneColor
|
||||||
|
// 副本,使层 L 的玻璃折射采样包含所有更远的透明体(含更远玻璃),
|
||||||
|
// 而不是只有不透明段快照。上限 kTransparentLayers(见文件头
|
||||||
|
// 匿名 namespace 注释),超出层数的单位并入最后一层。
|
||||||
|
for (u32 layer = 0; layer < kTransparentLayers; ++layer) {
|
||||||
|
// 层 L 的折射采样源刷新(层 0 用不透明段快照,无需再拷)
|
||||||
|
if (layer > 0) {
|
||||||
|
rg.AddPass("SceneColorCopyL" + std::to_string(layer),
|
||||||
|
[this, layer](RG::RenderGraphContext& ctx) {
|
||||||
|
// 本层无绘制单位:既无新内容可拷,也无玻璃会采样
|
||||||
|
if (TransparentUnitCount() <= layer) return;
|
||||||
|
|
||||||
const vk::CommandBuffer cmd = ctx.cmd;
|
const vk::CommandBuffer cmd = ctx.cmd;
|
||||||
|
vk::ImageCopy region{};
|
||||||
|
region.srcSubresource =
|
||||||
|
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor,
|
||||||
|
0, 0, 1);
|
||||||
|
region.dstSubresource =
|
||||||
|
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor,
|
||||||
|
0, 0, 1);
|
||||||
|
region.setExtent(vk::Extent3D(ctx.extent.width,
|
||||||
|
ctx.extent.height, 1));
|
||||||
|
cmd.copyImage(ctx.Image(m_HDRColor),
|
||||||
|
vk::ImageLayout::eTransferSrcOptimal,
|
||||||
|
ctx.Image(m_SceneColor),
|
||||||
|
vk::ImageLayout::eTransferDstOptimal,
|
||||||
|
{region});
|
||||||
|
})
|
||||||
|
.Read(m_HDRColor, RG::ResourceUse::TransferSrc)
|
||||||
|
.Write(m_SceneColor, RG::ResourceUse::TransferDst);
|
||||||
|
}
|
||||||
|
|
||||||
|
rg.AddPass("ForwardTransL" + std::to_string(layer),
|
||||||
|
[this, layer](RG::RenderGraphContext& ctx) {
|
||||||
|
const u32 total = TransparentUnitCount();
|
||||||
|
if (total <= layer) return; // 本层无单位
|
||||||
|
// 末层兜底:消化剩余全部单位;其余层每层一个单位
|
||||||
|
const u32 end =
|
||||||
|
(layer + 1 < kTransparentLayers) ? layer + 1 : total;
|
||||||
|
DrawTransparentRange(ctx, ctx.cmd, layer, end);
|
||||||
|
})
|
||||||
|
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||||
|
.Write(m_Velocity, RG::ResourceUse::ColorAttachment)
|
||||||
|
// depth 走写 use(WAW 边):保证透明段排在不透明段之后,
|
||||||
|
// barrier 覆盖透明段对不透明深度的测试读
|
||||||
|
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
||||||
|
// 场景色副本:透射折射采样源(translucent frag set2 binding 6)
|
||||||
|
.Read(m_SceneColor, RG::ResourceUse::ShaderRead,
|
||||||
|
vk::ShaderStageFlagBits::eFragment)
|
||||||
|
.Read(m_ShadowMaps[0], RG::ResourceUse::ShaderRead,
|
||||||
|
vk::ShaderStageFlagBits::eFragment)
|
||||||
|
.Read(m_ShadowMaps[1], RG::ResourceUse::ShaderRead,
|
||||||
|
vk::ShaderStageFlagBits::eFragment)
|
||||||
|
.Read(m_ShadowMaps[2], RG::ResourceUse::ShaderRead,
|
||||||
|
vk::ShaderStageFlagBits::eFragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ForwardPass::TransparentUnitCount() const
|
||||||
|
{
|
||||||
|
u32 n = 0;
|
||||||
|
for (const auto& b : m_Frame.translucentView.batches) n += b.count;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ForwardPass::DrawTransparentRange(const RG::RenderGraphContext& ctx,
|
||||||
|
vk::CommandBuffer cmd,
|
||||||
|
u32 start, u32 end) const
|
||||||
|
{
|
||||||
|
// HDR 颜色 + velocity 均 Load 承接(透明段叠加在前序内容上);
|
||||||
|
// 这两项的 clearValue 在 eLoad 下不生效,仅为结构完整
|
||||||
vk::ClearValue clearValue;
|
vk::ClearValue clearValue;
|
||||||
clearValue.color = vk::ClearColorValue(
|
clearValue.color = vk::ClearColorValue(
|
||||||
std::array<float, 4>{0.1f, 0.1f, 0.1f, 0.0f});
|
std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f});
|
||||||
|
|
||||||
vk::RenderingAttachmentInfo colorAttachments[2];
|
vk::RenderingAttachmentInfo colorAttachments[2];
|
||||||
colorAttachments[0].setImageView(ctx.View(m_HDRColor))
|
colorAttachments[0].setImageView(ctx.View(m_HDRColor))
|
||||||
@@ -244,23 +323,23 @@ namespace Engine::Passes
|
|||||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
.setClearValue(clearValue);
|
.setClearValue(clearValue);
|
||||||
|
|
||||||
// velocity:Load 不透明段写出的运动向量,玻璃只覆盖自己的像素
|
|
||||||
vk::ClearValue velocityClear;
|
vk::ClearValue velocityClear;
|
||||||
velocityClear.color = vk::ClearColorValue(std::array<float, 4>{0.f, 0.f, 0.f, 0.f});
|
velocityClear.color = vk::ClearColorValue(
|
||||||
|
std::array<float, 4>{0.f, 0.f, 0.f, 0.f});
|
||||||
colorAttachments[1].setImageView(ctx.View(m_Velocity))
|
colorAttachments[1].setImageView(ctx.View(m_Velocity))
|
||||||
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
.setImageLayout(vk::ImageLayout::eColorAttachmentOptimal)
|
||||||
.setLoadOp(vk::AttachmentLoadOp::eLoad)
|
.setLoadOp(vk::AttachmentLoadOp::eLoad)
|
||||||
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
.setClearValue(velocityClear);
|
.setClearValue(velocityClear);
|
||||||
|
|
||||||
|
// 深度只读测试源:Load 承接不透明段深度,depthWrite=FALSE 不产生
|
||||||
|
// 写入。storeOp 必须 eStore——透明段已拆成多层,后续层的 Load 依赖
|
||||||
|
// 本层写回的有效深度内容(DontCare 会让内容在层间变 undefined)。
|
||||||
vk::RenderingAttachmentInfo depthAttachment;
|
vk::RenderingAttachmentInfo depthAttachment;
|
||||||
depthAttachment.setImageView(ctx.View(m_Depth))
|
depthAttachment.setImageView(ctx.View(m_Depth))
|
||||||
// 深度只读测试源:Load 承接不透明段深度,
|
|
||||||
// 透明段 depthWrite=FALSE 不产生写入,
|
|
||||||
// storeOp DontCare(下帧 entry 重新 Clear)
|
|
||||||
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
.setImageLayout(vk::ImageLayout::eDepthAttachmentOptimal)
|
||||||
.setLoadOp(vk::AttachmentLoadOp::eLoad)
|
.setLoadOp(vk::AttachmentLoadOp::eLoad)
|
||||||
.setStoreOp(vk::AttachmentStoreOp::eDontCare)
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
||||||
.setClearValue(vk::ClearValue(
|
.setClearValue(vk::ClearValue(
|
||||||
vk::ClearDepthStencilValue(1.0f, 0)));
|
vk::ClearDepthStencilValue(1.0f, 0)));
|
||||||
|
|
||||||
@@ -277,13 +356,9 @@ namespace Engine::Passes
|
|||||||
RecordSharedState(ctx, cmd, true);
|
RecordSharedState(ctx, cmd, true);
|
||||||
// UE/Unity 语义:透明段只测不写深度。写深度会让"先画的玻璃"
|
// UE/Unity 语义:透明段只测不写深度。写深度会让"先画的玻璃"
|
||||||
// 剔掉后画但实际在它前面的玻璃(交叉半透明整块消失)。
|
// 剔掉后画但实际在它前面的玻璃(交叉半透明整块消失)。
|
||||||
// 早期 depthWrite=FALSE 引发 NVIDIA ~5fps 病态的实验是在
|
|
||||||
// frag 仍写 gl_FragDepth 的时期做的(禁 early-Z),归因存疑;
|
|
||||||
// gl_FragDepth 已删除,FALSE 若复现病态再另行隔离。
|
|
||||||
cmd.setDepthWriteEnable(VK_FALSE);
|
cmd.setDepthWriteEnable(VK_FALSE);
|
||||||
// 单面玻璃:透明段剔除背面。不透明段 cullNone + depthWrite
|
// 单面玻璃:透明段剔除背面。透明段 cullNone 会让闭合体(cube)
|
||||||
// 无所谓(深度裁决),透明段 cullNone 会让闭合体(cube)的
|
// 的正反面片按 mesh 顺序乱序 over 混合 = "深度错乱";cull back
|
||||||
// 正反面片按 mesh 顺序乱序 over 混合 = "深度错乱";cull back
|
|
||||||
// 每像素至多一面。双面透明(树叶卡片)将来加材质开关
|
// 每像素至多一面。双面透明(树叶卡片)将来加材质开关
|
||||||
cmd.setCullMode(vk::CullModeFlagBits::eBack);
|
cmd.setCullMode(vk::CullModeFlagBits::eBack);
|
||||||
|
|
||||||
@@ -291,11 +366,9 @@ namespace Engine::Passes
|
|||||||
premul.setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
|
premul.setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
|
||||||
.setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
|
.setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
|
||||||
.setColorBlendOp(vk::BlendOp::eAdd)
|
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||||
// alpha 穿透:玻璃只做场景内 rgb over(透出背景色),
|
// alpha 穿透:玻璃只做场景内 rgb over(透出背景色),不修改
|
||||||
// 不修改像素覆盖率——HDR 的 a 是"窗口透过率"(tonemap
|
// 像素覆盖率——HDR 的 a 是"窗口透过率"(tonemap rgb *= a 预乘
|
||||||
// rgb *= a 预乘 + 透明窗口桌面合成),归背景专属。若让
|
// + 透明窗口桌面合成),归背景专属
|
||||||
// 玻璃 over 混入 a(旧写法),a<1 背景上的玻璃会被拖进
|
|
||||||
// 桌面混色链路 → 预乘拉黑
|
|
||||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eZero)
|
.setSrcAlphaBlendFactor(vk::BlendFactor::eZero)
|
||||||
.setDstAlphaBlendFactor(vk::BlendFactor::eOne)
|
.setDstAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||||
@@ -308,30 +381,24 @@ namespace Engine::Passes
|
|||||||
cmd.setColorWriteMaskEXT(0,
|
cmd.setColorWriteMaskEXT(0,
|
||||||
std::array<vk::ColorComponentFlags, 2>{kColorWriteMask, kColorWriteMask});
|
std::array<vk::ColorComponentFlags, 2>{kColorWriteMask, kColorWriteMask});
|
||||||
|
|
||||||
|
// 绘制单位区间 [start,end):批次内连续实例合并为一次 draw
|
||||||
|
u32 consumed = 0;
|
||||||
for (const auto& batch : m_Frame.translucentView.batches) {
|
for (const auto& batch : m_Frame.translucentView.batches) {
|
||||||
|
const u32 bStart = consumed;
|
||||||
|
const u32 bEnd = consumed + batch.count;
|
||||||
|
const u32 s = std::max(start, bStart);
|
||||||
|
const u32 e = std::min(end, bEnd);
|
||||||
|
consumed = bEnd;
|
||||||
|
if (s < e) {
|
||||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||||
m_Gpu.forwardLayout, 1,
|
m_Gpu.forwardLayout, 1,
|
||||||
batch.material->GetDescriptorSet(), {});
|
batch.material->GetDescriptorSet(), {});
|
||||||
batch.mesh->Draw(cmd, batch.count, batch.first, batch.lod);
|
batch.mesh->Draw(cmd, e - s, batch.first + (s - bStart), batch.lod);
|
||||||
|
}
|
||||||
|
if (consumed >= end) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.endRendering();
|
cmd.endRendering();
|
||||||
})
|
|
||||||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
|
||||||
.Write(m_Velocity, RG::ResourceUse::ColorAttachment)
|
|
||||||
// depth 走写 use(WAW 边):保证透明段排在不透明段之后,
|
|
||||||
// barrier 覆盖透明段对不透明深度的测试读(Read-Write hazard
|
|
||||||
// 由 src=Write 的内存域保证可见性)
|
|
||||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment)
|
|
||||||
// 场景色副本:透射折射采样源(translucent frag set2 binding 6)
|
|
||||||
.Read(m_SceneColor, RG::ResourceUse::ShaderRead,
|
|
||||||
vk::ShaderStageFlagBits::eFragment)
|
|
||||||
.Read(m_ShadowMaps[0], RG::ResourceUse::ShaderRead,
|
|
||||||
vk::ShaderStageFlagBits::eFragment)
|
|
||||||
.Read(m_ShadowMaps[1], RG::ResourceUse::ShaderRead,
|
|
||||||
vk::ShaderStageFlagBits::eFragment)
|
|
||||||
.Read(m_ShadowMaps[2], RG::ResourceUse::ShaderRead,
|
|
||||||
vk::ShaderStageFlagBits::eFragment);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
void ForwardPass::OnTargetsRebuilt(RG::RenderGraph& rg)
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ namespace Engine::Passes
|
|||||||
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
cmd.setAlphaToOneEnableEXT(VK_FALSE);
|
||||||
|
|
||||||
// ---- 颜色混合必须显式直写 ----
|
// ---- 颜色混合必须显式直写 ----
|
||||||
// shader-object 路径混合是动态状态,会从上一个 pass(ForwardTrans
|
// shader-object 路径混合是动态状态,会从上一个 pass(ForwardTransL*
|
||||||
// 的 premul 混合)残留:tonemap 输出被二次混合 → rgb = color×a²
|
// 的 premul 混合)残留:tonemap 输出被二次混合 → rgb = color×a²
|
||||||
//(拖低 alpha 平方级变黑),且 alpha 被残留方程与 clear(a=1) 混成
|
//(拖低 alpha 平方级变黑),且 alpha 被残留方程与 clear(a=1) 混成
|
||||||
// 恒 1 → 透明窗口永远不生效
|
// 恒 1 → 透明窗口永远不生效
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ namespace Engine::Passes
|
|||||||
|
|
||||||
cmd.endRendering();
|
cmd.endRendering();
|
||||||
})
|
})
|
||||||
// Write use(WAW 链):保证排在 Forward/ForwardTrans 之后、
|
// Write use(WAW 链):保证排在 Forward/ForwardTransL* 之后、
|
||||||
// TAA(Read HDRColor)之前——调试图元进入 TAA/Bloom/Tonemap 主链路
|
// TAA(Read HDRColor)之前——调试图元进入 TAA/Bloom/Tonemap 主链路
|
||||||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||||
.Write(m_Depth, RG::ResourceUse::DepthAttachment);
|
.Write(m_Depth, RG::ResourceUse::DepthAttachment);
|
||||||
|
|||||||
@@ -426,9 +426,10 @@ namespace Engine
|
|||||||
"HDRColor",
|
"HDRColor",
|
||||||
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
||||||
|
|
||||||
// 场景色副本:不透明段快照(含天空盒背景),透射折射采样源。
|
// 场景色副本:透射折射采样源。ForwardPass 分层折射逐层刷新——
|
||||||
// 每帧一次 copyImage(Forward 后、ForwardTrans 前),独立于
|
// 不透明段快照一次(SceneColorCopy),此后每透明层绘制前各拷一次
|
||||||
// HDRColor 生命周期(后者还会被透明段/TAA 继续写入)
|
// (SceneColorCopyL*),使前层玻璃折射能采样到更远透明体
|
||||||
|
//(玻璃见玻璃)。独立于 HDRColor 生命周期(后者仍被 TAA 继续写)
|
||||||
m_SceneColor = m_RenderGraph->CreateTexture(
|
m_SceneColor = m_RenderGraph->CreateTexture(
|
||||||
"SceneColor",
|
"SceneColor",
|
||||||
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
RG::TextureDesc{vk::Format::eR16G16B16A16Sfloat});
|
||||||
@@ -521,7 +522,7 @@ namespace Engine
|
|||||||
frame, m_HDRColor, m_Depth, *m_GizmoIconVB, *m_GizmoWireVB));
|
frame, m_HDRColor, m_Depth, *m_GizmoIconVB, *m_GizmoWireVB));
|
||||||
#endif
|
#endif
|
||||||
// UE 式 GPU 拾取:请求帧把 id 画进 R32Uint + 拷贝请求像素进
|
// UE 式 GPU 拾取:请求帧把 id 画进 R32Uint + 拷贝请求像素进
|
||||||
// host 暂存(深度 WAW 链尾,排在 Forward/ForwardTrans/PhysicsDebug
|
// host 暂存(深度 WAW 链尾,排在 Forward/ForwardTransL*/PhysicsDebug
|
||||||
// 之后;App 层经 RequestPick/TakePickResult 消费)
|
// 之后;App 层经 RequestPick/TakePickResult 消费)
|
||||||
m_Passes.push_back(CreateScope<Passes::HitProxyPass>(
|
m_Passes.push_back(CreateScope<Passes::HitProxyPass>(
|
||||||
*m_Device, *m_Allocator, *m_ShaderManager, *m_DescriptorManager,
|
*m_Device, *m_Allocator, *m_ShaderManager, *m_DescriptorManager,
|
||||||
|
|||||||
@@ -810,6 +810,15 @@ namespace Engine
|
|||||||
// 阈值 10/20 倍半径,可调)。开关关闭 = 恒 LOD0
|
// 阈值 10/20 倍半径,可调)。开关关闭 = 恒 LOD0
|
||||||
const f32 dist =
|
const f32 dist =
|
||||||
glm::length(worldCenter - camTransform->GetWorldPosition());
|
glm::length(worldCenter - camTransform->GetWorldPosition());
|
||||||
|
|
||||||
|
// 透明排序键:视图空间深度 = -(view·p).z(沿相机前向
|
||||||
|
// 的投影深度)。绝不能用欧氏距离——侧向偏移会被计入
|
||||||
|
// "距离",使偏侧物体被误判为更远("正方体右上的甜甜圈
|
||||||
|
// 在特定视角被排到后方"的根因)。投影深度只取沿视线
|
||||||
|
// 分量,侧向偏移不干扰序
|
||||||
|
const f32 viewDepth =
|
||||||
|
-(ubo.view * glm::vec4(worldCenter, 1.0f)).z;
|
||||||
|
|
||||||
u32 mainLod = 0;
|
u32 mainLod = 0;
|
||||||
if (m_LodEnabled) {
|
if (m_LodEnabled) {
|
||||||
if (dist >= worldRadius * 20.0f) {
|
if (dist >= worldRadius * 20.0f) {
|
||||||
@@ -828,6 +837,23 @@ namespace Engine
|
|||||||
instanceTruncated = true;
|
instanceTruncated = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// 透明队列逐实例成批(不合并):透明段是"每层一个
|
||||||
|
// 绘制单位"(ForwardPass kTransparentLayers 分层),
|
||||||
|
// 层序 = 混合/折射序,必须按实例深度逐实例排。若合并
|
||||||
|
// 成多实例 batch,排序只能以批平均深度参与 → 批内
|
||||||
|
// 实例无法各自定序,层序错。不透明/阴影视图合并
|
||||||
|
//(批内顺序无混合语义,只为少绑状态)
|
||||||
|
const bool perInstance = (&s == &m_TranslucentScratch);
|
||||||
|
u32 bi;
|
||||||
|
if (perInstance) {
|
||||||
|
bi = static_cast<u32>(s.result.batches.size());
|
||||||
|
// first 是占位:真实基址在 appendView 聚合时回填
|
||||||
|
s.result.batches.push_back(
|
||||||
|
{mesh.get(), resolved, 0, 0, lod});
|
||||||
|
s.batchDistSum.push_back(0.0f);
|
||||||
|
s.batchHead.push_back(ViewScratch::kNoInstance);
|
||||||
|
}
|
||||||
|
else {
|
||||||
// batch 查找:内建材质 = 数组寻址快路径
|
// batch 查找:内建材质 = 数组寻址快路径
|
||||||
//(Mesh::GetSlot 槽位 + LOD 槽);覆盖材质 =
|
//(Mesh::GetSlot 槽位 + LOD 槽);覆盖材质 =
|
||||||
// hash 寻址(键含材质变体:同 mesh 不同覆盖天然
|
// hash 寻址(键含材质变体:同 mesh 不同覆盖天然
|
||||||
@@ -854,18 +880,18 @@ namespace Engine
|
|||||||
// batchIdx 存储 +1 偏移(0 = 该槽位尚无 batch)
|
// batchIdx 存储 +1 偏移(0 = 该槽位尚无 batch)
|
||||||
u32 batchIdx = (*lodSlots)[lod];
|
u32 batchIdx = (*lodSlots)[lod];
|
||||||
if (batchIdx == 0) {
|
if (batchIdx == 0) {
|
||||||
batchIdx = static_cast<u32>(s.result.batches.size()) + 1;
|
batchIdx =
|
||||||
|
static_cast<u32>(s.result.batches.size()) + 1;
|
||||||
(*lodSlots)[lod] = batchIdx;
|
(*lodSlots)[lod] = batchIdx;
|
||||||
// first 是占位:真实基址在 appendView 聚合时
|
|
||||||
// 回填(收集序下 batch 实例散布,见链表注释)
|
|
||||||
s.result.batches.push_back(
|
s.result.batches.push_back(
|
||||||
{mesh.get(), resolved, 0, 0, lod});
|
{mesh.get(), resolved, 0, 0, lod});
|
||||||
s.batchDistSum.push_back(0.0f);
|
s.batchDistSum.push_back(0.0f);
|
||||||
s.batchHead.push_back(ViewScratch::kNoInstance);
|
s.batchHead.push_back(ViewScratch::kNoInstance);
|
||||||
}
|
}
|
||||||
// 实例归入自己的 batch(mesh 分组):收集序散布,
|
bi = batchIdx - 1;
|
||||||
// 组内连接用链表维护(O(1) push,无 per-batch 分配)
|
}
|
||||||
const u32 bi = batchIdx - 1;
|
// 实例归入自己的 batch(收集序散布,组内连接用链表
|
||||||
|
// 维护:O(1) push,无 per-batch 分配)
|
||||||
s.result.batches[bi].count++;
|
s.result.batches[bi].count++;
|
||||||
const u32 ii =
|
const u32 ii =
|
||||||
static_cast<u32>(s.instances.size());
|
static_cast<u32>(s.instances.size());
|
||||||
@@ -874,7 +900,7 @@ namespace Engine
|
|||||||
s.instanceNext.push_back(s.batchHead[bi]);
|
s.instanceNext.push_back(s.batchHead[bi]);
|
||||||
s.batchHead[bi] = ii; // 头插:链序 = push 逆序(无妨,
|
s.batchHead[bi] = ii; // 头插:链序 = push 逆序(无妨,
|
||||||
// 实例对齐性由 (矩阵,id) 成对保证)
|
// 实例对齐性由 (矩阵,id) 成对保证)
|
||||||
s.batchDistSum[bi] += dist; // 透明排序用
|
s.batchDistSum[bi] += viewDepth; // 透明排序键:视图深度
|
||||||
s.result.instanceCount++;
|
s.result.instanceCount++;
|
||||||
++totalInstances;
|
++totalInstances;
|
||||||
};
|
};
|
||||||
@@ -939,9 +965,11 @@ namespace Engine
|
|||||||
// PackIndirect(m_Frame.shadowCascades[c], false);
|
// PackIndirect(m_Frame.shadowCascades[c], false);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排。
|
// ---- 5. 透明批次排序:视图深度降序(远 → 近),连同实例列表同步重排。
|
||||||
// 实例按链表逐 batch 搬运(收集序散布,不能按 (first,count) 切片);
|
// 透明队列逐实例成批(见 tryAppend),故 count=1、batchDistSum = 该
|
||||||
// 搬完的新数组天然按 batch 连续 → 链表确定性重建(顺序遍历)
|
// 实例的视图深度 → 批序 = 逐实例深度序。实例按链表逐 batch 搬运
|
||||||
|
//(收集序散布,不能按 (first,count) 切片);搬完的新数组天然按
|
||||||
|
// batch 连续 → 链表确定性重建(顺序遍历)
|
||||||
{
|
{
|
||||||
auto& tb = m_TranslucentScratch.result.batches;
|
auto& tb = m_TranslucentScratch.result.batches;
|
||||||
if (tb.size() > 1) {
|
if (tb.size() > 1) {
|
||||||
|
|||||||
@@ -268,9 +268,10 @@ void main() {
|
|||||||
* mix(0.4, 1.0, shade0); // 阴影区环境同步衰减(简化遮蔽)
|
* mix(0.4, 1.0, shade0); // 阴影区环境同步衰减(简化遮蔽)
|
||||||
|
|
||||||
// ---- 透射(KHR_materials_transmission):屏幕空间折射 ----
|
// ---- 透射(KHR_materials_transmission):屏幕空间折射 ----
|
||||||
// 采样源 = 不透明段快照(天空盒 + 不透明几何,Forward 后
|
// 采样源 = 场景色副本,每层绘制前逐层刷新(ForwardPass 的分层折射:
|
||||||
// ForwardTrans 前拷贝)。玻璃互相不可见(副本不含透明队列),
|
// 透明队列远→近拆成至多 kTransparentLayers 层,层 L 绘制前把 HDRColor
|
||||||
// 与 UE 屏幕空间透射同一限制。
|
// 拷进副本)。故层 L 玻璃的副本含全部更远透明体——前玻璃能折射出
|
||||||
|
// 后玻璃(玻璃见玻璃),摆脱 UE/Unity 单快照"玻璃互相不可见"的局限。
|
||||||
vec3 transmitted = vec3(0.0);
|
vec3 transmitted = vec3(0.0);
|
||||||
if (transmission > 0.0) {
|
if (transmission > 0.0) {
|
||||||
// 进入介质 eta = 1/ior;全内反射(TIR)退化为反射方向
|
// 进入介质 eta = 1/ior;全内反射(TIR)退化为反射方向
|
||||||
|
|||||||
Reference in New Issue
Block a user