From 7f3fe36da9022e18d7d126eb00a7e20a787e2df1 Mon Sep 17 00:00:00 2001 From: WuYongJie Date: Wed, 13 May 2026 16:35:43 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E6=9D=90=E8=B4=A8?= =?UTF-8?q?=E9=87=91=E5=B1=9E=E5=BA=A6=E6=B8=B2=E6=9F=93=E8=A1=A8=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MaterialLibraryClient.tsx | 12 +++- .../engine-highlight-resources.test.ts | 20 ++++++ .../lib/3d/__tests__/material-manager.test.ts | 46 +++++++++++++ .../src/lib/3d/engine-highlight-resources.ts | 69 +------------------ .../src/lib/3d/material-manager.ts | 32 ++++++++- .../src/lib/3d/material-pbr-presets.ts | 2 +- 6 files changed, 111 insertions(+), 70 deletions(-) create mode 100644 furniture-3d-system/src/lib/3d/__tests__/engine-highlight-resources.test.ts create mode 100644 furniture-3d-system/src/lib/3d/__tests__/material-manager.test.ts diff --git a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx index 0e6878b7..45ece190 100644 --- a/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx +++ b/furniture-3d-system/src/app/asset-center/material-library/MaterialLibraryClient.tsx @@ -255,6 +255,7 @@ function FinishWorkbenchPreviewCard({ form }: { form: FinishForm }) { const textureSize = imageUrl ? `${Math.max(72, Math.round((form.textureRepeat || 1) * 96))}%` : '100% 100%' + const metalHighlightOpacity = Math.min(0.5, Math.max(0, form.metalness * (1 - form.roughness * 0.55))) return (
@@ -283,9 +284,18 @@ function FinishWorkbenchPreviewCard({ form }: { form: FinishForm }) { backgroundSize: `100% 100%, 100% 100%, ${textureSize}`, backgroundPosition: 'center', opacity: Math.max(0.35, Math.min(1, form.opacity)), - filter: `saturate(${1 + form.metalness * 0.18}) contrast(${1 + form.metalness * 0.14}) brightness(${1 - form.roughness * 0.04})`, + filter: `brightness(${1 - form.roughness * 0.04})`, }} > +
diff --git a/furniture-3d-system/src/lib/3d/__tests__/engine-highlight-resources.test.ts b/furniture-3d-system/src/lib/3d/__tests__/engine-highlight-resources.test.ts new file mode 100644 index 00000000..cc48e04b --- /dev/null +++ b/furniture-3d-system/src/lib/3d/__tests__/engine-highlight-resources.test.ts @@ -0,0 +1,20 @@ +import * as THREE from "three"; +import { normalizeTooBrightMaterial } from "@/lib/3d/engine-highlight-resources"; + +describe("normalizeTooBrightMaterial", () => { + it("保留材质编辑器写入的金属度、粗糙度和颜色", () => { + const material = new THREE.MeshStandardMaterial({ + color: "#f2f2f2", + metalness: 0.92, + roughness: 0.12, + envMapIntensity: 1.4, + }); + + normalizeTooBrightMaterial(material); + + expect(material.metalness).toBeCloseTo(0.92); + expect(material.roughness).toBeCloseTo(0.12); + expect(material.envMapIntensity).toBeCloseTo(1.4); + expect(material.color.getHexString()).toBe("f2f2f2"); + }); +}); diff --git a/furniture-3d-system/src/lib/3d/__tests__/material-manager.test.ts b/furniture-3d-system/src/lib/3d/__tests__/material-manager.test.ts new file mode 100644 index 00000000..4e27b48a --- /dev/null +++ b/furniture-3d-system/src/lib/3d/__tests__/material-manager.test.ts @@ -0,0 +1,46 @@ +import * as THREE from "three"; +import { MaterialManager, type MaterialConfig } from "@/lib/3d/material-manager"; + +function createMaterialConfig(overrides: Partial = {}): MaterialConfig { + return { + id: "material-metal", + name: "测试金属", + categoryId: "category-metal", + categoryName: "金属", + thumbnailUrl: null, + textureUrl: null, + normalMapUrl: null, + roughnessMapUrl: null, + metalnessMapUrl: null, + aoMapUrl: null, + color: "#c9ccd0", + metalness: 0.92, + roughness: 0.22, + opacity: 1, + textureRepeat: 1, + textureRotation: 0, + textureSizeMm: 600, + ...overrides, + }; +} + +describe("MaterialManager", () => { + it("高金属度材质默认获得更强环境反射", () => { + const material = MaterialManager.getInstance().createImmediateMaterial( + createMaterialConfig(), + ); + + expect(material).toBeInstanceOf(THREE.MeshStandardMaterial); + expect(material.metalness).toBeCloseTo(0.92); + expect(material.roughness).toBeCloseTo(0.22); + expect(material.envMapIntensity).toBeCloseTo(1.65); + }); + + it("显式环境反射强度优先于金属默认值", () => { + const material = MaterialManager.getInstance().createImmediateMaterial( + createMaterialConfig({ envMapIntensity: 1.18 }), + ); + + expect(material.envMapIntensity).toBeCloseTo(1.18); + }); +}); diff --git a/furniture-3d-system/src/lib/3d/engine-highlight-resources.ts b/furniture-3d-system/src/lib/3d/engine-highlight-resources.ts index 51b81824..f22a4341 100644 --- a/furniture-3d-system/src/lib/3d/engine-highlight-resources.ts +++ b/furniture-3d-system/src/lib/3d/engine-highlight-resources.ts @@ -400,73 +400,8 @@ export function normalizeTooBrightMaterial( emissiveIntensity?: number; }; - if (colorLikeMaterial.color instanceof THREE.Color) { - const originalColor = colorLikeMaterial.color.clone(); - const maxChannel = Math.max( - originalColor.r, - originalColor.g, - originalColor.b, - ); - if (maxChannel > 1) { - colorLikeMaterial.color.multiplyScalar(1 / maxChannel); - } - - const r = colorLikeMaterial.color.r; - const g = colorLikeMaterial.color.g; - const b = colorLikeMaterial.color.b; - const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b; - const chroma = Math.max(r, g, b) - Math.min(r, g, b); - const isNearNeutral = chroma < 0.22; - if (isNearNeutral && luminance > 0.38) { - const grayStrength = THREE.MathUtils.clamp((luminance - 0.38) / 0.62, 0, 1); - const hasColorMap = Boolean(colorLikeMaterial.map); - const nearWhiteGray = hasColorMap - ? 0.46 - grayStrength * 0.08 - : 0.4 - grayStrength * 0.1; - const grayTarget = new THREE.Color().setScalar(nearWhiteGray); - colorLikeMaterial.color.lerp(grayTarget, hasColorMap ? 0.92 : 0.98); - if (typeof colorLikeMaterial.roughness === "number") { - colorLikeMaterial.roughness = Math.max( - colorLikeMaterial.roughness, - 0.95, - ); - } - if (typeof colorLikeMaterial.envMapIntensity === "number") { - colorLikeMaterial.envMapIntensity = Math.min( - colorLikeMaterial.envMapIntensity, - 0.03, - ); - } - if (typeof colorLikeMaterial.metalness === "number") { - colorLikeMaterial.metalness = Math.min(colorLikeMaterial.metalness, 0.02); - } - if ( - colorLikeMaterial.emissive instanceof THREE.Color && - typeof colorLikeMaterial.emissiveIntensity === "number" - ) { - colorLikeMaterial.emissive.setScalar(0); - colorLikeMaterial.emissiveIntensity = Math.min( - colorLikeMaterial.emissiveIntensity, - 0.01, - ); - } - } - } - - if (typeof colorLikeMaterial.roughness === "number") { - colorLikeMaterial.roughness = THREE.MathUtils.clamp( - colorLikeMaterial.roughness, - 0.25, - 1, - ); - } - if (typeof colorLikeMaterial.metalness === "number") { - colorLikeMaterial.metalness = THREE.MathUtils.clamp( - colorLikeMaterial.metalness, - 0, - 0.5, - ); - } + // 材质编辑器的物理参数要在 3D 世界里按设计师填写值生效。 + // 这里不再归一化 color / roughness / metalness / envMapIntensity。 if (colorLikeMaterial.map) { colorLikeMaterial.map.colorSpace = THREE.SRGBColorSpace; diff --git a/furniture-3d-system/src/lib/3d/material-manager.ts b/furniture-3d-system/src/lib/3d/material-manager.ts index be1c0a8b..d69931fa 100644 --- a/furniture-3d-system/src/lib/3d/material-manager.ts +++ b/furniture-3d-system/src/lib/3d/material-manager.ts @@ -42,6 +42,36 @@ export interface MaterialCategoryWithMaterials { type RuntimeMaterial = THREE.MeshStandardMaterial | THREE.MeshPhysicalMaterial +function isMetalLikeMaterial(config: MaterialConfig): boolean { + const text = `${config.categoryName ?? ''} ${config.name}`.toLowerCase() + return ( + config.metalness >= 0.65 || + text.includes('金属') || + text.includes('不锈钢') || + text.includes('钢') || + text.includes('铝') || + text.includes('铜') || + text.includes('铁') || + text.includes('metal') || + text.includes('steel') || + text.includes('aluminum') || + text.includes('brass') + ) +} + +function resolveEnvMapIntensity(config: MaterialConfig, options: { isGlassLikeMaterial: boolean }): number { + if (typeof config.envMapIntensity === 'number') { + return config.envMapIntensity + } + if (options.isGlassLikeMaterial) { + return 1.2 + } + if (isMetalLikeMaterial(config)) { + return 1.65 + } + return 1 +} + export class MaterialManager { private static instance: MaterialManager | null = null private materialCache: Map = new Map() @@ -135,7 +165,7 @@ export class MaterialManager { depthWrite: safeOpacity >= 1, }) - material.envMapIntensity = resolvedConfig.envMapIntensity ?? (isGlassLikeMaterial ? 1.2 : 1) + material.envMapIntensity = resolveEnvMapIntensity(resolvedConfig, { isGlassLikeMaterial }) material.userData = { ...material.userData, textureSizeMm: resolvedConfig.textureSizeMm ?? 600, diff --git a/furniture-3d-system/src/lib/3d/material-pbr-presets.ts b/furniture-3d-system/src/lib/3d/material-pbr-presets.ts index 76e383f4..d7697790 100644 --- a/furniture-3d-system/src/lib/3d/material-pbr-presets.ts +++ b/furniture-3d-system/src/lib/3d/material-pbr-presets.ts @@ -164,7 +164,7 @@ const TEXTURE_LIBRARY = { const CATEGORY_DEFAULT_PRESETS: Record = { wood: { metalness: 0, roughness: 0.62, opacity: 1, textureRepeat: 1.8, envMapIntensity: 0.55 }, solid: { metalness: 0, roughness: 0.42, opacity: 1, textureRepeat: 1, envMapIntensity: 0.4 }, - metal: { metalness: 1, roughness: 0.28, opacity: 1, textureRepeat: 1.3, envMapIntensity: 1.25 }, + metal: { metalness: 1, roughness: 0.28, opacity: 1, textureRepeat: 1.3, envMapIntensity: 1.65 }, glass: { metalness: 0, roughness: 0.08,