mcpField
T
threejs-errors-rendering

threejs-errors-rendering

github.com/impertio-studio开发23143 次安装30 周前更新
代码仓库

关于 threejs-errors-rendering

面向 Three.js 开发者的渲染故障排查技能,按常见症状整理检查步骤与修复方法,涵盖画面、颜色、阴影和 WebGL 问题。

主要功能

  1. 01黑屏与不可见对象排查
  2. 02颜色空间与色调映射检查
  3. 03Z-Fighting 与阴影问题修复
  4. 04WebGL 上下文丢失恢复
  5. 05缓冲区与材质更新检查

适用场景

  1. 01定位场景无画面的原因
  2. 02修复物体不可见或颜色异常
  3. 03处理阴影瑕疵与表面闪烁
  4. 04排查 WebGL 丢失或更新问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/impertio-studio/three.js-claude-skill-package --skill threejs-errors-rendering

安装后在 Claude Code 中输入/threejs-errors-rendering确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

.gitkeep
references/
anti-patterns.md
examples.md
methods.md

详细介绍

threejs-errors-rendering

调试工作流检查清单

当 Three.js 场景无法正确渲染时,务必按以下顺序检查:

  1. 打开浏览器控制台——检查是否有 WebGL 错误或 Three.js 警告
  2. 确认渲染器尺寸非零(renderer.getSize(new THREE.Vector2()))
  3. 确认画布位于 DOM 中且可见(不是 display: none)
  4. 检查是否调用了 renderer.render(scene, camera)(在循环中或至少调用一次)
  5. 确认摄像机正对场景(位置、目标、近裁剪面/远裁剪面)
  6. 确认场景中至少有一个光源,供受光材质使用
  7. 检查材质的 side、visible、opacity 和 transparent 属性
  8. 检查对象的 visible、layers 和 frustumCulled 属性
  9. 检查渲染器和纹理的色彩空间设置

症状 1:黑屏(什么都看不见)

原因 A:渲染器尺寸为零

画布的尺寸为 0x0。这通常是因为在容器加入 DOM 之前调用了 setSize(),或者容器没有 CSS 尺寸。

修复:

// ALWAYS ensure the container is in the DOM and has dimensions before setSize
document.body.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
原因 B:摄像机位于对象内部或背对对象

摄像机位于 (0, 0, 0),对象也位于 (0, 0, 0),因此摄像机处于网格内部。或者摄像机指向了错误的方向。

修复:

camera.position.set(0, 2, 5); // ALWAYS move camera away from origin
camera.lookAt(0, 0, 0);
原因 C:近/远裁剪面将对象排除在外

距离近于 near 或远于 far 的对象会被裁剪。默认的 PerspectiveCamera 近裁剪面为 0.1,远裁剪面为 2000。

修复:

const camera = new THREE.PerspectiveCamera(75, aspect, 0.1, 1000);
// NEVER set near to 0 -- causes z-fighting and depth buffer issues
// ALWAYS keep far/near ratio below 100000 for stable depth precision
原因 D:场景中没有光源

MeshStandardMaterial、MeshPhongMaterial 和 MeshLambertMaterial 需要光源。没有光源时,它们会渲染为黑色。MeshBasicMaterial 则不需要光源。

修复:

scene.add(new THREE.AmbientLight(0xffffff, 0.5));
scene.add(new THREE.DirectionalLight(0xffffff, 1));
原因 E:从未调用 render()

动画循环未启动,或者缺少 renderer.render(scene, camera)。

修复:

function animate() {
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
animate(); // ALWAYS call the function to start the loop
原因 F:场景或摄像机引用错误

向 render() 传入了空场景或未初始化的摄像机。

诊断: 在渲染调用之前记录 scene.children.length 和 camera.type。


症状 2:对象不可见

原因 A:材质侧面设置错误

默认情况下会剔除背面(FrontSide)。如果摄像机看到的是平面或薄几何体的背面,该对象就不可见。

修复:

const material = new THREE.MeshStandardMaterial({
  side: THREE.DoubleSide // ALWAYS use for planes, leaves, thin objects
});
原因 B:设置了 opacity,但未设置 transparent

未设置 transparent: true 时,设置 opacity: 0.5 不会生效。

修复:

const material = new THREE.MeshStandardMaterial({
  opacity: 0.5,
  transparent: true // ALWAYS pair with opacity < 1
});
原因 C:对象位于不同图层

摄像机和对象必须至少共享一个图层。默认情况下,两者都位于图层 0。如果将对象移到其他图层,摄像机也必须启用该图层。

修复:

object.layers.set(1);
camera.layers.enable(1); // camera must see layer 1
原因 D:继承的 visible 为 false

父对象的 visible = false 会使其所有后代对象都不可见。请检查完整的父级链。

诊断:

let node = object;
while (node) {
  if (!node.visible) console.log('Hidden ancestor:', node.name || node.type);
  node = node.parent;
}
原因 E:frustumCulled 设置不正确

如果包围球有误(例如,手动修改顶点后未调用 computeBoundingSphere()),即使对象可见,也可能会被剔除。

修复:

geometry.computeBoundingSphere(); // ALWAYS call after modifying positions
// Or disable frustum culling for objects that must always render:
mesh.frustumCulled = false;
原因 F:对象位置错误或缩放为 0

对象的位置离摄像机很远,或者设置了 scale.set(0, 0, 0)。

诊断: 记录 object.position、object.scale、object.matrixWorld。


症状 3:颜色错误

原因 A:色彩空间不匹配

这是 Three.js r160+ 中最常见的颜色错误。

规则:

  • 颜色/漫反射/自发光纹理:务必设置 texture.colorSpace = THREE.SRGBColorSpace
  • 数据纹理(法线、粗糙度、金属度、AO、位移):务必保持为 THREE.LinearSRGBColorSpace
  • 渲染器输出:renderer.outputColorSpace = THREE.SRGBColorSpace(r160+ 中的默认值)

色彩空间错误的症状:

  • 颜色发灰:数据纹理被错误地设置为 SRGBColorSpace(伽马校正两次)
  • 颜色过饱和:颜色纹理保留为 LinearSRGBColorSpace(未应用伽马校正)

修复:

const texture = await loader.loadAsync('diffuse.png');
texture.colorSpace = THREE.SRGBColorSpace; // for color textures

const normalMap = await loader.loadAsync('normal.png');
// NEVER set SRGBColorSpace on normal maps -- corrupts surface data
原因 B:未配置色调映射

未使用色调映射时,HDR 值会被截断,导致颜色平淡或不正确。

修复:

renderer.toneMapping = THREE.ACESFilmicToneMapping; // or AgXToneMapping
renderer.toneMappingExposure = 1.0;
原因 C:构造后设置材质颜色被忽略

material.color.set() 可以正常工作,但在构造后设置 material.color = new THREE.Color() 也可以正常工作。常见错误是直接将十六进制数字设为颜色:material.color = 0xff0000 不起作用。

修复方法:

material.color.set(0xff0000);      // Correct
material.color = new THREE.Color(0xff0000); // Correct
// material.color = 0xff0000;      // WRONG -- silently fails

症状 4:Z 冲突(表面闪烁)

当两个表面在几乎相同的深度重叠时,会发生 Z 冲突,导致深度缓冲区在两者之间交替。

修复方法 A:多边形偏移
const material = new THREE.MeshStandardMaterial({
  polygonOffset: true,
  polygonOffsetFactor: -1,
  polygonOffsetUnits: -1
});
修复方法 B:对数深度缓冲区
const renderer = new THREE.WebGLRenderer({ logarithmicDepthBuffer: true });
// Trades performance for better depth precision across large near/far ranges
// NEVER use with EffectComposer post-processing -- causes artifacts
修复方法 C:位置偏移

稍微移动一个表面:

decalMesh.position.z += 0.01; // small offset to prevent overlap
修复方法 D:减小远近比
// ALWAYS keep the near plane as large as possible
camera.near = 1;    // not 0.001
camera.far = 1000;  // not 1000000
camera.updateProjectionMatrix();

症状 5:阴影伪影

有关完整的阴影配置,请参阅 threejs-impl-shadows 技能。常见的快速修复方法:

  • 完全没有阴影: renderer.shadowMap.enabled = true、light.castShadow = true、mesh.castShadow = true、ground.receiveShadow = true
  • 阴影痤疮(条纹): 增大 light.shadow.bias(例如 -0.005)
  • 彼得潘效应(阴影脱离): light.shadow.bias 过大,应减小
  • 阴影分辨率低: 增大 light.shadow.mapSize.set(2048, 2048)

症状 6:WebGL 上下文丢失

浏览器在内存压力过大或 GPU 重置时会回收 WebGL 上下文。

恢复模式
renderer.domElement.addEventListener('webglcontextlost', (event) => {
  event.preventDefault(); // ALWAYS prevent default to allow restoration
  cancelAnimationFrame(animationId);
}, false);

renderer.domElement.addEventListener('webglcontextrestored', () => {
  // Re-initialize materials, textures, render targets
  initScene();
  animate();
}, false);

绝不要忽略上下文丢失——画布会永久变黑。务必添加这两个事件监听器。


症状 7:缺少 needsUpdate

BufferAttribute

修改顶点数据后,GPU 缓冲区会过时:

positions.array[0] = newX;
positions.needsUpdate = true; // ALWAYS set after modifying attribute data
材质

更改结构属性(添加或移除贴图、更改 defines)后:

material.map = newTexture;
material.needsUpdate = true; // triggers shader recompilation
// NEVER set needsUpdate = true every frame -- causes constant recompilation
纹理

修改纹理图像数据后:

texture.image = newImage;
texture.needsUpdate = true; // triggers GPU re-upload
InstancedMesh

调用 setMatrixAt() 或 setColorAt() 后:

mesh.instanceMatrix.needsUpdate = true; // ALWAYS after setMatrixAt
mesh.instanceColor.needsUpdate = true;  // ALWAYS after setColorAt

症状 8:需要调用 updateProjectionMatrix

更改以下属性后,务必调用 camera.updateProjectionMatrix():

  • camera.fov
  • camera.aspect
  • camera.near
  • camera.far
  • camera.zoom
  • camera.left/right/top/bottom(OrthographicCamera)
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix(); // NEVER forget this
  renderer.setSize(window.innerWidth, window.innerHeight);
});

症状 9:透明对象渲染问题

透明对象的渲染顺序不正确(从后到前的排序失败)。

修复方法 A:renderOrder
// Force specific rendering order
backgroundMesh.renderOrder = 0;
transparentMesh.renderOrder = 1;
frontMesh.renderOrder = 2;
修复方法 B:depthWrite
const material = new THREE.MeshStandardMaterial({
  transparent: true,
  opacity: 0.5,
  depthWrite: false // prevents transparent objects from writing to depth buffer
});
修复方法 C:使用 alphaTest 代替透明度

对于具有硬边缘 Alpha 的纹理(树叶、栅栏):

const material = new THREE.MeshStandardMaterial({
  map: leafTexture,
  alphaTest: 0.5, // discards fragments below threshold
  side: THREE.DoubleSide
  // NEVER set transparent: true for hard-edge alpha -- use alphaTest instead
});

常见控制台警告

警告原因修复方法
THREE.WebGLRenderer: Texture is not power of two使用重复包裹模式的 NPOT 纹理使用二次幂纹理或 ClampToEdgeWrapping
THREE.WebGLProgram: shader errorGLSL 编译失败检查自定义着色器代码是否存在语法错误
THREE.PropertyBinding: Can not bind to...动画目标缺少属性确保骨架/变形目标与动画剪辑匹配
THREE.BufferGeometry: .addAttribute() removed使用了已弃用的 API改用 setAttribute()
GL_INVALID_OPERATION: Feedback loop从同时用作渲染目标的纹理读取使用独立纹理进行读写

参考链接

官方来源

评论

支持 Markdown