與主題體系詳解:從表面角色到場景主題的完整著色管線)
Pascal 3D 編輯器材質(zhì)與主題體系詳解從表面角色到場景主題的完整著色管線【免費(fèi)下載鏈接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.項目地址: https://gitcode.com/GitHub_Trending/editor93/editor本文檔基于wiki/architecture/materials-and-themes.md編寫結(jié)合packages/viewer、packages/core、packages/nodes等源碼展開。文中提到的文件路徑均相對倉庫根目錄。導(dǎo)讀本指南系統(tǒng)講解開源 3D 建筑編輯器 Pascal當(dāng)前倉庫中表面顏色surface colour的完整機(jī)制從節(jié)點(diǎn)如何聲明表面角色surfaceRole、顏色如何被解析與緩存到四種顏色預(yù)設(shè)colorPreset與十余種場景主題sceneTheme如何正交疊加、以及自定義網(wǎng)格Block的面級材質(zhì)槽Slots與紋理世界尺度UV 以米為單位約定。讀完本文你將掌握 Pascal 查看器packages/viewer中shading、textures、colorPreset、sceneTheme、shadows、edges六大外觀軸的全部含義與協(xié)作方式理解無紋理表面永遠(yuǎn)使用主題化角色顏色這一核心規(guī)則并能獨(dú)立為倉庫新增一個場景主題或為自定義網(wǎng)格接入MaterialRef材質(zhì)系統(tǒng)。一、外觀軸The axes六個正交的外觀狀態(tài)Pascal 把節(jié)點(diǎn)外觀拆成一組互相正交的狀態(tài)軸全部集中保存在查看器的useViewerstore 中packages/viewer/src/store/use-viewer.ts狀態(tài)取值控制內(nèi)容shadingsolid \| renderedsolidMeshLambertNodeMaterial無 SSGI/AOrenderedMeshStandardNodeMaterial SSGI/AO屏幕空間全局光照/環(huán)境光遮蔽texturesboolean擁有真實(shí)材質(zhì)/預(yù)設(shè)的表面是否顯示其紋理貼圖colorPresetclay \| white \| mono \| blueprint無紋理表面的按角色基礎(chǔ)調(diào)色板sceneTheme主題 idstudio、mediterranean、night、verdant等光照 背景 地面 按角色的顏色色調(diào)詳見場景主題shadowsboolean平行光投影常開的 key light見 packages/viewer/src/components/viewer/lights.tsxedgesoff \| soft \| strong屏幕空間墨水描邊在post-processing.tsx中處理lib/ink-edges.ts從 use-viewer.ts 的狀態(tài)定義可以看到shading、textures、colorPreset等字段均帶對應(yīng)的setXxxsetter且shadingByContext讓編輯器editor默認(rèn)使用solid、社區(qū)查看器viewer默認(rèn)使用rendered——兩者可以有不同的默認(rèn)著色模式。關(guān)鍵設(shè)計shading/textures/colorPreset按上下文context持久化而shadingByContext正是PartialRecordRenderContext, RenderShading類型RenderContext editor | viewer即同一場景在編輯器與查看器里可以分別記住各自的著色偏好。二、表面角色Surface rolesCore 只存令牌不存顏色core包為每個注冊表種類registry kind提供一個可選的表面角色令牌聲明在其NodeDefinition上packages/core/src/registry/types.tssurfaceRole?: wall | floor | ceiling | roof | joinery | glazing | furnishing這一設(shè)計非??酥芻ore只存儲令牌字符串不攜帶任何顏色也從不 import three.js。顏色解析完全發(fā)生在查看器層。令牌的意義在于墻wall、樓板slab、柱column等不同種類可以通過各自的surfaceRole從同一份調(diào)色板里解析出不同的顏色——同一個clay預(yù)設(shè)墻是#dcd6c7屋頂是#b8ad96玻璃是#c8d4dc。從源碼可見SurfaceRole類型被packages/viewer、packages/core多處引用是整個材質(zhì)體系的最小契約單元。三、解析顏色單一事實(shí)來源與緩存鍵顏色解析的唯一事實(shí)來源在 packages/viewer/src/lib/materials.tsresolveSurfaceColor(role, colorPreset, sceneThemeId?) // getSceneTheme(sceneThemeId).clayTints?.[role] // theme override, if any // ?? PRESET_PALETTES[colorPreset][role] // else the preset palette實(shí)際實(shí)現(xiàn)materials.ts為export function resolveSurfaceColor( role: SurfaceRole, preset: ColorPreset, sceneThemeId?: string, ): string { // 主題可以按角色覆蓋顏色例如地中海主題的藍(lán)色屋頂未覆蓋時回退到所選顏色預(yù)設(shè)的調(diào)色板。 const tints sceneThemeId ? getSceneTheme(sceneThemeId).clayTints : undefined return tints?.[role] ?? (PRESET_PALETTES[preset] ?? CLAY_PALETTE)[role] }3.1 四種顏色預(yù)設(shè)的真實(shí)色值四個預(yù)設(shè)分別定義在 materials.ts 中每個都覆蓋全部 7 種表面角色clay黏土灰| 角色 | 色值 | |---|---| | wall |#dcd6c7| | floor |#cfc8b6| | ceiling |#e4ded0| | roof |#b8ad96| | joinery |#c4bba6| | glazing |#c8d4dc| | furnishing |#d2ccbe|white白色——源碼注釋特別說明albedo 被鉗制在約 0.83 線性值最大通道#eb因為真實(shí)白漆反射率約 80%純白 albedo 會殺死 GI/陰影對比度 wall#ebeae6、floor#e7e4dd、ceiling#ebeae6、roof#dedbd2、joinery#e5e2d9、glazing#dbe8ee、furnishing#e9e7e1。mono單色灰wall#c8c8c8、floor#b8b8b8、ceiling#d8d8d8、roof#9a9a9a、joinery#adadad、glazing#c2cbd0、furnishing#c0c0c0。blueprint藍(lán)圖藍(lán)wall#90a9c7、floor#7f98ba、ceiling#aec0d8、roof#5f789b、joinery#6f86a8、glazing#b6d7ea、furnishing#8ba2bf。3.2createSurfaceRoleMaterial與緩存鍵createSurfaceRoleMaterial(role, colorPreset, side?, sceneThemeId?)把解析出的顏色包裝成一個受光照的MeshLambertNodeMaterialmaterials.ts并按role-preset-side-sceneTheme組合做緩存const cacheKey ${role}-${preset}-${resolvedSide}-${sceneThemeId ?? base}緩存鍵正是每個消費(fèi)方都必須把sceneTheme一路傳下來的根本原因——否則切換主題時會命中舊主題的陳舊緩存材質(zhì)。另外兩個實(shí)現(xiàn)細(xì)節(jié)值得注意glazing 特殊處理玻璃角色強(qiáng)制使用FrontSiderole glazing ? THREE.FrontSide : ...且depthWrite: false、opacity: 0.25、transparent: true。原因是 MRT scenePassSSGI 的 diffuseColor/normal 目標(biāo)中任何DoubleSideNodeMaterial 都會觸發(fā) WebGPU 渲染管線校驗失敗back-face 變體缺少 MRT 輸出報錯 Color target has no corresponding fragment stage output 并污染整個渲染上下文。需要雙面可見時應(yīng)把宿主網(wǎng)格旋轉(zhuǎn) 180° 讓 FrontSide 朝向觀察者。所有緩存材質(zhì)都打上userData.__pascalCachedMaterial true標(biāo)記供幾何重建時區(qū)分共享緩存材質(zhì)與節(jié)點(diǎn)私有材質(zhì)。3.3 材質(zhì)緩存與紋理加載管線packages/viewer/src/lib/materials.ts 還維護(hù)了四類緩存materialCache預(yù)設(shè)/普通材質(zhì)、defaultMaterialCache默認(rèn)色材質(zhì)、surfaceRoleMaterialCache角色材質(zhì)、textureCache紋理。紋理支持.ktx2格式通過共享的 KTX2Loader 轉(zhuǎn)碼支持在 viewer 初始化時檢測一次與普通圖片兩種加載路徑且wrapS/wrapT/repeat/rotation/flipY等貼圖屬性均來自MaterialMapProperties見 packages/core/src/material-library.ts 中每個目錄項的mapProperties。四、核心規(guī)則兩種模式下無紋理表面都用主題色這是整個外觀體系最重要的一條規(guī)則對于沒有聲明插槽默認(rèn)值slot defaults的種類有紋理僅當(dāng)該節(jié)點(diǎn)顯式帶有materialPreset或material時才成立textures關(guān)閉→ 每個表面都使用resolveSurfaceColor(role, …)textures開啟→ 有紋理的表面顯示紋理無紋理表面仍然使用resolveSurfaceColor而不是硬編碼的白色/灰色默認(rèn)值。因此選擇地中海Mediterranean主題會得到藍(lán)色屋頂 暖色墻而且完全不需要動textures開關(guān)。系統(tǒng)不存在全白模式——無紋理永遠(yuǎn)意味著主題化的角色顏色。4.1 各種類的接入位置種類角色顏色應(yīng)用位置wallpackages/viewer/src/systems/wall/wall-materials.tsgetMaterialsForWall每幀由wall-cutout.tsx重新應(yīng)用roof / roof-segmentpackages/viewer/src/systems/roof/roof-materials.tsgetRoofMaterialArrayslabpackages/nodes/src/slab/geometry.tsgetSlabSlotMaterialceilingpackages/nodes/src/ceiling/renderer.tsx通用注冊表種類packages/viewer/src/systems/geometry/geometry-system.tsx →applyDefaultSurfaceRoletextures 關(guān)閉時door / windowpackages/viewer/src/systems/door/door-system.tsx / window-system.tsxstair / column / item / elevatorpackages/nodes/kind/renderer.tsx每個接入點(diǎn)都會從useViewer讀取shading/textures/colorPreset/sceneTheme或從GeometrySystem線程化傳入并且必須把sceneTheme放進(jìn)材質(zhì)緩存鍵和重建依賴數(shù)組中否則切換主題不會重新著色。4.2 GeometrySystem 的聯(lián)動機(jī)制geometry-system.tsx 用useEffect把外觀狀態(tài)作為故意的重建觸發(fā)器shading、textures、colorPreset、sceneTheme任一變化都會把所有聲明了def.geometry的節(jié)點(diǎn)重新標(biāo)記為 dirty從而觸發(fā)幾何重建并取用新外觀。源碼中注釋明確說明這四個值是re-run TRIGGERS而非函數(shù)體讀取值刪除它們會靜默破壞外觀模式切換。同時def.geometryKey機(jī)制把全局渲染輸入折疊進(jìn)緩存鍵geometry-system.tsxconst builtKey ${shading}|${textures}|${colorPreset}|${sceneTheme}|${def.geometryKey(effectiveNode)}|${childLiveOverrideKey}這樣主題/著色變化永遠(yuǎn)不會被geometryKey的輸入未變則跳過重建邏輯誤跳過而當(dāng)!textures def.surfaceRole時系統(tǒng)調(diào)用applyDefaultSurfaceRole(built, def.surfaceRole, colorPreset, sceneTheme)第 234-236 行統(tǒng)一給通用幾何體套上角色顏色。4.3 天花板與樓板的插槽默認(rèn)值細(xì)節(jié)天花板和樓板在帶色textures開啟模式下使用聲明的插槽默認(rèn)值declared slot defaults。實(shí)現(xiàn)上有幾點(diǎn)工程細(xì)節(jié)見 geometry-system.tsx 與 slab 幾何代碼天花板底面在兩種外觀下都使用不透明的BackSide材質(zhì)只有ceiling-grid做混合blend。樓板頂面、側(cè)面/底面與可選的地形裙邊terrain skirt網(wǎng)格可以分別批量處理batch。扁平插槽默認(rèn)值按顏色、粗糙度與著色模式共享 viewer 緩存slab 的舊版緩存材質(zhì)攜帶__pascalCachedMaterial標(biāo)記使幾何重建后共享材質(zhì)仍存活。透明的插槽覆蓋slot overrides自己繪制自己不參與共享緩存。五、自定義網(wǎng)格Block的面級材質(zhì)MaterialRef 與 SlotsBlock自定義網(wǎng)格通過穩(wěn)定的、用戶可命名的對象插槽復(fù)用MaterialRef模型BlockNode.slots把插槽 ID 映射到scene:或library:材質(zhì)引用slotNames存儲用戶可編輯的插槽標(biāo)簽每個BlockFace.materialSlot存儲一個插槽 IDbody是永久基礎(chǔ)插槽也是未綁定/未解析插槽的回退目標(biāo)。幾何構(gòu)建器為每個拓?fù)涿鎡opology face生成一個 Three.js group并按節(jié)點(diǎn)穩(wěn)定的插槽 ID 順序生成材質(zhì)數(shù)組渲染材質(zhì)順序發(fā)布在userData.slotIds每個面的頂點(diǎn)范圍記錄在geometry.userData.blockFaces。5.1 繪制能力Paint的命中映射Paint 工具重新對網(wǎng)格做射線檢測raycast把命中三角形通過這些頂點(diǎn)范圍映射到穩(wěn)定的拓?fù)涿?ID因此預(yù)覽與提交只影響該面。每個面的 UV 保留下文世界尺度投影契約。5.2 插槽交互編輯器中的 Slots 集合Block 檢查器把這一集合稱為Slots。交互規(guī)則用戶可重命名插槽Paint 工具通過可復(fù)用的 scene-material 數(shù)據(jù)塊改變插槽材質(zhì)。編輯模式下選中一個或多個面后點(diǎn)擊某個插槽立即把那些面綁定到該插槽——沒有單獨(dú)的 Assign / Select / Deselect 按鈕行。在已選面的情況下添加插槽同一場景更新中創(chuàng)建插槽、綁定那些面、并分配一個明顯不同的生成強(qiáng)調(diào)材質(zhì)accent material——這樣在用戶選定最終涂裝材質(zhì)之前新表面在編輯模式和渲染模型中都能肉眼可見。無選中面時 Add Slot 是 no-op避免產(chǎn)生不可見的無用插槽。刪除非 body 插槽在同一節(jié)點(diǎn)更新中把所有已分配面重新映射回bodybody成為激活的賦值來源可復(fù)用的 scene/library 材質(zhì)仍可供其他節(jié)點(diǎn)使用。5.3 全局 Paint 工具與拓?fù)洳僮鞣拇_定性全局 Paint 工具解析命中面的已分配插槽改變該插槽的材質(zhì)綁定。全新網(wǎng)格的每個面都綁定到body所以第一次涂裝會更新整個網(wǎng)格一旦面被分配到命名插槽涂裝其中任意面都會更新使用該插槽的所有面。一次性材質(zhì)在創(chuàng)建可復(fù)用 scene 材質(zhì)前會先復(fù)用結(jié)構(gòu)匹配的 scene 材質(zhì)。擦除Erase清除插槽綁定body回到墻角色默認(rèn)值其他未綁定插槽回退到body。拓?fù)洳僮鞣3仲x值確定性保留與變換的面保持其插槽擠出蓋/側(cè)、內(nèi)縮蓋/環(huán)繼承源面環(huán)切l(wèi)oop-cut片段繼承被切分的面倒角帶bevel bands與混合材質(zhì)溶解使用穩(wěn)定topology.faces順序中的第一個相鄰面刪除使用某插槽的最后一個面不會刪除其可復(fù)用材質(zhì)。5.4 外部插件渲染器的接入契約插件渲染器通過公開的pascal-app/viewer表面遵循同樣的四個軸。對導(dǎo)入的層級結(jié)構(gòu)先一次性捕獲其作者材質(zhì)再響應(yīng)式地應(yīng)用以下映射宿主狀態(tài)導(dǎo)入材質(zhì)帶色 Rendered作者材質(zhì)帶色 Solid緩存 Lambert 變體保留顏色、albedo 貼圖、alpha 與插槽MonochromecreateSurfaceRoleMaterial(surfaceRole, colorPreset, side, sceneTheme)適配器屬于插件渲染器——因為它擁有層級結(jié)構(gòu)知道哪些表面是 furnishing、glazing 還是其他角色。材質(zhì)交換發(fā)生在偏好變化時絕不在useFrame中。銷毀 loader 擁有的層級結(jié)構(gòu)前先恢復(fù)作者材質(zhì)只銷毀插件擁有的變體宿主緩存的角色材質(zhì)保持不動。另外描邊edges與昂貴的渲染管線不需要插件材質(zhì)鉤子——它們是覆蓋SCENE_LAYER的屏幕空間宿主通道。放置幽靈placement ghosts應(yīng)使用編輯器疊加層overlay layer保持清晰且不進(jìn)入場景深度/法線目標(biāo)。六、場景主題Scene themes一個SceneThemepackages/viewer/src/lib/scene-themes.ts把定義一個look所需的一切打包字段驅(qū)動appearance: light \| dark2D 場景 chrome——畫布背景、網(wǎng)格線顏色、測量標(biāo)簽/光標(biāo)對比度沒有獨(dú)立的淺/深色開關(guān)主題擁有這一切background3D 背景在 packages/viewer/src/components/viewer/post-processing.tsx 中與無幾何處混合backgroundSky?可選的天頂顏色后處理管線渲染從該色頂部到background地平線的垂直屏幕空間漸變省略則用純backgroundground場地地面填充nodes/site/renderer.tsx與無限地面遮擋平面viewer/ground-occluder.tsx。與background分離使深色主題得到受光照的中調(diào)地面而非近黑lights/ambient/hemi燈光裝置lights.tsx一盞 key light 投射陰影toneMappingExposure渲染器曝光clayTints?每SurfaceRole的顏色覆蓋疊加在colorPreset之上編輯器 UI chrome 永遠(yuǎn)是深色的固定document.body.classList.add(dark)與appearance無關(guān)。6.1 內(nèi)置主題速查倉庫內(nèi)置 9 個主題scene-themes.tsid名稱appearancebackgroundground代表性 clayTintsstudioStudiolight#fbfbfa#e9e7e2wall#e9e5db/ roof#c4bba6paperPaperlight#ede9df#e7e1d3wall#efe9da/ roof#b9b09asunsetSunsetlight#f6e8d4#ecd9bfwall#f3e3cf/ roof#a6764fovercastOvercastlight#e6e7e6#dadcd9wall#dedfdc/ roof#a3a49eblueprintBlueprintlight#dde6ef#c9d6e6wall#9fb6d2/ roof#5f789bmediterraneanMediterraneanlight#bdd6e8#ddd2bbwall#f6f1e6/roof#3e6585藍(lán)twilightTwilightdark#3a3550#67618awall#c5b9cf/ roof#5b4f74nightNightdark#1f2433#4a5470wall#aab3c6/ roof#5b6680verdantVerdantlight#d6e4d2#c7d6b4wall#eef0e6/ roof#6f8a5a主題查找函數(shù)getSceneTheme(id)未命中時回退到SCENE_THEMES[0]即studioSCENE_THEME_IDS導(dǎo)出全部 id 供 UI 使用。6.2 添加一個主題把SceneTheme追加到SCENE_THEMES數(shù)組并填齊所有必填字段即可。clayTints是Partial類型——省略的角色自動回退到當(dāng)前colorPreset。主題選擇器工具欄 社區(qū) overlay會基于clayTints疊加在background上渲染 2×2 色塊因此至少填充wall/roof/floor/glazing才能得到像樣的預(yù)覽色塊。七、紋理世界尺度UV 以米為單位每一個程序化表面生成的 UV 都以米為單位1 個 UV 單位 1 米。這是一條全局契約參與方包括wallpackages/viewer/src/systems/wall/wall-system.tsxExtrudeGeometryslabpackages/viewer/src/systems/slab/slab-system.tsxgeneratePositiveSlabGeometry、generatePoolGeometryceilingpackages/viewer/src/systems/ceiling/ceiling-system.tsxroofpackages/viewer/src/systems/roof/roof-system.tsxchimney / dormerpackages/nodes/src/chimney/geometry.tsGLB 物品插槽遵循同樣的約 1 UV 單位/米作者約定由插槽驗證器的 UV 存在性檢查和 item-authoring 中的 Blender 配方強(qiáng)制保證。這是作者要求不是渲染期修正。因此目錄材質(zhì)catalog material的repeatmapProperties.repeatX/repeatY見 packages/core/src/material-library.ts就是一個按材質(zhì)的全局尺度設(shè)置每米瓷磚數(shù)repeat: 1→ 1 塊/米repeat: 0.4→ 每 2.5 米一塊repeat: 1.5→ 1.5 塊/米。repeat是材質(zhì)的屬性對使用它的每個表面都相同永遠(yuǎn)不是按物品或按表面的。自定義 repeat 值是有意為之的材質(zhì)尺度而非逐表面的 hack。在材質(zhì)庫實(shí)現(xiàn)中MaterialCatalogItem攜帶preset: MaterialPresetPayload其mapProperties內(nèi)含完整的color、roughness、metalness、repeatX/repeatY、rotation、wrapS/wrapT、normalScaleX/Y、emissiveColor/Intensity、displacementScale、transparent、opacity、side等參數(shù)packages/core/src/material-library.ts。查看器側(cè)的 materials.tsapplyMaterialMapProperties會把這些參數(shù)逐一映射到 three.js 材質(zhì)roughness、metalness、displacementScale、bumpScale、aoMapIntensity、lightMapIntensity、normalScale、emissive、opacity、side等并同步wrapS/wrapT/repeat/rotation/flipY貼圖屬性。渲染時紋理的 repeat 由resolveTextureRepeat(repeat, scale)解析優(yōu)先二維數(shù)組[x, y]其次標(biāo)量x/y 相同再次{x, y}對象最后回退到scale或 1materials.ts。八、實(shí)現(xiàn)要點(diǎn)速查給源碼讀者主題必須進(jìn)緩存鍵createSurfaceRoleMaterial的緩存鍵為role-preset-side-sceneThemeId任何新接入點(diǎn)漏傳sceneTheme都會導(dǎo)致切換主題后顏色不刷新。四個重建觸發(fā)器shading / textures / colorPreset / sceneTheme是GeometrySystem的故意 re-run 觸發(fā)器刪除它們會破壞外觀切換見 geometry-system.tsx 的 biome-ignore 注釋。glazing 用 FrontSideMRT 渲染管線對DoubleSideNodeMaterial 的校驗限制是玻璃強(qiáng)制FrontSide的根因需要雙面顯示時旋轉(zhuǎn)網(wǎng)格 180°。無全白模式無紋理表面永遠(yuǎn)解析為主題化角色顏色這是 Mediterranean 藍(lán)屋頂?shù)刃Ч臋C(jī)制來源。UV 契約是作者級約定1 UV 單位 1 米程序化表面與 GLB 物品均遵守repeat 每米瓷磚數(shù)是材質(zhì)屬性而非表面屬性。外部插件材質(zhì)交換只在偏好變化時進(jìn)行絕不在useFrame銷毀順序為恢復(fù)作者材質(zhì) → 只銷毀插件變體 → 不動宿主緩存角色材質(zhì)。相關(guān)文檔wiki/architecture/item-authoring.md —— GLB 物品與 UV 作者約定wiki/architecture/node-definitions.md ——NodeDefinition、geometry與幾何系統(tǒng)wiki/architecture/renderers.md —— 渲染器架構(gòu)wiki/architecture/materials-and-themes.md —— 本文檔原始出處【免費(fèi)下載鏈接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.項目地址: https://gitcode.com/GitHub_Trending/editor93/editor創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考