From 22309d24f0750115aefa32e864d9eb37070355f4 Mon Sep 17 00:00:00 2001 From: atdunbg Date: Sun, 18 Oct 2026 15:23:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9D=90=E8=B4=A8=E4=B8=8E=E7=8E=AF=E5=A2=83?= =?UTF-8?q?=E5=85=89=E7=85=A7=EF=BC=9AUE=20=E6=89=93=E5=8C=85=20Specular?= =?UTF-8?q?=20=E5=AF=BC=E5=85=A5=EF=BC=88R=3DAO/G=3DRough/B=3DMetal=20?= =?UTF-8?q?=E4=B8=8E=20glTF=20MR=20=E5=90=8C=E5=BA=8F=20=E2=86=92=20?= =?UTF-8?q?=E5=90=8C=E4=B8=80=E8=B4=B4=E5=9B=BE=E7=9B=B4=E4=BE=9B=20Metall?= =?UTF-8?q?icRoughness=20+=20Ao=20=E6=A7=BD=EF=BC=8Cfactors=20=E7=9B=B4?= =?UTF-8?q?=E7=94=A8=E8=B4=B4=E5=9B=BE=EF=BC=8C=E9=BB=98=E8=AE=A4=E7=BD=AE?= =?UTF-8?q?=E7=BF=BB=E7=BB=BF=E4=BD=8D=20misc.w=EF=BC=89=EF=BC=9BMappedNor?= =?UTF-8?q?mal=20=E6=94=AF=E6=8C=81=20DirectX=20=E6=B3=95=E7=BA=BF?= =?UTF-8?q?=E7=BF=BB=E7=BB=BF=EF=BC=88misc.w=EF=BC=8C=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=99=A8=E5=8A=A0=20Flip=20Green=20=E5=BC=80=E5=85=B3=EF=BC=89?= =?UTF-8?q?=EF=BC=9B=E7=8E=AF=E5=A2=83=E9=81=AE=E8=94=BD=E6=8B=86=E5=88=86?= =?UTF-8?q?=E6=BC=AB=E5=8F=8D=E5=B0=84=E4=B8=8E=E9=AB=98=E5=85=89=EF=BC=88?= =?UTF-8?q?=E9=AB=98=E5=85=89=E9=81=AE=E8=94=BD=E6=9B=B4=E7=8B=A0=EF=BC=9A?= =?UTF-8?q?=E5=AE=A4=E5=86=85=E9=AB=98=E5=85=89=E9=A1=BB=E5=8E=8B=E6=9A=97?= =?UTF-8?q?=EF=BC=8C=E6=AD=A4=E5=89=8D=E8=A1=A8=E9=9D=A2=E5=8F=8D=E5=B0=84?= =?UTF-8?q?=E5=A4=A9=E7=A9=BA=E8=B4=B4=E5=9B=BE=20=3D=20=E5=AE=A4=E5=86=85?= =?UTF-8?q?=E7=A9=BF=E5=B8=AE=EF=BC=89=EF=BC=9Bufbx=20=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E6=97=A5=E5=BF=97=E8=A1=A5=E9=BD=90=20mr/normal/ao/emissive=20?= =?UTF-8?q?=E6=A7=BD=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/editor/panels/MaterialEditorPanel.cpp | 9 +++++ Engine/src/Renderer/Model.cpp | 41 +++++++++++++++++++--- shaders/pbr/simple_shader.frag | 16 ++++++--- shaders/pbr/simple_shader_translucent.frag | 4 ++- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/App/editor/panels/MaterialEditorPanel.cpp b/App/editor/panels/MaterialEditorPanel.cpp index 0007808..67fcbc1 100644 --- a/App/editor/panels/MaterialEditorPanel.cpp +++ b/App/editor/panels/MaterialEditorPanel.cpp @@ -211,6 +211,15 @@ void MaterialEditorPanel::DrawAssetMode(const EditorContext& ctx) if (c) { p.misc.z = flipV ? 1.0f : 0.0f; } EditParam(c); } + ImGui::SameLine(); + { + // DirectX 法线绿通道翻转(UE 打包 Specular 资产由导入器默认为开; + // 方向反了在此逐材质纠正)。写 misc.w,旧 .material 缺省 0 = 关闭 + bool flipG = p.misc.w > 0.5f; + const bool c = ImGui::Checkbox("Flip Green (DirectX)", &flipG); + if (c) { p.misc.w = flipG ? 1.0f : 0.0f; } + EditParam(c); + } // ---- 纹理槽(变化 → 落盘 + waitIdle 重建)---- ImGui::SeparatorText("Textures"); diff --git a/Engine/src/Renderer/Model.cpp b/Engine/src/Renderer/Model.cpp index dd3437f..6481a2e 100644 --- a/Engine/src/Renderer/Model.cpp +++ b/Engine/src/Renderer/Model.cpp @@ -288,12 +288,45 @@ namespace { dst.texPaths[Material::Albedo] = ResolveUfbxTexture(modelDir, *base.texture); } + + // UE/ORCA 打包 Specular 工作流:R=AO, G=Roughness, B=Metalness。 + // 通道与 glTF MR 合图(G=rough, B=metal)恰好同序 → 同一张贴图 + // 直供 MetallicRoughness 槽 + Ao 槽,纹理缓存按路径去重共享同一 + // Texture*。factors=(1,1,0,0):金属/粗糙因子直用贴图(z=0 → shader + // 读 MR.g 当粗糙度,同 glTF 语义)。UE 打包 Specular ⇔ DirectX + // 法线(同源约定)→ 默认置翻绿位 misc.w + if (const ufbx_material_map& spec = mat->pbr.specular_color; + spec.texture != nullptr) { + const std::string p = + ResolveUfbxTexture(modelDir, *spec.texture); + dst.texPaths[Material::MetallicRoughness] = p; + dst.texPaths[Material::Ao] = p; + dst.params.factors = glm::vec4(1.0f, 1.0f, 0.0f, 0.0f); + dst.params.misc.w = 1.0f; + } + if (const ufbx_material_map& nrm = mat->pbr.normal_map; + nrm.texture != nullptr) { + dst.texPaths[Material::Normal] = + ResolveUfbxTexture(modelDir, *nrm.texture); + } + if (const ufbx_material_map& emi = mat->pbr.emission_color; + emi.texture != nullptr) { + dst.texPaths[Material::Emissive] = + ResolveUfbxTexture(modelDir, *emi.texture); + } ENGINE_INFO( - "ufbx Mesh '{}': Material '{}': albedo={}", dst.name, - mat->name.length ? U8Str(mat->name) : "?", + "ufbx Mesh '{}': Material '{}': albedo={} mr={} normal={} ao={} emissive={}", + dst.name, mat->name.length ? U8Str(mat->name) : "?", dst.texPaths[Material::Albedo].empty() - ? "" - : dst.texPaths[Material::Albedo]); + ? "" : dst.texPaths[Material::Albedo], + dst.texPaths[Material::MetallicRoughness].empty() + ? "" : dst.texPaths[Material::MetallicRoughness], + dst.texPaths[Material::Normal].empty() + ? "" : dst.texPaths[Material::Normal], + dst.texPaths[Material::Ao].empty() + ? "" : dst.texPaths[Material::Ao], + dst.texPaths[Material::Emissive].empty() + ? "" : dst.texPaths[Material::Emissive]); }; std::unordered_map boneSlots; diff --git a/shaders/pbr/simple_shader.frag b/shaders/pbr/simple_shader.frag index 95c5115..d2e8211 100644 --- a/shaders/pbr/simple_shader.frag +++ b/shaders/pbr/simple_shader.frag @@ -45,7 +45,7 @@ layout(set = 1, binding = 6) uniform MaterialParams { vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关,w = MASK 镂空开关 vec4 baseColor; // glTF baseColorFactor:rgb 调色 albedo,a = BLEND 透明度 vec4 surface; // x = ior(F0 换算),y = transmission(不透明段恒 0 语义),z = 自发光强度,w = 体积厚度 - vec4 misc; // x = unlit(KHR_materials_unlit,1 = 早退直出 albedo),y = UV 左右镜像,z = UV 上下镜像 + vec4 misc; // x = unlit(KHR_materials_unlit,1 = 早退直出 albedo),y = UV 左右镜像,z = UV 上下镜像,w = 法线绿通道翻转(DirectX) vec4 emissiveFactor; // 自发光颜色(rgb × emissiveMap × surface.z;黑 = 不发光) vec4 uvA[6]; // 每槽 UV 线性变换(KHR_texture_transform,v-up 空间)(a11,a12,a21,a22),槽序 = binding vec4 uvT[6]; // 每槽 UV 平移 (tx, ty, 0, 0) @@ -124,6 +124,8 @@ vec3 MappedNormal(vec3 geomN, vec3 T, vec2 uv) { vec3 nT = normalize(T - N * dot(N, T)); // 正交化 vec3 B = cross(N, nT); vec3 sampled = texture(normalMap, uv).xyz * 2.0 - 1.0; + // DirectX 法线(绿通道朝下)→ OpenGL:翻绿。UE 打包 Specular 资产默认开 + if (matParams.misc.w > 0.5) sampled.y = -sampled.y; if (dot(sampled, sampled) < 1e-4) return geomN; return normalize(nT * sampled.x + B * sampled.y + N * sampled.z); @@ -257,17 +259,21 @@ void main() { vec3 irradiance = texture(irradianceMap, N).rgb; float ao = texture(aoMap, ApplyUv(4, fragUV)).r; - vec3 diffuse = kD_amb * irradiance * albedo * ao; + // 漫反射环境遮蔽:材料 AO × 阴影软化(较温和,保留阴影内可读性) + float diffOcc = ao * mix(0.4, 1.0, shade0); + vec3 diffuse = kD_amb * irradiance * albedo * diffOcc; vec3 R = reflect(-V, N); const float kMaxPrefilterMip = 4.0; // 与 Cubemap::kPrefilterMips - 1 同步 vec3 prefilteredColor = textureLod(prefilteredMap, R, roughness * kMaxPrefilterMip).rgb; vec2 envBRDF = texture(brdfLut, vec2(NdotV, roughness)).rg; - vec3 specular = prefilteredColor * (F_amb * envBRDF.x + envBRDF.y); + // 高光遮蔽(specular occlusion):比漫反射更狠——室内环境高光必须压暗, + // 否则室内表面反射出天空贴图(穿帮根因) + float specOcc = ao * mix(0.12, 1.0, shade0); + vec3 specular = prefilteredColor * (F_amb * envBRDF.x + envBRDF.y) * specOcc; - vec3 ambient = (diffuse + specular) * ubo.ambientColor.rgb - * mix(0.4, 1.0, shade0); // 阴影区环境同步衰减(简化遮蔽) + vec3 ambient = (diffuse + specular) * ubo.ambientColor.rgb; // 不透明段覆盖率恒 1:本 pass 只画 alphaMode=OPAQUE/MASK 表面,像素必 // 被完全覆盖。绝不能拿 albedo 贴图 alpha 当覆盖率——HI3/Genshin 脸部 diff --git a/shaders/pbr/simple_shader_translucent.frag b/shaders/pbr/simple_shader_translucent.frag index 8283bdc..ec638da 100644 --- a/shaders/pbr/simple_shader_translucent.frag +++ b/shaders/pbr/simple_shader_translucent.frag @@ -45,7 +45,7 @@ layout(set = 1, binding = 6) uniform MaterialParams { vec4 factors; // x = metallic 因子,y = roughness 因子,z = 独立粗糙度图开关,w = MASK 镂空开关 vec4 baseColor; // glTF baseColorFactor:rgb 调色 albedo,a = BLEND 透明度 vec4 surface; // x = ior,y = transmission(KHR_materials_transmission),z = 自发光强度,w = 体积厚度 - vec4 misc; // x = unlit(透射材质不会是 unlit,仅为与 simple_shader 布局一致),y = UV 左右镜像,z = UV 上下镜像 + vec4 misc; // x = unlit(透射材质不会是 unlit,仅为与 simple_shader 布局一致),y = UV 左右镜像,z = UV 上下镜像,w = 法线绿通道翻转(DirectX) vec4 emissiveFactor; // 自发光颜色(rgb × emissiveMap × surface.z;黑 = 不发光) vec4 uvA[6]; // 每槽 UV 线性变换(KHR_texture_transform,v-up 空间)(a11,a12,a21,a22),槽序 = binding vec4 uvT[6]; // 每槽 UV 平移 (tx, ty, 0, 0) @@ -123,6 +123,8 @@ vec3 MappedNormal(vec3 geomN, vec3 T, vec2 uv) { vec3 nT = normalize(T - N * dot(N, T)); // 正交化 vec3 B = cross(N, nT); vec3 sampled = texture(normalMap, uv).xyz * 2.0 - 1.0; + // DirectX 法线(绿通道朝下)→ OpenGL:翻绿。UE 打包 Specular 资产默认开 + if (matParams.misc.w > 0.5) sampled.y = -sampled.y; if (dot(sampled, sampled) < 1e-4) return geomN; return normalize(nT * sampled.x + B * sampled.y + N * sampled.z);