# Web Blender:RE 5000PBR × 猴头 GLB 渲染与初学者 HTML 教案 日期:2026-11-11 状态:**功能实现完成;有头 Chromium 聚焦验证通过;整体产品验收仍由用户执行** 后续:已知限制的修补与验证见 [`RE_PBR_LIMITS_20261112.md`](RE_PBR_LIMITS_20261112.md)。 (`IMPLEMENTED_NOT_USER_ACCEPTED`) ## 交付内容 ### 1. RE 5000PBR 派生资产库 - `tools/web/generate-re-pbr-library.py`:从 `re/5000PBR精简版/5000+ PBR catalog.blend` 的工作副本派生 6 个材质;标记 5 个已标记材质 + 未标记的 `Black_Anti_Slip_grip`;统一挂到 `Materials/RE5000PBR` catalog。 - 保留原节点图(`FRAME`、`REROUTE`、`DISPLACEMENT`、`MAPPING`、`VALUE` 与四张 `IMAGE_TEXTURE`), 只把贴图降采样:Base Color/Normal 1024²,Roughness/Height 512²,并重新 pack 为 JPEG。 - 产物:`web/app/public/assets/re-pbr/` - `re-pbr-library.blend`:6 个 MATERIAL,SHA-256 `d67f5a058c154626dfc7171c5bc7fc254b8ee183fea8740261b9711b3309d782`。 - `manifest.json`、`preview-manifest.json`、`desktop-canonical.json`、`blender_assets.cats.txt`、 6 张 256² 预览。 - 库大小 3.7 MiB,packed 贴图约 3.06 MiB(源 10.7 MiB)。 - `tools/web/check-re-pbr-library.mjs`:校验 6 个材质、预览、SHA、尺寸、JPEG 格式、色彩空间, 并只读复检节点图仍包含 `FRAME/REROUTE/DISPLACEMENT/MAPPING/VALUE` 与四张贴图。 ### 2. 多库播种与合并(资产面板 20 个资产) - `web/protocol/asset-catalog-v2.ts`:新增 `mergeAssetCatalogManifests`,按 catalog/asset/library id 去重追加、bump revision 并重新校验。 - `web/app/src/storage/asset-catalog-store.ts`:新增 `mergeAssetCatalogManifest`,事务内重读原始行、 检测 revision 漂移、更新/删除预览回执,避免旧回执失效。 - `web/protocol/storage.ts`、`StorageClient.ts`、`storage.worker.ts`:新增合并命令; seed/读取 manifest/预览的客户端超时提高到 30–60 s。 - `web/app/src/app/pbr-library-seed.ts`:`PBR_LIBRARY_SOURCES` 支持 `pbr-essentials-v1`(14)与 `re-pbr-v1`(6);首装先 seed 主库,再对缺失库 `seed(repair=true)` + merge;单库失败只降级该库并保留既有资产。 - `AssetLibraryPanel.tsx`:卡片补 `data-testid`/`data-asset-id`/`data-library`;新增“资产来源”筛选; 缩略图改为 eager,20 张预览全部加载。 - 既有 PBR 教程/测试的资产数从 14 更新到 20;PBR 教程原有 12 张渲染矩阵保持不变。 ### 3. 渲染正确性修复 - **快照后重绑纹理与环境**:`viewport-render.worker.ts` 拆分 `reapplyCurrentTextures()`(只从已上传 store 重绑)与 `applyTextureAssets()`(upload + 重绑); `setSnapshot` 末尾重绑,不再在每次分配材质时重新解码全部贴图。 - **HDRI 背景保护**:只有 world 确有环境贴图时才让 `applyEnvironment` 覆盖背景, flat world color 不再覆盖已应用的 HDRI。 - **纹理就绪屏障**:`offscreen-viewport.ts` 增加按 snapshot revision 对齐的 `whenTexturesReady()`;`renderStill` 等待纹理与环境应用完成;空纹理也明确回传状态。 面板用该屏障替换固定 150 ms 延时。 - **原生 reader**:`web_engine_blend_reader.cpp` 不再把 `NodeReroute/NodeFrame/ShaderNodeDisplacement` 统一映射为 `UNSUPPORTED`,而是输出 `REROUTE/FRAME/DISPLACEMENT`。 - **编译器归一化**:`compileMaterialGraph` 渲染路径与 `compileShaderGraph` 共用 `normalizeShaderGraph`:折叠 REROUTE、移除 FRAME、删除 DISPLACEMENT 节点及全部连线; 对不含这些节点的图严格 no-op。 - **编译器扩展**:支持 `IMAGE_TEXTURE → Roughness/Metallic/Alpha`、`IMAGE_TEXTURE → SEPARATE_COLOR`、`VALUE → MAPPING.Scale/Rotation/Location`(标量广播);Mapping 的 `uvTransform` 与原生 `textureBindings` 按 usage 合并,不再被原生绑定覆盖。 - `SceneIR` 运行时校验同步允许 `REROUTE/FRAME/DISPLACEMENT`。 - `App.tsx` 的材质分配在对象无槽时用 `slotIndex=-1` 追加槽,避免先创建空白材质导致 `name.001` 冲突;重开工程后材质槽保留。 - `web/app/public/engine-manifest.json` 的 `single` 变体改指向重新构建的 `/vendor/blender/web_engine.{js,wasm}`,修复旧的 `vendor/blender/single/web_engine.wasm` 哈希不匹配(`MANIFEST_REJECTED`)。 ### 4. 有头 Chromium 测试与 20 张成片 - `web/tests/e2e/re-pbr-render-workflow.spec.ts`: - 打开摄影棚 → 导入 `monkey-head.glb` → Shading 20 卡/6 个 RE 材质 → 逐个分配 6 材质 → 材质球 `COMPILED` → 1024/场景渲染 → `data-texture-status` ready → PNG 下载 → 保存 .blend → 重开并确认材质槽仍存在。 - 截断 GLB 负例:对象数不变。 - `tools/web/capture-re-pbr-lesson.mjs`:有头 Chromium(`headless:false`)生成 - `tests/golden/RE-PBR-RENDER-001/renders/*.png`:6 材质 ×(1024 场景 + 1024 视口 + 512 场景) + 未分配基线 + 保存重开复渲染 = 20 张; - `web/app/public/tutorial/pbr/lesson/renders/*.jpg` 与 `steps/*.jpg`、`landing.jpg`; - `report.json`(尺寸、SHA、mean/std、textureStatus、材质、相机、浏览器/DISPLAY)。 - `tools/web/check-re-pbr-lesson.mjs`:PASS(20 张、6 材质 SHA 两两不同、基线不同、 PNG 尺寸、textureStatus ready、GLB SHA、教案 HTML 无 inline script/style/on*、无远程 URL、 所有图片存在)。 ### 5. 初学者 HTML 教案 - `web/app/public/tutorial/pbr/render-lesson.html` + `render-lesson.css` - 12 节:目标、术语、准备、导入 GLB、资产库、六种材质逐课、渲染设置、下载/保存/重开、 20 张矩阵、黑图排查、限制、附录。 - 静态 HTML;外链 CSS;无 inline `