编译失败回退演示(故意出错的 GLSL)
一个刻意保留语法错误的 GLSL ES 3.00 片元着色器,用来端到端验证错误处理路径:驱动日志必须可见、静态回退必须出现、页面不能留下空白画布。
静态预览:实时渲染需要启用 JavaScript 与支持 WebGL 2 的浏览器。初始化成功后会自动切换为实时画面。
实时预览未能启动
已自动切换到上方的静态回退预览。下面是浏览器驱动返回的原始编译日志,可直接用于定位 GLSL 错误。
运行环境与资源诊断
尚未初始化。
本页目录
这个条目为什么存在
任务书里有一条硬性要求:不允许渲染失败后只留下空白区域。这句话只有在真的触发一次失败时才能被验证,所以仓库里保留了这个「故障演练」条目。
src/shaders/glsl/compile-error-demo.frag 含有两处刻意留下的错误:
- 第一行赋值语句缺少分号;
- 调用了一个不存在的函数
undefinedHelper()。
因此这个条目的 runtime.status 依然是 live(预览确实会挂载并尝试编译),但额外设置了 runtime.expectedCompileFailure: true。这个标记会让列表页与详情页的徽标显示为「故意编译失败 · 错误回退演示」,而不是宣称有一个可以实时运行的效果。
请不要修复这个文件。
tests/shader-error-path.spec.ts依赖它的失败行为。
期望看到的页面状态
打开本页后,运行时应当依次做到:
checkWebGL2()通过,状态短暂进入loading;assertCompiles()在把源码交给 three.js 之前,用同一个 WebGL 2 上下文单独编译一次,捕获驱动返回的原始日志;- 抛出
ShaderCompileError,状态变为error; - 画布被隐藏,静态回退预览显示出来;
- 红色错误面板展开,
<pre>中是驱动日志,并且用annotateShaderError()在ERROR: 0:行号之后附上对应的源码行,方便直接定位; - 已经创建的 three.js
WebGLRenderer被dispose(),不会留下孤立的上下文。
日志的具体文字由驱动决定,因此断言只检查「非空」和「包含 ERROR 或编译失败关键字」,不去匹配驱动的实现细节。
为什么要在 three.js 之外单独编译一次
three.js 遇到着色器编译错误时只调用 console.error,不会抛出异常,也不会把日志交回调用方。对一个需要向访问者解释失败原因的作品集网站来说这不够用,所以 src/lib/shader-runtime/compileCheck.ts 先用同一个上下文创建一个临时 program 做完整编译 + 链接,读取 getShaderInfoLog / getProgramInfoLog,然后在 finally 中删除临时对象。
这样做的额外好处是:错误信息来自真实驱动,而不是我们自己写的解析器,因此不同 GPU / 不同浏览器上的日志差异会被如实呈现。
源码
浏览器 GLSL 与原始 HLSL 分开存放、分开展示。HLSL 仅作参考,浏览器无法直接执行 Unity URP 的 HLSL。
顶点着色器 fullscreen.vert glsl GLSL ES 3.00 · 浏览器中实际编译 22 行
three.js 会自动在最前面加入 `#version 300 es`,因此源文件本身不包含该指令。
// Fullscreen quad vertex shader (GLSL ES 3.00 / WebGL 2).
//
// NOTE: Three.js injects `#version 300 es` because the material is created as
// `new THREE.RawShaderMaterial({ glslVersion: THREE.GLSL3, ... })`.
// If you paste this file into a raw WebGL 2 context, add `#version 300 es`
// as the very first line and bind the attribute locations yourself.
//
// Built-in uniforms/attributes below are supplied by Three.js but must be
// declared explicitly because RawShaderMaterial adds no prefix code.
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
in vec3 position;
in vec2 uv;
out vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
} 片元着色器 compile-error-demo.frag glsl GLSL ES 3.00 · 浏览器中实际编译 25 行
// =====================================================================
// 这个文件是【故意写错】的,用于验证错误处理路径,请不要修复它。
//
// INTENTIONALLY BROKEN - this file exists so the compile-failure path can be
// verified end to end (Playwright test `shader-error-path`). It contains a
// missing semicolon and a call to an undeclared function, so the WebGL 2
// driver rejects it and the page must fall back to the static preview and
// print the driver log.
//
// 对应的 Playwright 用例:tests/shader-error-path.spec.ts
// =====================================================================
precision highp float;
in vec2 vUv;
uniform float uTime;
out vec4 fragColor;
void main() {
vec3 color = vec3(vUv.x, vUv.y, 0.5)
color += 0.1 * sin(uTime + undefinedHelper(color));
fragColor = vec4(color, 1.0);
}