feat: 透明材质 alpha blending——独立队列 + 线性 over 混合 + 距离排序
透明来源:glTF alphaMode=BLEND(解析期读取,OBJ/FBX 恒不透明—— 资产格式定义写死);MASK 后续演进。Material 加透明语义(入缓存键), 程序化测试资产 CreateTransparentPlane(RGBA 纯色纹理)。 SceneRenderer:透明材质实例收集进独立 scratch(仅主视图,不投 阴影——alpha 阴影后续演进),batch 按平均距离降序排序(远→近, batchMats 同步重排),SSBO 扁平化顺序 = 排序后顺序。 ForwardPass 两段绘制:不透明(无混合 + 深度写)→ 透明(Shader Object 动态状态切 SRC_ALPHA/ONE_MINUS_SRC_ALPHA 线性 over + 深度 只测不写);velocity 附件透明段直写(透明表面 velocity 即自身运动)。 frag alpha = albedo 贴图 a 直通,预乘在 tonemap 段(hard constraint 不变)。已知边界:TAA alpha 时域混合(透明边缘几帧渐显);同 batch 多实例不做实例级排序。
This commit is contained in:
@@ -157,6 +157,9 @@ namespace Engine
|
||||
|
||||
// 程序化平面(纯色贴图):size 为半边长。带缓存,同参数共享一份。
|
||||
ModelRef CreatePlane(f32 halfSize, u8 r, u8 g, u8 b);
|
||||
// 半透明平面(alpha 0-255,透明绘制队列):同参数缓存去重
|
||||
ModelRef CreateTransparentPlane(f32 halfSize,
|
||||
u8 r, u8 g, u8 b, u8 a);
|
||||
|
||||
// 环境资产(HDR → IBL 预积分四件套)。路径缓存去重,
|
||||
// 重复 Load 同一路径零成本(动态切换场景直接反复调用)
|
||||
|
||||
@@ -37,6 +37,10 @@ namespace Engine::Assets {
|
||||
// 程序化纯色平面(size 为半边长)
|
||||
virtual Scope<Model> CreatePlane(f32 halfSize, u8 r, u8 g, u8 b) = 0;
|
||||
|
||||
// 半透明纯色平面(alpha 0-255,透明绘制队列)
|
||||
virtual Scope<Model> CreateTransparentPlane(f32 halfSize,
|
||||
u8 r, u8 g, u8 b, u8 a) = 0;
|
||||
|
||||
// 环境 HDR → IBL 预积分四件套(compute 生成,毫秒级;失败返回 nullptr)
|
||||
virtual Scope<Cubemap> LoadEnvironment(const std::string& path) = 0;
|
||||
|
||||
|
||||
@@ -55,7 +55,8 @@ namespace Engine {
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout layout,
|
||||
const std::array<Texture*, kSlotCount>& textures,
|
||||
const MaterialParams& params);
|
||||
const MaterialParams& params,
|
||||
bool transparent = false);
|
||||
~Material();
|
||||
|
||||
Material(const Material&) = delete;
|
||||
@@ -63,15 +64,19 @@ namespace Engine {
|
||||
|
||||
vk::DescriptorSet GetDescriptorSet() const { return m_DescriptorSet; }
|
||||
Texture* GetTexture(Slot slot) const { return m_Textures[slot]; }
|
||||
// 透明队列归属(glTF alphaMode=BLEND;CPU 收集侧用)
|
||||
bool IsTransparent() const { return m_Transparent; }
|
||||
|
||||
// 材质缓存键:同贴图组合+同因子共享一份(Model 层使用)
|
||||
// 材质缓存键:同贴图组合+同因子+同透明语义共享一份(Model 层使用)
|
||||
static std::string MakeCacheKey(const std::string paths[kSlotCount],
|
||||
const MaterialParams& params);
|
||||
const MaterialParams& params,
|
||||
bool transparent);
|
||||
|
||||
private:
|
||||
std::array<Texture*, kSlotCount> m_Textures{};
|
||||
vk::DescriptorSet m_DescriptorSet;
|
||||
Scope<UniformBuffer> m_ParamsBuffer; // per-slot 参数 ring(创建时写死)
|
||||
bool m_Transparent = false;
|
||||
};
|
||||
} // namespace Engine
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Engine
|
||||
std::vector<std::vector<u32>> lods;
|
||||
std::array<std::string, Material::kSlotCount> texPaths;
|
||||
MaterialParams params;
|
||||
bool transparent = false; // glTF alphaMode=BLEND(透明绘制队列)
|
||||
};
|
||||
struct ParsedModel {
|
||||
std::string path;
|
||||
@@ -74,6 +75,12 @@ namespace Engine
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
f32 halfSize, u8 r, u8 g, u8 b);
|
||||
// 半透明纯色平面(alpha 0-255,预乘 alpha 链):透明管线验证用
|
||||
static Scope<Model> CreateTransparentPlane(
|
||||
Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
f32 halfSize, u8 r, u8 g, u8 b, u8 a);
|
||||
|
||||
~Model();
|
||||
|
||||
@@ -89,7 +96,8 @@ namespace Engine
|
||||
Model(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
ProceduralTag, f32 halfSize, const std::array<u8, 3>& color);
|
||||
ProceduralTag, f32 halfSize, const std::array<u8, 4>& rgba,
|
||||
bool transparent);
|
||||
|
||||
// 空模型骨架(FromParsed 用):只建默认纹理,mesh 由调用方填
|
||||
Model(Device& device, MemoryAllocator& allocator,
|
||||
@@ -111,11 +119,12 @@ namespace Engine
|
||||
|
||||
Texture* GetOrLoadTexture(const std::string& path, bool srgb);
|
||||
|
||||
// 材质缓存入口:同贴图组合+因子共享同一 Material(一个描述符集),
|
||||
// 材质缓存入口:同贴图组合+同因子+同透明语义共享一份 Material,
|
||||
// 槽位路径为空/文件缺失折到对应默认纹理。路径已做反斜杠归一化。
|
||||
// paths 下标 = Material::Slot;params = metallic/roughness 因子
|
||||
Material* GetOrCreateMaterial(const std::string paths[Material::kSlotCount],
|
||||
const MaterialParams& params);
|
||||
const MaterialParams& params,
|
||||
bool transparent = false);
|
||||
|
||||
Device& m_Device;
|
||||
MemoryAllocator& m_Allocator;
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace Engine {
|
||||
// firstInstance + 连续区间,实体序收集不满足(多实体共享 mesh
|
||||
// 时实例分散各处),扁平化成连续数组推迟到 appendView
|
||||
std::vector<std::vector<InstanceMatrices>> batchMats;
|
||||
// 每 batch 实例距离累加(透明排序用:平均距离降序 = 远→近)
|
||||
std::vector<f32> batchDistSum;
|
||||
// batch 查找:数组寻址(下标 = Mesh::GetSlot(),值 = 每 LOD 的
|
||||
// batchIdx+1,0 = 未用)——逐实例 hash 在大场景是毫秒级开销
|
||||
std::vector<std::array<u32, kMeshLodCount>> lookup;
|
||||
@@ -89,11 +91,13 @@ namespace Engine {
|
||||
result.batches.clear();
|
||||
result.instanceCount = 0;
|
||||
batchMats.clear();
|
||||
batchDistSum.clear();
|
||||
for (auto& e : lookup) e.fill(0); // 容量保留,置零复用
|
||||
}
|
||||
};
|
||||
ViewScratch m_MainScratch;
|
||||
ViewScratch m_CascScratch[kMaxCascades];
|
||||
ViewScratch m_TranslucentScratch; // 透明材质(仅主视图,不投阴影)
|
||||
|
||||
// ---- TAA 时域状态 ----
|
||||
u64 m_FrameIndex = 0; // 抖动序列游标(Halton 2,3 ×8 采样)
|
||||
|
||||
@@ -91,6 +91,9 @@ namespace Engine {
|
||||
// 每级联独立剔除结果(Shadow pass 按 region 各画各的——
|
||||
// 级联盒沿光方向加了 caster pad,影子不会被剔除裁掉)
|
||||
CullResult shadowCascades[kMaxCascades];
|
||||
// 透明批次(预乘 alpha 混合,深度只测不写,远→近排序);
|
||||
// 不参与阴影投射
|
||||
CullResult translucentView;
|
||||
|
||||
// ---- 运行时开关/调试视图(ImGui 经 Renderer 门面写入,
|
||||
// SceneRenderer 唯一落帧,各 pass 只读;每帧确定性)----
|
||||
|
||||
@@ -35,6 +35,8 @@ namespace Engine {
|
||||
Scope<Model> LoadModel(const std::string& path,
|
||||
ParsedModel&& parsed) override;
|
||||
Scope<Model> CreatePlane(f32 halfSize, u8 r, u8 g, u8 b) override;
|
||||
Scope<Model> CreateTransparentPlane(f32 halfSize,
|
||||
u8 r, u8 g, u8 b, u8 a) override;
|
||||
Scope<Cubemap> LoadEnvironment(const std::string& path) override;
|
||||
Scope<Cubemap> LoadEnvironment(const std::string& path,
|
||||
DecodedEquirect&& decoded) override;
|
||||
|
||||
@@ -31,6 +31,9 @@ namespace Engine {
|
||||
struct ProceduralTag {};
|
||||
static Scope<Texture> CreateSolidColor(Device& device, MemoryAllocator& allocator,
|
||||
u8 r, u8 g, u8 b, bool srgb = true);
|
||||
// 带 alpha 的纯色纹理(透明管线验证等)
|
||||
static Scope<Texture> CreateSolidColorRGBA(Device& device, MemoryAllocator& allocator,
|
||||
u8 r, u8 g, u8 b, u8 a, bool srgb = true);
|
||||
|
||||
~Texture();
|
||||
|
||||
|
||||
@@ -41,6 +41,18 @@ namespace Engine::Assets {
|
||||
[&](const std::string&) { return m_Uploader.CreatePlane(halfSize, r, g, b); });
|
||||
}
|
||||
|
||||
ModelRef AssetManager::CreateTransparentPlane(f32 halfSize,
|
||||
u8 r, u8 g, u8 b, u8 a)
|
||||
{
|
||||
std::string key = "procedural://transparent-plane/" +
|
||||
std::to_string(halfSize) + "/" +
|
||||
std::to_string(r) + "," + std::to_string(g) + "," +
|
||||
std::to_string(b) + "," + std::to_string(a);
|
||||
return m_Models.Load(key, [&](const std::string&) {
|
||||
return m_Uploader.CreateTransparentPlane(halfSize, r, g, b, a);
|
||||
});
|
||||
}
|
||||
|
||||
EnvRef AssetManager::LoadEnvironment(const std::string& path)
|
||||
{
|
||||
// GPU 生成(compute 预积分)经接口发生在渲染层实现里
|
||||
|
||||
@@ -17,8 +17,10 @@ namespace Engine
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout layout,
|
||||
const std::array<Texture*, kSlotCount>& textures,
|
||||
const MaterialParams& params)
|
||||
const MaterialParams& params,
|
||||
bool transparent)
|
||||
: m_Textures(textures)
|
||||
, m_Transparent(transparent)
|
||||
{
|
||||
m_DescriptorSet = descriptorManager.AllocateDescriptorSet(layout);
|
||||
|
||||
@@ -44,7 +46,8 @@ namespace Engine
|
||||
Material::~Material() = default;
|
||||
|
||||
std::string Material::MakeCacheKey(const std::string paths[kSlotCount],
|
||||
const MaterialParams& params)
|
||||
const MaterialParams& params,
|
||||
bool transparent)
|
||||
{
|
||||
std::string key;
|
||||
for (u32 slot = 0; slot < kSlotCount; ++slot) {
|
||||
@@ -53,6 +56,7 @@ namespace Engine
|
||||
}
|
||||
key += "f:" + std::to_string(params.factors.x) +
|
||||
"," + std::to_string(params.factors.y);
|
||||
key += transparent ? ":t" : ":o"; // 透明语义入键(描述符相同但队列不同)
|
||||
return key;
|
||||
}
|
||||
} // namespace Engine
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#include <assimp/GltfMaterial.h> // AI_MATKEY_GLTF_ALPHAMODE
|
||||
|
||||
#include <meshoptimizer.h>
|
||||
|
||||
@@ -81,15 +82,19 @@ void Model::InitDefaultTextures()
|
||||
Model::Model(Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
ProceduralTag, f32 halfSize, const std::array<u8, 3>& color)
|
||||
ProceduralTag, f32 halfSize, const std::array<u8, 4>& rgba,
|
||||
bool transparent)
|
||||
: m_Device(device)
|
||||
, m_Allocator(allocator)
|
||||
, m_DescriptorManager(descriptorManager)
|
||||
, m_TextureLayout(textureLayout)
|
||||
{
|
||||
// 纯色 1x1 贴图(albedo 兜底)
|
||||
// 纯色贴图(albedo 兜底;alpha 非全 1 时为半透明材质)
|
||||
InitDefaultTextures();
|
||||
auto tex = Texture::CreateSolidColor(device, allocator, color[0], color[1], color[2]);
|
||||
auto tex = (rgba[3] == 255)
|
||||
? Texture::CreateSolidColor(device, allocator, rgba[0], rgba[1], rgba[2])
|
||||
: Texture::CreateSolidColorRGBA(device, allocator,
|
||||
rgba[0], rgba[1], rgba[2], rgba[3]);
|
||||
|
||||
// XZ 平面,法线 +Y,从上往下看 CCW 绕序
|
||||
const f32 h = halfSize;
|
||||
@@ -104,7 +109,7 @@ Model::Model(Device& device, MemoryAllocator& allocator,
|
||||
|
||||
// 材质/纹理生命周期随模型(缓存持有)
|
||||
std::string paths[Material::kSlotCount] = {"", "", "", ""};
|
||||
Material* mat = GetOrCreateMaterial(paths, MaterialParams{}); // 全默认材质
|
||||
Material* mat = GetOrCreateMaterial(paths, MaterialParams{}, transparent);
|
||||
|
||||
m_Meshes.push_back(CreateScope<Mesh>(
|
||||
m_Device, m_Allocator, vertices, lodIndices, mat));
|
||||
@@ -118,7 +123,20 @@ Scope<Model> Model::CreatePlane(Device& device, MemoryAllocator& allocator,
|
||||
{
|
||||
return Scope<Model>(new Model(device, allocator, descriptorManager,
|
||||
textureLayout, ProceduralTag{},
|
||||
halfSize, std::array<u8, 3>{r, g, b}));
|
||||
halfSize, std::array<u8, 4>{r, g, b, 255},
|
||||
false));
|
||||
}
|
||||
|
||||
Scope<Model> Model::CreateTransparentPlane(
|
||||
Device& device, MemoryAllocator& allocator,
|
||||
DescriptorManager& descriptorManager,
|
||||
vk::DescriptorSetLayout textureLayout,
|
||||
f32 halfSize, u8 r, u8 g, u8 b, u8 a)
|
||||
{
|
||||
return Scope<Model>(new Model(device, allocator, descriptorManager,
|
||||
textureLayout, ProceduralTag{},
|
||||
halfSize, std::array<u8, 4>{r, g, b, a},
|
||||
true));
|
||||
}
|
||||
|
||||
ParsedModel Model::ParseFromDisk(const std::string& path)
|
||||
@@ -245,9 +263,10 @@ Texture* Model::GetOrLoadTexture(const std::string& rawPath, bool srgb)
|
||||
}
|
||||
|
||||
Material* Model::GetOrCreateMaterial(const std::string paths[Material::kSlotCount],
|
||||
const MaterialParams& params)
|
||||
const MaterialParams& params,
|
||||
bool transparent)
|
||||
{
|
||||
const std::string key = Material::MakeCacheKey(paths, params);
|
||||
const std::string key = Material::MakeCacheKey(paths, params, transparent);
|
||||
if (auto it = m_MaterialCache.find(key); it != m_MaterialCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
@@ -267,7 +286,7 @@ Material* Model::GetOrCreateMaterial(const std::string paths[Material::kSlotCoun
|
||||
|
||||
auto mat = CreateScope<Material>(
|
||||
m_Device, m_Allocator, m_DescriptorManager, m_TextureLayout,
|
||||
textures, params);
|
||||
textures, params, transparent);
|
||||
Material* ptr = mat.get();
|
||||
m_MaterialCache.emplace(key, std::move(mat));
|
||||
return ptr;
|
||||
@@ -391,6 +410,14 @@ ParsedMesh Model::ProcessMesh(const aiMesh* mesh, const aiScene* scene,
|
||||
material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness);
|
||||
params.factors = glm::vec4(static_cast<f32>(metallic),
|
||||
static_cast<f32>(roughness), 0.0f, 0.0f);
|
||||
|
||||
// glTF alphaMode:BLEND = 半透明(透明绘制队列 + 深度只测不写)。
|
||||
// MASK(alpha cutout)暂按不透明——需要 alpha test,后续演进。
|
||||
// OBJ/FBX 无此键 → 恒不透明(资产格式定义写死)
|
||||
aiString alphaMode;
|
||||
if (material->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) {
|
||||
out.transparent = (alphaMode == aiString("BLEND"));
|
||||
}
|
||||
} else {
|
||||
f32 roughnessFactor = 1.0f;
|
||||
if (texPaths[Material::MetallicRoughness].empty()) {
|
||||
@@ -409,6 +436,7 @@ ParsedMesh Model::ProcessMesh(const aiMesh* mesh, const aiScene* scene,
|
||||
out.texPaths[slot] = std::move(texPaths[slot]);
|
||||
}
|
||||
out.params = params;
|
||||
// out.transparent 已在 glTF 分支读取
|
||||
|
||||
// ---- LOD 链(解析期生成,worker 线程免费时间):----
|
||||
// LOD0 = 原始;每级目标索引数减半,相对误差 1e-2。顶点共享,
|
||||
|
||||
@@ -131,10 +131,7 @@ namespace Engine::Passes
|
||||
cmd.setColorWriteMaskEXT(0,
|
||||
std::array<vk::ColorComponentFlags, 2>{colorWriteMask, colorWriteMask});
|
||||
|
||||
// ---- 绘制:GPUDriven 按 mesh 分 batch(相机视锥剔除后)----
|
||||
// 实例矩阵已由 Renderer 写入 ObjectBuffer(per-slot SSBO);
|
||||
// 同 mesh 的 N 个可见实体合并为一次 drawIndexed(instanceCount=N),
|
||||
// draw 数 = 可见唯一 mesh 数,与实体数量解耦
|
||||
// ---- 不透明批次:无混合直写,深度测试 + 写 ----
|
||||
for (const auto& batch : m_Frame.mainView.batches) {
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 1,
|
||||
@@ -142,6 +139,37 @@ namespace Engine::Passes
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first, batch.lod);
|
||||
}
|
||||
|
||||
// ---- 透明批次:预乘 alpha 混合(线性空间 over),深度只测
|
||||
// 不写,远 → 近排序(SceneRenderer 侧已按 batch 平均距离排)。
|
||||
// 附件 0(HDR)混合、附件 1(velocity)直写——透明表面的
|
||||
// velocity 就是它自身的运动,覆盖自己的像素即正确。
|
||||
// alpha 语义:frag 输出未预乘线性色 + 覆盖率 alpha,
|
||||
// 预乘在 tonemap 段(rgb * a)完成 —— hard constraint。
|
||||
if (!m_Frame.translucentView.batches.empty()) {
|
||||
vk::ColorBlendEquationEXT premul;
|
||||
premul.setSrcColorBlendFactor(vk::BlendFactor::eSrcAlpha)
|
||||
.setDstColorBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
|
||||
.setColorBlendOp(vk::BlendOp::eAdd)
|
||||
.setSrcAlphaBlendFactor(vk::BlendFactor::eOne)
|
||||
.setDstAlphaBlendFactor(vk::BlendFactor::eOneMinusSrcAlpha)
|
||||
.setAlphaBlendOp(vk::BlendOp::eAdd);
|
||||
|
||||
cmd.setColorBlendEnableEXT(0,
|
||||
std::array<vk::Bool32, 2>{VK_TRUE, VK_FALSE});
|
||||
cmd.setColorBlendEquationEXT(0,
|
||||
std::array<vk::ColorBlendEquationEXT, 2>{premul, blendEquation});
|
||||
cmd.setDepthWriteEnableEXT(VK_FALSE);
|
||||
|
||||
for (const auto& batch : m_Frame.translucentView.batches) {
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics,
|
||||
m_Gpu.forwardLayout, 1,
|
||||
batch.mesh->GetMaterial()->GetDescriptorSet(), {});
|
||||
batch.mesh->Draw(cmd, batch.count, batch.first, batch.lod);
|
||||
}
|
||||
|
||||
cmd.setDepthWriteEnableEXT(VK_TRUE); // 恢复(下帧 opaque 段也会重设)
|
||||
}
|
||||
|
||||
cmd.endRendering();
|
||||
})
|
||||
.Write(m_HDRColor, RG::ResourceUse::ColorAttachment)
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <tracy/Tracy.hpp>
|
||||
|
||||
#include <chrono>
|
||||
@@ -350,23 +353,34 @@ namespace Engine
|
||||
lodSlots[lod] = batchIdx;
|
||||
s.result.batches.push_back({mesh.get(), 0, 0, lod});
|
||||
s.batchMats.emplace_back();
|
||||
s.batchDistSum.push_back(0.0f);
|
||||
}
|
||||
// 实例归入自己的 batch(mesh 分组),保证同 batch
|
||||
// 实例在扁平化后连续——firstInstance 语义的前提
|
||||
const u32 bi = batchIdx - 1;
|
||||
s.result.batches[bi].count++;
|
||||
s.batchMats[bi].push_back({t.World(), prevWorld});
|
||||
s.batchDistSum[bi] += dist; // 透明排序用
|
||||
s.result.instanceCount++;
|
||||
++totalInstances;
|
||||
};
|
||||
|
||||
tryAppend(m_MainScratch, mainFrustum, mainLod);
|
||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||
if (!cascVis[c]) continue; // 粗剔除已判不可见
|
||||
// 级联降级:越远的级联覆盖范围越大,用越低的 LOD
|
||||
//(阴影本来就是低频信息,误差可接受);开关关闭 = 恒 LOD0
|
||||
tryAppend(m_CascScratch[c], cascFrustum[c],
|
||||
m_LodEnabled ? std::min(c, kMeshLodCount - 1) : 0u);
|
||||
// 透明材质 → 独立队列(主视图,混合绘制 + 排序;
|
||||
// 不进阴影——alpha 阴影复杂度高收益低,注释后续演进)
|
||||
const bool isTransparentMesh =
|
||||
mesh->GetMaterial()->IsTransparent();
|
||||
ViewScratch& mainTarget = isTransparentMesh
|
||||
? m_TranslucentScratch : m_MainScratch;
|
||||
|
||||
tryAppend(mainTarget, mainFrustum, mainLod);
|
||||
if (!isTransparentMesh) {
|
||||
for (u32 c = 0; c < kMaxCascades; ++c) {
|
||||
if (!cascVis[c]) continue; // 粗剔除已判不可见
|
||||
// 级联降级:越远的级联覆盖范围越大,用越低的 LOD
|
||||
//(阴影本来就是低频信息,误差可接受);开关关闭 = 恒 LOD0
|
||||
tryAppend(m_CascScratch[c], cascFrustum[c],
|
||||
m_LodEnabled ? std::min(c, kMeshLodCount - 1) : 0u);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -393,6 +407,44 @@ namespace Engine
|
||||
appendView(m_Frame.shadowCascades[c], m_CascScratch[c]);
|
||||
}
|
||||
|
||||
// ---- 5. 透明批次排序:平均距离降序(远 → 近),连同实例列表同步重排
|
||||
{
|
||||
auto& tb = m_TranslucentScratch.result.batches;
|
||||
if (tb.size() > 1) {
|
||||
std::vector<u32> order(tb.size());
|
||||
std::iota(order.begin(), order.end(), 0);
|
||||
std::sort(order.begin(), order.end(), [&](u32 a, u32 b) {
|
||||
return m_TranslucentScratch.batchDistSum[a] / tb[a].count >
|
||||
m_TranslucentScratch.batchDistSum[b] / tb[b].count;
|
||||
});
|
||||
CullResult sorted;
|
||||
sorted.instanceCount = m_TranslucentScratch.result.instanceCount;
|
||||
std::vector<ViewScratch::InstanceMatrices> sortedMats;
|
||||
std::vector<f32> sortedDist;
|
||||
for (const u32 i : order) {
|
||||
sorted.batches.push_back(tb[i]);
|
||||
sortedDist.push_back(m_TranslucentScratch.batchDistSum[i]);
|
||||
for (auto& m : m_TranslucentScratch.batchMats[i]) {
|
||||
sortedMats.push_back(std::move(m));
|
||||
}
|
||||
}
|
||||
m_TranslucentScratch.result = std::move(sorted);
|
||||
m_TranslucentScratch.batchDistSum = std::move(sortedDist);
|
||||
// batchMats 按新分组的 count 顺序切分重装(保留容量)
|
||||
m_TranslucentScratch.batchMats.assign(
|
||||
m_TranslucentScratch.result.batches.size(), {});
|
||||
u32 offset = 0;
|
||||
for (size_t i = 0; i < m_TranslucentScratch.result.batches.size(); ++i) {
|
||||
const u32 n = m_TranslucentScratch.result.batches[i].count;
|
||||
m_TranslucentScratch.batchMats[i].assign(
|
||||
std::make_move_iterator(sortedMats.begin() + offset),
|
||||
std::make_move_iterator(sortedMats.begin() + offset + n));
|
||||
offset += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
appendView(m_Frame.translucentView, m_TranslucentScratch);
|
||||
|
||||
// 帧末滚动时域状态:cur → prev(每帧重建,销毁的实体自动清出)
|
||||
m_PrevWorlds.swap(m_CurWorlds);
|
||||
m_CurWorlds.clear();
|
||||
|
||||
@@ -29,6 +29,14 @@ namespace Engine {
|
||||
m_TextureLayout, halfSize, r, g, b);
|
||||
}
|
||||
|
||||
Scope<Model> VulkanAssetUploader::CreateTransparentPlane(
|
||||
f32 halfSize, u8 r, u8 g, u8 b, u8 a)
|
||||
{
|
||||
return Model::CreateTransparentPlane(m_Device, m_Allocator,
|
||||
m_Descriptors, m_TextureLayout,
|
||||
halfSize, r, g, b, a);
|
||||
}
|
||||
|
||||
Scope<Cubemap> VulkanAssetUploader::LoadEnvironment(const std::string& path)
|
||||
{
|
||||
// compute 预积分(毫秒级):HDR → cube + irradiance/prefilter/LUT
|
||||
|
||||
@@ -64,6 +64,13 @@ namespace Engine
|
||||
{r, g, b, 255}, srgb));
|
||||
}
|
||||
|
||||
Scope<Texture> Texture::CreateSolidColorRGBA(Device& device, MemoryAllocator& allocator,
|
||||
u8 r, u8 g, u8 b, u8 a, bool srgb)
|
||||
{
|
||||
return Scope<Texture>(new Texture(device, allocator, ProceduralTag{},
|
||||
{r, g, b, a}, srgb));
|
||||
}
|
||||
|
||||
void Texture::InitFromPixels(const void* pixels, u32 width, u32 height, bool srgb)
|
||||
{
|
||||
m_Width = width;
|
||||
|
||||
Reference in New Issue
Block a user