From 045cc961720039835ddf76ff1c2c1e3be2a1ac98 Mon Sep 17 00:00:00 2001 From: xcdoge Date: Mon, 12 Oct 2026 23:33:58 +0800 Subject: [PATCH 1/2] =?UTF-8?q?#549=EF=BC=9A=E5=AE=98=E6=96=B9=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E5=81=8F=E6=9A=97=20=E2=80=94=E2=80=94=20=E6=8A=8A?= =?UTF-8?q?=E4=B8=A4=E4=B8=AA=E6=89=8B=E8=B0=83=E5=A2=9E=E7=9B=8A=E6=94=B9?= =?UTF-8?q?=E5=9B=9E=E5=AE=98=E6=96=B9=E8=AF=AD=E4=B9=89=EF=BC=88=E7=9B=B4?= =?UTF-8?q?=E5=B0=84=E5=85=89=20intensity=E3=80=81=E7=83=98=E7=84=99?= =?UTF-8?q?=E5=85=89=E5=BC=BA=20lightRange=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题(#549):3D 棋盘按战场显示官方场景后,整体比官方实机明显偏暗、发闷。只取棋盘区域做量化:官方截图 平均 RGB 152.4/125.8/92.3(亮度 129.0),本项目 98.8/89.4/65.0(亮度 89.7)。 原因:官方场景材质的两个增益是当初照截图手调的猜值(代码注释里自己标了 [ASSUMED]): export const SCENE_LIGHT = Object.freeze({ range: 8.0, ambient: 0.2, direct: 0.8, indirect: 0.4 }); 而官方数据是(从客户端场景包里 dump 出来的真值,见下): scene.json light = { dir: [0.36121, 0.44604, 0.81889], color: [1.0, 0.9312, 0.87131], intensity: 1.1 } Light「Mainlight (1)」: m_Type=1(Directional) m_Intensity=1.1 m_BounceIntensity=1.5 lightRange = 8.0 - direct 0.8 ⇒ 直射光 = lin(color) × (1.1 × 0.8) = 0.88,比官方的 1.1 暗 20%; - indirect 0.4 ⇒ 烘焙间接光被乘成 lightRange × 0.4 = 3.2,而 lightmap 的语义是"贴图 1.0 = lightRange 的线性光" (scenes.py:`L = 5^2.2 · A^2.2 · RGB`,PNG 存 (L / lightRange)^(1/2.2)),所以 indirect 必须为 1 才等于官方烘焙值, 0.4 等于把间接光压掉 60%。片元里的 `pow(lm.rgb, 2.2) * uLmScale` 与 `uLightColor * N·L * shade` 正是这两项。 改动:`SCENE_LIGHT` 的 direct / indirect 改回 1(= 场景自己的值,与那段注释原本的描述一致),并把注释改成 说明它们为什么必须是 1、以及低于 1 会相对官方渲染变暗(#549)。 实测(同一 m04 场景、同一相机,只取棋盘区域求平均): 官方截图 亮度 129.0 改前 亮度 89.7(−30%) 改后 亮度 103.3(−20%) 顺手排除的两个猜想(都量过,都不是原因): - 雾:官方 RenderSettings 是 m_Fog = False,而 scene.js 给场景挂了 T.Fog(0x0c1114, 17, 36)。A/B 实测雾开/关 亮度 95.3 / 95.4 ⇒ 在本视角下没有影响(相机在 near 以内),本次不动它。 - 焦点暗角:官方场景材质也套了棋盘的 focusMask(框外 × uFocusDim = 0.72)。A/B 实测去掉后 103.3 → 106.6(+3%), 因为本视角的焦点框覆盖了大部分棋盘;官方场景本身没有这种衰减,但它是棋盘的既有玩法表现,留待单独确认,本次不改。 证据来源:`.cache/official-scenes//scenes/.../level_.ab` 用 UnityPy(配合 tools/local-extract/aklz4.py 注册的 LZ4AK 解码器)dump 出的 Light 与 RenderSettings;以及仓库自己的 tools/local-extract/scenes.py 里 lightmap 的 RGBM 编码定义。 测试:node --test test/render/board3d-scene.test.js test/render/board3d.test.js test/render/boardart.test.js(见 PR)。 --- public/js/render/board3d/materials.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/js/render/board3d/materials.js b/public/js/render/board3d/materials.js index 79ec86c9..750e15b8 100644 --- a/public/js/render/board3d/materials.js +++ b/public/js/render/board3d/materials.js @@ -349,9 +349,11 @@ export function smogMaterial(THREE, tex, focus) { * not reproduced: the albedo is lit by the baked indirect light of the scene's lightmap plus the scene's key light * masked by its baked shadow, in linear light (Unity's Shadowmask lighting mode). `range` — the linear light a lightmap * texel of 1.0 stands for when scene.json has no lightRange; [ASSUMED] `ambient` — the light of a mesh without a - * lightmap page; `direct` / `indirect` — gains of the two terms (1 = the scene's own values). + * lightmap page; `direct` / `indirect` — gains of the two terms, both 1: the scene's own values (the key light's + * `light.intensity`, and the lightmap's baked linear light, whose texel is (L / lightRange)^(1/2.2)). Anything below 1 + * darkens the board against the official render (issue #549). */ -export const SCENE_LIGHT = Object.freeze({ range: 8.0, ambient: 0.2, direct: 0.8, indirect: 0.4 }); +export const SCENE_LIGHT = Object.freeze({ range: 8.0, ambient: 0.2, direct: 1, indirect: 1 }); const colorOf = (info, key, fallback = [1, 1, 1, 1]) => { const c = info && info.colors && Array.isArray(info.colors[key]) ? info.colors[key] : fallback; From 80abaaf1b159ccdcae68252ba1296d77badb05e1 Mon Sep 17 00:00:00 2001 From: xcdoge Date: Mon, 12 Oct 2026 23:52:31 +0800 Subject: [PATCH 2/2] =?UTF-8?q?#549=EF=BC=9A=E6=A3=8B=E7=9B=98=E6=9D=90?= =?UTF-8?q?=E8=B4=A8=E7=9A=84=E8=87=AA=E5=8F=91=E5=85=89=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E5=AE=98=E6=96=B9=20=5FEmissionColor=EF=BC=880.25=20=E2=86=92?= =?UTF-8?q?=201=EF=BC=89=EF=BC=8C=E6=A3=8B=E7=9B=98=E4=BA=AE=E5=BA=A6?= =?UTF-8?q?=E5=86=8D=20+4%?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 接上一提交(官方场景的两个增益改回 1)之后,剩下的偏暗还有一处同类问题:棋盘瓦片材质的自发光被手调压暗了。 官方真值(UnityPy + tools/local-extract/aklz4.py 的 LZ4AK 解码器 dump 客户端 arts/maps/map_autochess/res.ab 的 Material): MT_autochess _Color = (1,1,1,1) _EmissionColor = (1,1,1,1) _MainTex = TX_autochessi_D _EmissionMap = TX_autochessi_E _MetallicGlossMap = TX_autochessi_M _BumpMap = TX_autochessi_N _Metallic = 0 _Glossiness = 0 MT_autochess_common _EmissionColor = 0.5588(同样带 _EmissionMap) MT_autochessi_BG _Color = 0.71323 + shader Torappu/Unlit/Texture ⇒ 与代码里的 LIGHTING.bg.color = 0.713 完全一致(这条本来是对的) 也就是说官方棋盘是**自发光**的(albedo 加 emission),不是靠环境灯照亮;而 materials.js 里 boardMaterial 的 emissive 默认就是 1(emissive / emissiveMap 直接对应上面两个材质),decalMaterial(_common 图集)也已经是 官方的 0.559 —— 只有 scene.js 的 LIGHTING.emissive = 0.25 把它整体压到四分之一。 改动:LIGHTING.emissive 0.25 → 1,注释写明官方 _EmissionColor 的来源与它为什么必须是 1(与上一提交那两个 增益同一类问题:照截图手调的系数)。 实测(同一 m04 场景、同一相机,只取棋盘区域求平均,官方截图同口径): 官方截图 亮度 129.0 最初 亮度 89.7(−30%) 场景增益改回 1 亮度 103.3(−20%) 本提交 亮度 107.4(−17%) 已知未尽:还剩约 −17%。官方棋盘瓦片的着色器是自定义的(dump 里 m_Shader 读不到完整定义),无法直接比对公式, 下一步需要把画面按材质逐区域分离(棋盘瓦片 / 场景沙地 / 水)再分别对齐,而不是继续拧全局系数。 --- public/js/render/board3d/scene.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/js/render/board3d/scene.js b/public/js/render/board3d/scene.js index 502b7806..50afc8ce 100644 --- a/public/js/render/board3d/scene.js +++ b/public/js/render/board3d/scene.js @@ -37,7 +37,12 @@ export const LIGHTING = Object.freeze({ key: { color: 0xfff1df, intensity: 2.6, dir: [-5.2, -3.4, 10] }, hemi: { sky: 0xe4ecf4, ground: 0x4a5058, intensity: 0.6 }, env: 1.0, - emissive: 0.25, + // MT_autochess._EmissionColor = (1,1,1) and MT_autochess_common._EmissionColor = 0.5588, both with an + // _EmissionMap — the official board is emissive, not lamp-lit. boardMaterial's own default is 1 (its emissive / + // emissiveMap come straight from those two materials); LIGHTING.emissive used to scale it down to 0.25, which is + // what made our board read darker than the official render (issue #549). decalMaterial (the _common atlas) keeps + // the official 0.559 of its own. + emissive: 1, roughness: 0.78, shadow: { size: 2048, radius: 3, bias: -0.0004, normalBias: 0.025 }, exposure: 1.0,