fix(canvas): 3D 导演台 - 修复白膜视频导出渲染循环逐帧崩溃 (#439)
导出白膜视频得到的是 0 秒视频:文件实测仅 1 帧(ffprobe nb_read_frames=1,duration=0.001s),却无任何报错并照常回写画布。 根因:applyClaySceneMaterials 把场景内所有非 actor mesh 的材质换成 clay MeshStandardMaterial,drei Grid 的 GridMaterial(带 uniforms 的 ShaderMaterial)也在其中。Grid 组件内部 useFrame 每帧读取 material.uniforms.worldCamProjPosition,材质被换后 uniforms 为 undefined,渲染循环逐帧抛 TypeError,R3F 不再产出新帧, captureStream 只录到启动前手动 gl.render 的那一帧;5 秒后 setTimeout 照常 stop,残缺视频被当作成功结果。开启轨迹显示 (drei Line 同为 uniforms 材质)或视口白膜显示模式时同样触发。 修复: - applyClaySceneMaterials 跳过带 uniforms 的 ShaderMaterial, 并抽到 lib/canvas/director/director-clay-materials.ts 补回归测试; - recordCanvas 加固:录制期间监听 window 未捕获错误,首个渲染 错误立即中止并报错;录制结束用 <video> 探测真实时长(Chrome MediaRecorder 的 webm 无时长头,需 seek 到末尾),低于预期一半 报「时长异常」,杜绝残缺视频再被静默回写。
This commit is contained in:
29
web/src/lib/canvas/director/director-clay-materials.ts
Normal file
29
web/src/lib/canvas/director/director-clay-materials.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Mesh, MeshStandardMaterial, Scene, type Material } from "three";
|
||||
|
||||
/**
|
||||
* 将场景整体切换为白膜材质,返回恢复函数;恢复时销毁共享的 clay 材质。
|
||||
*
|
||||
* 两类 mesh 必须跳过,否则切换本身会破坏渲染:
|
||||
* - directorActor mesh 的材质由展示层按展示状态自行管理;
|
||||
* - 带 uniforms 的 ShaderMaterial(drei Grid/Line 等)由组件自身 useFrame 逐帧读写 uniforms,
|
||||
* 换成 clay 后渲染循环会逐帧抛错、画布冻结,白膜录制只剩启动前手动渲染的首帧。
|
||||
*/
|
||||
export function applyClaySceneMaterials(scene: Scene) {
|
||||
const clayMaterial = new MeshStandardMaterial({ color: "#d6d9dd", roughness: 0.88, metalness: 0 });
|
||||
const originals: Array<{ mesh: Mesh; material: Material | Material[] }> = [];
|
||||
scene.traverse((child) => {
|
||||
const mesh = child as Mesh;
|
||||
if (!mesh.isMesh || mesh.userData.directorActor) return;
|
||||
const material = mesh.material;
|
||||
const shaderBacked = Array.isArray(material) ? material.some((item) => "uniforms" in item) : "uniforms" in material;
|
||||
if (shaderBacked) return;
|
||||
originals.push({ mesh, material });
|
||||
mesh.material = clayMaterial;
|
||||
});
|
||||
return () => {
|
||||
originals.forEach(({ mesh, material }) => {
|
||||
mesh.material = material;
|
||||
});
|
||||
clayMaterial.dispose();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user