因为ShaderMaterial不自动提供光照、法线、uv等变量,需手动声明attribute/uniform/varying,漏写、类型不匹配或precision声明缺失均会导致编译失败而黑屏;顶点与片元着色器的varying必须完全一致,且自定义uniform须在render循环中手动更新。

为什么直接写 ShaderMaterial 容易黑屏或报错
因为 ShaderMaterial 不像 MeshStandardMaterial 那样自动提供光照、法线、uv 等变量;你得手动声明所有需要的 attribute、uniform 和 varying,漏一个或类型不匹配,GPU 就拒绝编译。常见错误包括:ERROR: 0:12: 'vUv' : undeclared identifier、WebGL: INVALID_OPERATION: useProgram: program not valid。
必须确保顶点着色器输出的 varying 变量名、类型、精度,与片元着色器中声明的完全一致(包括 precision mediump float; 声明位置);否则链接阶段失败,材质渲染为空白或默认灰。
- 顶点着色器里用
attribute vec3 position,但没在 JavaScript 中传入 geometry 的positionattribute?会静默失败 - 忘记在片元着色器开头加
precision mediump float;?iOS Safari 直接崩溃 - 把
uniform sampler2D texture写成uniform sampler2D uTexture,但 JS 里没用uTexture当 key 赋值?纹理就为 null
怎么组织 vertexShader 和 fragmentShader 字符串
别把 GLSL 写在 JS 字符串里拼接——换行、引号、反斜杠极易出错。推荐用 <script type="x-shader/x-vertex"></script> 和 <script type="x-shader/x-fragment"></script> 标签内联,再用 document.getElementById().textContent 读取。这样编辑器有语法高亮,缩进和注释也自然保留。
示例结构:
```html
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script><p><script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vUv;
uniform float uTime;
void main() {
gl_FragColor = vec4(vUv.x, vUv.y, sin(uTime), 1.0);
}
</script></p><pre class="brush:language-;toolbar:false;">
注意:uv 是 Three.js 默认提供的 attribute(只要 geometry 有 uv 属性),但必须显式赋给 varying 才能传到片元着色器;projectionMatrix 和 modelViewMatrix 是 Three.js 自动注入的 uniform,不用自己声明。
哪些 uniform 是 Three.js 自动传入的,哪些必须手动更新
Three.js 会自动注入常用矩阵和相机参数,比如:modelMatrix、viewMatrix、projectionMatrix、cameraPosition、lightPosition(仅当 scene 里有 PointLight 且 material 的 lights: true)。但自定义 uniform(如 uTime、uResolution)必须在每一帧或需要时手动更新。
-
material.uniforms.uTime.value = clock.getElapsedTime();—— 必须在 render 循环里调 -
material.uniforms.uResolution.value = new THREE.Vector2(window.innerWidth, window.innerHeight);—— resize 时重设 - 如果忘了在
render()里更新uTime,动画就卡住不动 -
lights: true开启后,Three.js 才注入pointLights数组,否则即使写了uniform vec3 pointLights[1]也会链接失败
调试 GLSL 报错最有效的三步
浏览器控制台只显示“program not linked”,看不出哪行错。真正有用的是从 WebGL 上下文里捞日志:
- 在创建
ShaderMaterial后加console.log(material.program?.getProgramInfoLog()); - 用
material.program?.getShaderInfoLog(material.vertexShader)单独查顶点着色器错误 - 临时删掉片元着色器全部逻辑,只留
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);看是否红——能红说明管线通了,问题在你的计算逻辑
另外,ShaderMaterial 默认不响应灯光、阴影、雾效;如果需要这些,要么自己实现光照模型(比如 Phong),要么改用 RawShaderMaterial 并手动管理更多状态——这点很容易被忽略,直到发现物体永远不随灯光变亮才回头查。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











