fix: IBL 模型全黑——prefilter mip 拷贝偏移错位 + 采样器 maxLod 错误

- UploadCube 的 mip buffer 偏移误用当前 mip 尺寸×序号(mip 面递减,
  正确应为累积偏移)→ 各 mip 拷到 staging 错位数据,含未初始化 fp16
  NaN → 污染 ambient 输出整屏黑
- prefilter/irradiance 绑定改用环境采样器(maxLod=mipCount-1):
  shadowSampler maxLod=0 会把 textureLod 显式 lod clamp 到 0,
  pre-filter 永远停在最锐 mip
This commit is contained in:
2026-09-23 19:07:53 +08:00
parent 9269afaa18
commit 6e107ac2ba
2 changed files with 14 additions and 5 deletions
+8 -2
View File
@@ -328,13 +328,17 @@ namespace Engine
cmd.pipelineBarrier2( cmd.pipelineBarrier2(
vk::DependencyInfo{}.setImageMemoryBarriers(toTransfer)); vk::DependencyInfo{}.setImageMemoryBarriers(toTransfer));
// 逐 mip 逐面拷贝;buffer 偏移必须按 mips[0..mip) 字节数累积
//(mip 面尺寸递减,用当前 dim 乘 mip 序号会错位 → 读到
// staging 未初始化数据 → fp16 NaN 污染整屏)
vk::DeviceSize offset = 0;
for (u32 mip = 0; mip < mipCount; ++mip) { for (u32 mip = 0; mip < mipCount; ++mip) {
const u32 dim = mips[mip].faceSize; const u32 dim = mips[mip].faceSize;
for (u32 f = 0; f < kFaceCount; ++f) { for (u32 f = 0; f < kFaceCount; ++f) {
vk::BufferImageCopy region; vk::BufferImageCopy region;
region.setBufferOffset( region.setBufferOffset(
(static_cast<vk::DeviceSize>(dim) * dim * 8 * offset + static_cast<vk::DeviceSize>(dim) *
kFaceCount) * mip) dim * 8 * f)
.setBufferRowLength(0) .setBufferRowLength(0)
.setBufferImageHeight(0) .setBufferImageHeight(0)
.setImageSubresource({vk::ImageAspectFlagBits::eColor, .setImageSubresource({vk::ImageAspectFlagBits::eColor,
@@ -345,6 +349,8 @@ namespace Engine
vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eTransferDstOptimal,
region); region);
} }
offset += static_cast<vk::DeviceSize>(dim) * dim * 8 *
kFaceCount;
} }
vk::ImageMemoryBarrier2 toShader; vk::ImageMemoryBarrier2 toShader;
+6 -3
View File
@@ -166,11 +166,14 @@ namespace Engine::Passes
void SceneGpu::BindEnvironment(const Cubemap& env) void SceneGpu::BindEnvironment(const Cubemap& env)
{ {
// IBL 资源 view 全程稳定(不随 RG 重编译变化),绑一次即可 // IBL 资源 view 全程稳定(不随 RG 重编译变化),绑一次即可。
// cube 资源必须用带 mip 的环境采样器(textureLod 显式 lod 会被
// clamp 到 maxLod;shadowSampler maxLod=0 会让 pre-filter 永远
// 停在最锐的 mip)
for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) { for (u32 slot = 0; slot < kMaxFramesInFlight; ++slot) {
m_Descriptors.UpdateTexture(shadowSets[slot], 3, shadowSampler, m_Descriptors.UpdateTexture(shadowSets[slot], 3, env.GetSampler(),
env.GetIrradianceView()); env.GetIrradianceView());
m_Descriptors.UpdateTexture(shadowSets[slot], 4, shadowSampler, m_Descriptors.UpdateTexture(shadowSets[slot], 4, env.GetSampler(),
env.GetPrefilteredView()); env.GetPrefilteredView());
m_Descriptors.UpdateTexture(shadowSets[slot], 5, m_Descriptors.UpdateTexture(shadowSets[slot], 5,
env.GetBrdfLutSampler(), env.GetBrdfLutSampler(),