diff --git a/Engine/include/Engine/Renderer/Material.h b/Engine/include/Engine/Renderer/Material.h index bf9c252..516e5a8 100644 --- a/Engine/include/Engine/Renderer/Material.h +++ b/Engine/include/Engine/Renderer/Material.h @@ -43,7 +43,10 @@ namespace Engine { // x = metallic 因子 y = roughness 因子 // z = 独立粗糙度图开关(1 = 用 Roughness 槽 R 通道替换 MR.g) struct MaterialParams { + // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关,w = MASK 镂空开关 glm::vec4 factors{0.0f, 0.5f, 0.0f, 0.0f}; + // glTF baseColorFactor:rgb 乘进 albedo,a 作 BLEND 透明度 + glm::vec4 baseColor{1.0f, 1.0f, 1.0f, 1.0f}; }; class Material { diff --git a/Engine/include/Engine/Vulkan/Device.h b/Engine/include/Engine/Vulkan/Device.h index 81bb415..2143e33 100644 --- a/Engine/include/Engine/Vulkan/Device.h +++ b/Engine/include/Engine/Vulkan/Device.h @@ -45,6 +45,9 @@ public: bool IsSynchronization2Supported() const { return m_Synchronization2Supported; } // MDI:drawCount>1 的 drawIndexedIndirect(Count) 必需(core 特性位) bool IsMultiDrawIndirectSupported() const { return m_MultiDrawIndirectSupported; } + // 各向异性过滤:纹理采样器清晰度(掠射角材质锐利度)的硬件开关 + bool IsSamplerAnisotropySupported() const { return m_SamplerAnisotropySupported; } + float MaxSamplerAnisotropy() const { return m_MaxSamplerAnisotropy; } private: void CreateInstance(); @@ -76,6 +79,8 @@ private: bool m_Synchronization2Supported = false; bool m_MultiDrawIndirectSupported = false; bool m_DrawIndirectCountSupported = false; + bool m_SamplerAnisotropySupported = false; + float m_MaxSamplerAnisotropy = 1.0f; std::vector m_ValidationLayers = { "VK_LAYER_KHRONOS_validation" }; std::vector m_DeviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; diff --git a/Engine/include/Engine/Vulkan/Texture.h b/Engine/include/Engine/Vulkan/Texture.h index 57266a4..4ca09a5 100644 --- a/Engine/include/Engine/Vulkan/Texture.h +++ b/Engine/include/Engine/Vulkan/Texture.h @@ -54,7 +54,8 @@ namespace Engine { Texture(Device& device, MemoryAllocator& allocator, ProceduralTag, const std::array& rgba, bool srgb); - // 公共路径:staging → device local image → view/sampler(RGBA8 单 mip) + // 公共路径:staging → device local image → blit 生成完整 mip 链 → + // view/sampler(RGBA8,各向异性过滤) void InitFromPixels(const void* pixels, u32 width, u32 height, bool srgb); // DDS 路径:压缩数据直传(BC 硬件采样解码)+ 文件自带 mip 链 diff --git a/Engine/src/Renderer/Material.cpp b/Engine/src/Renderer/Material.cpp index 0b9d0da..7c331c1 100644 --- a/Engine/src/Renderer/Material.cpp +++ b/Engine/src/Renderer/Material.cpp @@ -55,7 +55,13 @@ namespace Engine key += '\n'; } key += "f:" + std::to_string(params.factors.x) + - "," + std::to_string(params.factors.y); + "," + std::to_string(params.factors.y) + + "," + std::to_string(params.factors.z) + + "," + std::to_string(params.factors.w) + + ",c:" + std::to_string(params.baseColor.x) + + "," + std::to_string(params.baseColor.y) + + "," + std::to_string(params.baseColor.z) + + "," + std::to_string(params.baseColor.w); key += transparent ? ":t" : ":o"; // 透明语义入键(描述符相同但队列不同) return key; } diff --git a/Engine/src/Renderer/Model.cpp b/Engine/src/Renderer/Model.cpp index bfd8e55..3742369 100644 --- a/Engine/src/Renderer/Model.cpp +++ b/Engine/src/Renderer/Model.cpp @@ -695,12 +695,27 @@ ParsedMesh Model::ProcessMesh(const aiMesh* mesh, const aiScene* scene, params.factors = glm::vec4(static_cast(metallic), static_cast(roughness), 0.0f, 0.0f); - // glTF alphaMode:BLEND = 半透明(透明绘制队列 + 深度只测不写)。 - // MASK(alpha cutout)暂按不透明——需要 alpha test,后续演进。 + // baseColorFactor:rgb 调色进 albedo;a 是 BLEND 材质的透明度 + //(玻璃等无 baseColor 贴图、纯靠 factor alpha 的材质全靠它, + // 缺了会退化成兜底白纹理的 a=1 = 完全不透) + aiColor4D baseColor(1.0f, 1.0f, 1.0f, 1.0f); + if (material->Get(AI_MATKEY_BASE_COLOR, baseColor) == AI_SUCCESS) { + params.baseColor = glm::vec4(baseColor.r, baseColor.g, + baseColor.b, baseColor.a); + } + + // glTF alphaMode: + // BLEND = 半透明(透明绘制队列 + 深度只测不写) + // MASK = 镂空(factors.w=1 → shader 侧 alpha test 丢弃, + // 表针/栅栏等硬边贴图常见)仍走不透明队列 // OBJ/FBX 无此键 → 恒不透明(资产格式定义写死) aiString alphaMode; if (material->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { - out.transparent = (alphaMode == aiString("BLEND")); + if (alphaMode == aiString("BLEND")) { + out.transparent = true; + } else if (alphaMode == aiString("MASK")) { + params.factors.w = 1.0f; + } } } else { // FBX/OBJ 独立贴图工作流 diff --git a/Engine/src/Vulkan/Device.cpp b/Engine/src/Vulkan/Device.cpp index 8f70ab7..749a169 100644 --- a/Engine/src/Vulkan/Device.cpp +++ b/Engine/src/Vulkan/Device.cpp @@ -166,6 +166,10 @@ void Device::PickPhysicalDevice() m_DynamicRenderingSupported = features13.dynamicRendering; m_Synchronization2Supported = features13.synchronization2; + // 各向异性过滤(core 特性位;桌面 GPU 普遍支持,不支持则采样器保持关闭) + m_SamplerAnisotropySupported = features2.features.samplerAnisotropy == VK_TRUE; + m_MaxSamplerAnisotropy = props.limits.maxSamplerAnisotropy; + // core 特性:MDI(drawCount>1)必需 m_MultiDrawIndirectSupported = features2.features.multiDrawIndirect == VK_TRUE; // 1.2 特性:drawIndexedIndirectCount 必需(VUID-04445) @@ -255,6 +259,8 @@ void Device::CreateLogicalDevice() vk::PhysicalDeviceFeatures features; // MDI:按材质分组的 drawIndexedIndirectCount,drawCount>1 必需 features.setMultiDrawIndirect(m_MultiDrawIndirectSupported ? VK_TRUE : VK_FALSE); + // 纹理各向异性过滤(材质掠射角清晰度) + features.setSamplerAnisotropy(m_SamplerAnisotropySupported ? VK_TRUE : VK_FALSE); // ======================================================== // 启用 1.4 特性链 diff --git a/Engine/src/Vulkan/Texture.cpp b/Engine/src/Vulkan/Texture.cpp index fa56ef5..48e5720 100644 --- a/Engine/src/Vulkan/Texture.cpp +++ b/Engine/src/Vulkan/Texture.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include "Engine/Core/Stats.h" @@ -76,7 +77,10 @@ namespace Engine m_Width = width; m_Height = height; m_Format = srgb ? vk::Format::eR8G8B8A8Srgb : vk::Format::eR8G8B8A8Unorm; - m_MipLevels = 1; + // 完整 mip 链:远处/掠射角采样落到低分辨率层(模糊 + 抗闪烁)。 + // 没有它,4K 贴图在小 UV 区域(如表针)采到的是原始分辨率单层 + m_MipLevels = 1 + static_cast( + std::floor(std::log2(std::max(std::max(width, height), 1u)))); vk::DeviceSize imageSize = static_cast(width) * static_cast(height) * 4; // RGBA8 @@ -95,11 +99,13 @@ namespace Engine imageInfo.setImageType(vk::ImageType::e2D) .setFormat(m_Format) .setExtent({m_Width, m_Height, 1}) - .setMipLevels(1) + .setMipLevels(m_MipLevels) .setArrayLayers(1) .setSamples(vk::SampleCountFlagBits::e1) .setTiling(vk::ImageTiling::eOptimal) - .setUsage(vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled) + .setUsage(vk::ImageUsageFlagBits::eTransferDst | + vk::ImageUsageFlagBits::eTransferSrc | + vk::ImageUsageFlagBits::eSampled) .setSharingMode(vk::SharingMode::eExclusive) .setInitialLayout(vk::ImageLayout::eUndefined); @@ -120,14 +126,17 @@ namespace Engine m_View = m_Device.GetDevice().createImageView(viewInfo); - // 5.5 创建 Sampler + // 5.5 创建 Sampler(各向异性过滤 = 掠射角材质锐利度关键,如地面/表盘) + const bool af = m_Device.IsSamplerAnisotropySupported(); vk::SamplerCreateInfo samplerInfo; samplerInfo.setMagFilter(vk::Filter::eLinear) .setMinFilter(vk::Filter::eLinear) .setAddressModeU(vk::SamplerAddressMode::eRepeat) .setAddressModeV(vk::SamplerAddressMode::eRepeat) .setAddressModeW(vk::SamplerAddressMode::eRepeat) - .setAnisotropyEnable(VK_FALSE) + .setAnisotropyEnable(af ? VK_TRUE : VK_FALSE) + .setMaxAnisotropy(af ? std::min(16.0f, m_Device.MaxSamplerAnisotropy()) + : 1.0f) .setBorderColor(vk::BorderColor::eIntOpaqueBlack) .setUnnormalizedCoordinates(VK_FALSE) .setCompareEnable(VK_FALSE) @@ -182,7 +191,53 @@ namespace Engine cmd.copyBufferToImage(stagingAlloc.Buffer, m_Image, vk::ImageLayout::eTransferDstOptimal, region); - // 6.3 布局:TRANSFER_DST → SHADER_READ_ONLY + // 6.3 blit 生成 mip 链:逐级半分辨率缩小(硬件线性滤波)。 + // 每级先把上一 mip 转 TRANSFER_SRC(等拷贝/上次 blit 写完), + // blit 出当前 mip,再把上一 mip 转着色器只读;末级单独收尾 + i32 mipW = static_cast(m_Width); + i32 mipH = static_cast(m_Height); + for (u32 mip = 1; mip < m_MipLevels; ++mip) { + vk::ImageMemoryBarrier2 toSrc; + toSrc.setSrcStageMask(vk::PipelineStageFlagBits2::eTransfer) + .setSrcAccessMask(vk::AccessFlagBits2::eTransferWrite) + .setDstStageMask(vk::PipelineStageFlagBits2::eTransfer) + .setDstAccessMask(vk::AccessFlagBits2::eTransferRead) + .setOldLayout(vk::ImageLayout::eTransferDstOptimal) + .setNewLayout(vk::ImageLayout::eTransferSrcOptimal) + .setImage(m_Image) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, + mip - 1, 1, 0, 1}); + cmd.pipelineBarrier2(vk::DependencyInfo{}.setImageMemoryBarriers(toSrc)); + + vk::ImageBlit blit; + blit.setSrcOffsets({vk::Offset3D{0, 0, 0}, vk::Offset3D{mipW, mipH, 1}}); + blit.setSrcSubresource({vk::ImageAspectFlagBits::eColor, mip - 1, 0, 1}); + blit.setDstOffsets({vk::Offset3D{0, 0, 0}, + vk::Offset3D{mipW > 1 ? mipW / 2 : 1, + mipH > 1 ? mipH / 2 : 1, 1}}); + blit.setDstSubresource({vk::ImageAspectFlagBits::eColor, mip, 0, 1}); + cmd.blitImage(m_Image, vk::ImageLayout::eTransferSrcOptimal, + m_Image, vk::ImageLayout::eTransferDstOptimal, + blit, vk::Filter::eLinear); + + // 上一级已消费完毕 → 直接转着色器只读 + vk::ImageMemoryBarrier2 toRead; + toRead.setSrcStageMask(vk::PipelineStageFlagBits2::eTransfer) + .setSrcAccessMask(vk::AccessFlagBits2::eTransferRead) + .setDstStageMask(vk::PipelineStageFlagBits2::eFragmentShader) + .setDstAccessMask(vk::AccessFlagBits2::eShaderRead) + .setOldLayout(vk::ImageLayout::eTransferSrcOptimal) + .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) + .setImage(m_Image) + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, + mip - 1, 1, 0, 1}); + cmd.pipelineBarrier2(vk::DependencyInfo{}.setImageMemoryBarriers(toRead)); + + if (mipW > 1) mipW /= 2; + if (mipH > 1) mipH /= 2; + } + + // 末级 mip 收尾(blit 目标;单 mip 时即 mip0)→ SHADER_READ_ONLY vk::ImageMemoryBarrier2 toShader; toShader.setSrcStageMask(vk::PipelineStageFlagBits2::eTransfer) .setSrcAccessMask(vk::AccessFlagBits2::eTransferWrite) @@ -191,7 +246,8 @@ namespace Engine .setOldLayout(vk::ImageLayout::eTransferDstOptimal) .setNewLayout(vk::ImageLayout::eShaderReadOnlyOptimal) .setImage(m_Image) - .setSubresourceRange({vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1}); + .setSubresourceRange({vk::ImageAspectFlagBits::eColor, + m_MipLevels - 1, 1, 0, 1}); vk::DependencyInfo dep2; dep2.setImageMemoryBarriers(toShader); @@ -330,7 +386,11 @@ namespace Engine .setAddressModeU(vk::SamplerAddressMode::eRepeat) .setAddressModeV(vk::SamplerAddressMode::eRepeat) .setAddressModeW(vk::SamplerAddressMode::eRepeat) - .setAnisotropyEnable(VK_FALSE) + .setAnisotropyEnable(m_Device.IsSamplerAnisotropySupported() + ? VK_TRUE : VK_FALSE) + .setMaxAnisotropy(m_Device.IsSamplerAnisotropySupported() + ? std::min(16.0f, m_Device.MaxSamplerAnisotropy()) + : 1.0f) .setBorderColor(vk::BorderColor::eIntOpaqueBlack) .setUnnormalizedCoordinates(VK_FALSE) .setCompareEnable(VK_FALSE) diff --git a/shaders/simple_shader.frag b/shaders/simple_shader.frag index fb7b9ea..2a064e5 100644 --- a/shaders/simple_shader.frag +++ b/shaders/simple_shader.frag @@ -42,7 +42,8 @@ layout(set = 1, binding = 3) uniform sampler2D emissiveMap; layout(set = 1, binding = 4) uniform sampler2D aoMap; // 环境光遮蔽(R 通道) layout(set = 1, binding = 5) uniform sampler2D roughnessMap; // 独立粗糙度图(R 通道,specular 工作流) layout(set = 1, binding = 6) uniform MaterialParams { - vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关 + vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关,w = MASK 镂空开关 + vec4 baseColor; // glTF baseColorFactor:rgb 调色 albedo,a = BLEND 透明度 } matParams; // CSM 阴影(sampler2DShadow:单次采样 = 硬件 2×2 深度比较 PCF) @@ -136,7 +137,9 @@ vec3 FresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { void main() { vec4 albedoTex = texture(albedoMap, fragUV); - vec3 albedo = albedoTex.rgb; + // alphaMode=MASK 镂空(表针/栅栏等硬边贴图):低于阈值的纹素直接丢弃 + if (matParams.factors.w > 0.5 && albedoTex.a < 0.5) discard; + vec3 albedo = albedoTex.rgb * matParams.baseColor.rgb; vec4 mr = texture(metallicRoughnessMap, fragUV); // 粗糙度源按 factors.z 切换:glTF 合图工作流读 MR.g; // FBX/OBJ specular 工作流(z=1)换用独立粗糙度图 R 通道 @@ -223,8 +226,8 @@ void main() { vec3 ambient = (diffuse + specular) * ubo.ambientColor.rgb * mix(0.4, 1.0, shade0); // 阴影区环境同步衰减(简化遮蔽) - // alpha = albedo 贴图覆盖率(glTF alphaMode=BLEND 混合源); - // 不透明资产贴图 a=1 行为不变。预乘在 tonemap 段完成。 - outColor = vec4(ambient + Lo + emissive, albedoTex.a); + // alpha = albedo 贴图覆盖率 × baseColorFactor.a(glTF alphaMode=BLEND + // 混合源);不透明资产两者恒 1 行为不变。预乘在 tonemap 段完成。 + outColor = vec4(ambient + Lo + emissive, albedoTex.a * matParams.baseColor.a); outVelocity = fragVelocity; } diff --git a/shaders/simple_shader_translucent.frag b/shaders/simple_shader_translucent.frag index cde5c8d..ea818bd 100644 --- a/shaders/simple_shader_translucent.frag +++ b/shaders/simple_shader_translucent.frag @@ -42,7 +42,8 @@ layout(set = 1, binding = 3) uniform sampler2D emissiveMap; layout(set = 1, binding = 4) uniform sampler2D aoMap; // 环境光遮蔽(R 通道) layout(set = 1, binding = 5) uniform sampler2D roughnessMap; // 独立粗糙度图(R 通道,specular 工作流) layout(set = 1, binding = 6) uniform MaterialParams { - vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关 + vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关,w = MASK 镂空开关 + vec4 baseColor; // glTF baseColorFactor:rgb 调色 albedo,a = BLEND 透明度 } matParams; // CSM 阴影(sampler2DShadow:单次采样 = 硬件 2×2 深度比较 PCF) @@ -136,7 +137,7 @@ vec3 FresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness) { void main() { vec4 albedoTex = texture(albedoMap, fragUV); - vec3 albedo = albedoTex.rgb; + vec3 albedo = albedoTex.rgb * matParams.baseColor.rgb; vec4 mr = texture(metallicRoughnessMap, fragUV); // 粗糙度源按 factors.z 切换:glTF 合图工作流读 MR.g; // FBX/OBJ specular 工作流(z=1)换用独立粗糙度图 R 通道 @@ -223,9 +224,10 @@ void main() { vec3 ambient = (diffuse + specular) * ubo.ambientColor.rgb * mix(0.4, 1.0, shade0); // 阴影区环境同步衰减(简化遮蔽) - // alpha = albedo 贴图覆盖率(glTF alphaMode=BLEND 混合源); - // 不透明资产贴图 a=1 行为不变。预乘在 tonemap 段完成。 - outColor = vec4(ambient + Lo + emissive, albedoTex.a); + // alpha = albedo 贴图覆盖率 × baseColorFactor.a(glTF alphaMode=BLEND + // 混合源;玻璃类无 baseColor 贴图材质的透明度全靠 factor.a)。 + // 预乘在 tonemap 段完成。 + outColor = vec4(ambient + Lo + emissive, albedoTex.a * matParams.baseColor.a); outVelocity = fragVelocity; // 深度语义:真实深度测试 + 真实写入(depthWrite 状态保持 TRUE——