12 KiB
Web Blender PBR 渲染实现与任务分解
更新日期:2026-08-09
1. 目标和声明边界
目标是在浏览器 3D Viewport 中提供与 Blender Principled 工作流一致、可保存、可重开的 PBR 预览。材质参数必须来自 Blender Main,经 SceneIR 传输并由主线程和 OffscreenCanvas Worker 使用相同映射,禁止只修改 Three.js 临时状态。
当前能力称为 physical-v1,不是 Cycles、EEVEE 或 Blender Shader Node 的逐像素等价实现。
它保证的是有界 Principled 参数、线性色彩输入、sRGB 输出、ACES Filmic tone mapping、
World exposure、场景灯光和有界 PCF 阴影的一致 WebGL 渲染。未声明节点、谱渲染、光程、体积、
次表面、多次散射和离线 path tracing 必须继续结构化阻断或降级并给出 warning。
2. 权威数据流
Blender Main
-> .blend writer/save/reopen
-> native blend reader
-> SceneIR MaterialIR/LightIR/WorldIR
-> transferable mesh buffers
-> shared pbr.ts mapping
-> main-thread WebGLRenderer
-> OffscreenCanvas render Worker
编辑链路为:Inspector 控件 -> setMaterialPrincipled -> EngineWorker -> Blender Main 节点
socket -> 新 revision SceneIR -> 视口重建。任何新增参数必须同时通过命令校验、Main 写回、
reader、SceneIR parser、保存重开和双后端验收。
3. Blender 到 Web 的材质映射
| Blender 5.2 Principled 输入 | SceneIR | Three.js | 范围/说明 |
|---|---|---|---|
| Base Color | baseColor |
color |
线性 RGB,alpha 独立处理 |
| Roughness | roughness |
roughness |
[0,1] |
| Metallic | metallic |
metalness |
[0,1] |
| IOR | ior |
ior |
当前限定 [1,2.333] |
| Specular IOR Level | specularIORLevel |
specularIntensity |
Blender 中性值 0.5 映射到 Three 中性值 1.0;高半区有界截断 |
| Transmission Weight | transmissionWeight |
transmission |
[0,1],使用物理透射 pass |
| Coat Weight | coatWeight |
clearcoat |
[0,1] |
| Coat Roughness | coatRoughness |
clearcoatRoughness |
[0,1] |
| Emission Color | emissionColor |
emissive |
线性 RGB |
| Emission Strength | emissionStrength |
emissiveIntensity |
[0,1000000] |
| Alpha | alpha |
opacity/transparent/depthWrite |
alpha blend 时关闭 depth write |
材质选择高亮保存原始 emission 和强度,取消选择后恢复,不再破坏自发光材质。所有网格、 实例和 LOD 都启用一致的 cast/receive shadow 标志。
4. 色彩、灯光和阴影
- Blender/SceneIR 数值按线性工作空间进入材质和灯光。
- WebGL 输出固定为
SRGBColorSpace,tone mapping 固定为ACESFilmicToneMapping。 WorldIR.exposure按摄影 stop 转换为2 ** exposure,并限制在[-8,8]。- 无场景灯时使用低强度中性 Hemisphere fill 和有阴影的 key light,确保空场景可检查。
- POINT、SUN、SPOT、AREA 均从
LightIR创建。SUN/SPOT 根据 Blender 本地-Z方向创建真实 Three target,不再错误地全部指向世界原点。 - SUN、SPOT、POINT 使用
PCFShadowMap,第一质量档为 1024 平方 shadow map,并设置 有界 bias、normal bias、near/far。AREA 受 Three WebGL 能力限制,当前不声明阴影等价。 - 主线程与 Worker 暴露
data-pbr-profile、data-tone-mapping和data-shadow-map,供 自动化测试核对真实后端配置。
5. 资产和纹理边界
当前 SceneIR 已表达 UV、Image Texture、Normal Map、packed image 和资源状态,但纹理像素
进入 GPU 的链路必须继续遵守 EngineWorker/StorageWorker 权威模型。浏览器渲染器不得根据
.blend 内路径自行访问文件系统或网络。
PBR-007 已建立 GPUTextureAsset 只读载荷:assetId、imageId、SHA-256、MIME、尺寸、
颜色空间、用途、UDIM tile 和 ArrayBuffer 必须通过字节数、64 MiB 单图、16384 维度和
SHA-256 校验后才可进入 ImageBitmap/Three Texture。主线程与 Offscreen Worker 共享同一
GPUTextureStore 逻辑,载荷状态和拒绝字节数可观测;路径仍只能由 EngineWorker/StorageWorker
解析,浏览器不得访问 .blend 内路径。当前不把 context-loss 自动恢复、mipmap/anisotropy
策略和完整 UV Map 节点映射误报为完成。
6. 质量档和性能预算
| 档位 | 阴影 | DPR | 透射 | 目标 |
|---|---|---|---|---|
| Low | 关闭或 512 | 1 | 关闭 | 低内存移动设备 |
| Medium | PCF 1024 | min(device,1.5) |
0.5 分辨率 | 默认移动/集显 |
| High | PCF 1024/2048 | min(device,2) |
全分辨率 | 默认桌面 |
physical-v1 当前实现 High 的有界子集。后续自动降档的输入包括 GPU tier、draw call、
transmission material 数、shadow caster 数、WASM/GPU 内存压力和帧时间 P95。质量切换只能
改变表现质量,不得改变 Main 或保存语义。
7. 任务分解与执行顺序
PBR-001 协议与安全门:done
- PBR-001-A:扩展
MaterialIR五个物理参数,保持旧 snapshot 可解析。 - PBR-001-B:对比例参数验证
[0,1],对 emission strength 设硬上限。 - PBR-001-C:扩展
setMaterialPrincipled,所有字段保持 optional 兼容旧客户端。 - PBR-001-D:linked material 继续拒绝写回;NaN/Infinity 继续由 native 阻断。
PBR-002 Blender Main authoring:done
- PBR-002-A:自建 Principled 节点补齐五个 Blender 5.2 socket。
- PBR-002-B:命令写入 Base/Roughness/Metallic/IOR/Specular/Transmission/Coat/Emission。
- PBR-002-C:native reader 从真实 socket 读取并生成 SceneIR。
- PBR-002-D:Main round-trip 和 save/reopen 核对每个物理参数误差小于
1e-5。
PBR-003 共享物理材质适配器:done
- PBR-003-A:用
MeshPhysicalMaterial替代两条路径的 Standard/fixed material。 - PBR-003-B:集中 clamp、默认值、透明深度策略和 selection emission 恢复。
- PBR-003-C:主线程、Worker、实例和 LOD 使用同一映射。
- PBR-003-D:Worker 恢复 UV、corner color 和多材质 triangle group。
PBR-004 色彩管理和曝光:done
- PBR-004-A:线性输入、sRGB output、ACES Filmic。
- PBR-004-B:World exposure 按 stop 映射并限制极端值。
- PBR-004-C:通过 canvas profile 属性和像素非空门验证双后端。
PBR-005 灯光和阴影:done_current_scope
- PBR-005-A:POINT/SUN/SPOT/AREA 类型映射。
- PBR-005-B:修复 SUN/SPOT 方向 target。
- PBR-005-C:SUN/SPOT/POINT PCF 阴影、bias 和 shadow camera 边界。
- PBR-005-D:待补能量单位校准 fixture、级联太阳阴影、AREA 阴影替代和 caster budget。
PBR-006 材质 Inspector:done_current_scope
- PBR-006-A:增加 IOR、Specular、Transmission、Coat、Coat Roughness、Emission Strength。
- PBR-006-B:应用命令保留 alpha 和 emission color。
- PBR-006-C:待补材质槽选择、emission color、alpha mode、双面和 blend mode 面板。
PBR-007 纹理像素闭环:in_progress(AssetPayload/解码已完成,GPU 生命周期仍有安全门)
- PBR-007-A:done_current_scope;
render-assets.ts定义 schema v1、SHA-256、MIME/尺寸/字节预算和稳定错误码,App 从requestAsset生成载荷并同时写入 OPFS。 - PBR-007-B:done_current_scope;Base Color/Emission 使用 sRGB,Normal/Data 使用 non-color/linear,主线程和 Worker 使用相同
GPUTextureStore。 - PBR-007-C:in_progress;ImageBitmap 解码、旧 Texture dispose、状态回传已完成;mipmap、anisotropy、GPU context-loss 恢复和全局显存预算仍未开放。
- PBR-007-D:Normal Map 切线空间、UV Map 选择、wrap/filter 和缺失纹理 fallback。
- PBR-007-E:in_progress;双后端同一载荷正例已覆盖,context-loss/Worker crash 后重新上传仍属于 PBR-011 故障注入门。
PBR-008 UDIM 与外部资源:in_progress(manifest/阻断已完成,多 tile 采样未声明)
- PBR-008-A:done_current_scope;
UDIMManifest校验 1001..1999、u/v 坐标、重复 tile、数量上限和 packed 状态;每个 tile 进入同一 SHA-256 载荷。 - PBR-008-B:in_progress;当前只允许单 tile 安全上传,多 tile 返回
UDIM_MULTI_TILE_UNAVAILABLE,尚未实现分页、LRU 和 GPU atlas/array 采样。 - PBR-008-C:done_current_scope;linked library、外部路径和缺 tile 使用结构化门,禁止任意路径读取。
- PBR-008-D:done_current_scope;generated/sequence/movie 不生成静态 GPU 载荷,保持独立 capability。
PBR-009 IBL 和 World Nodes:in_progress(有限 raster equirectangular IBL)
- PBR-009-A:in_progress;packed PNG/JPEG equirectangular 环境可经 GPU 载荷、PMREMGenerator 生成反射环境;HDR/EXR 解码尚未实现并返回
IBL_FORMAT_UNSUPPORTED。 - PBR-009-B:in_progress;WorldIR 已携带 environment image/strength/rotation 字段,当前只消费 strength;旋转、跨场景缓存和完整 HDR 色域仍待安全门。
- PBR-009-C:done_current_scope;
backgroundVisible与scene.environment生命周期分离,旧 PMREM target 会释放。 - PBR-009-D:done_current_scope;任意 World Shader Node 不进入纹理上传,保持结构化阻断。
PBR-010 GLB PBR 扩展:done_current_scope
- PBR-010-A:已导出
KHR_materials_ior、transmission、clearcoat、specular、emissive_strength,并保留无完整节点图时的有界imageIdsBase Color/Normal 路径。 - PBR-010-B:桌面 Blender 再导入比较覆盖 8x8 packed texture、骨骼、shape key、动作和材质基础值。
- PBR-010-C:不支持的 Blender 图返回
SHADER_GRAPH_UNMAPPABLE,禁止静默烘焙。 - PBR-010-D:advanced extension fixture 已验证桌面 Blender 对 IOR、Specular IOR Level、Transmission、Coat 和 Emission Strength 的导入数值。
PBR-011 性能、容错和跨浏览器:planned
- PBR-011-A:100k/1M 三角形下记录 CPU/GPU 帧时、draw call、shader compile 和显存。
- PBR-011-B:Chromium 主线程/OffscreenCanvas 的 ACES、transmission 与 shadow 差异矩阵。
- PBR-011-C:WebGL context loss、Worker crash、纹理解码失败和 GPU OOM 故障注入。
- PBR-011-D:自动降档必须可观测且不修改权威数据。
PBR-012 高级渲染:in_progress(能力查询/阻断协议,运行时家族仍未声明)
- PBR-012-A:done_current_scope;
queryRenderCapability明确返回WEBGPU_RENDERER_UNAVAILABLE,没有 WebGPU renderer bundle 时不伪装为 WebGPU。 - PBR-012-B:done_current_scope(安全门);BLOOM/SSAO/SSR/TAA/DOF/MOTION_BLUR 未有真实 pass 时返回
POSTPROCESS_PASS_UNAVAILABLE,未声明假实现。 - PBR-012-C:done_current_scope(安全门);Volume/SSS 返回独立
VOLUME_SHADER_UNAVAILABLE/SUBSURFACE_SHADER_UNAVAILABLE,任意 Shader node 返回SHADER_NODE_UNSUPPORTED。 - PBR-012-D:浏览器 path tracer 若引入,作为独立 Render backend,不声称 Cycles 等价。
8. 当前验收
source tools/web/emscripten-env.sh
cmake --build build_web_blender6 --target web_engine -- -j8
bash tools/web/install-web-engine-assets.sh
npm --prefix web run typecheck
npm --prefix web run lint
npm --prefix web test
npm --prefix web run test:authoring-roundtrip
WEB_TEST_PORT=5209 npm --prefix web run test:e2e -- -g "PBR|Principled|local UV|packed raster|OffscreenCanvas"
发布前还要运行完整 E2E、跨浏览器、GLB Blender round-trip 和离线包门。桌面与移动截图必须 检查 canvas 非空、模型构图、材质高光/阴影可见、控件不遮挡以及页面无异常横向溢出。
9. 完成定义
一个 PBR 子任务只有在以下条件全部满足后才能标记 done:协议字段可验证;Main 可写; 保存重开等价;主线程和 Worker 可渲染;资源可释放;错误稳定且结构化;至少一个正例和一个 边界反例;桌面/移动像素与截图门通过;状态文档同步。仅识别 Blender socket、仅构造 SceneIR 或仅显示 UI 控件都不构成完成。