web-shaders

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Web Shaders

Web Shader

You are writing shaders that run in a browser. Get the platform and language right first, then the technique, then verify visually.
你正在编写运行于浏览器的 shader。先确定正确的平台和语言,然后选择技术,最后进行视觉验证。

1. Establish the target before writing a line

1. 确定目标,再写第一行代码

Ask (or infer from the codebase) which of these you are in — the boilerplate and language differ:
TargetLanguageNotes
Raw WebGL 2GLSL ES 3.00 (
#version 300 es
)
You declare everything.
in/out
,
texture()
, custom
out vec4
.
Raw WebGL 1GLSL ES 1.00
attribute/varying
,
texture2D()
,
gl_FragColor
. Legacy — avoid for new work.
WebGPU (raw)WGSL
@vertex
/
@fragment
, bind groups, uniform buffers with strict alignment.
Three.js
ShaderMaterial
GLSL ES 1.00 by defaultThree injects camera/model matrices and attributes (see §2). Set
glslVersion: THREE.GLSL3
for GLSL 3.
Three.js
RawShaderMaterial
whatever you declareNo injected uniforms/attributes or
precision
. You write the full header.
Three.js TSL (WebGPURenderer)JavaScript nodesAuthor with
tslFn
/node ops; compiles to both WGSL and GLSL. No raw shader strings.
React Three FiberGLSL via drei
shaderMaterial
Wraps
ShaderMaterial
; uniforms become material props.
If the project uses
WebGPURenderer
, prefer TSL over raw WGSL strings — it is the supported path and cross-compiles.
询问目标,或从代码库中推断当前使用的技术——不同目标所需的模板代码和语言各不相同:
目标语言说明
原生 WebGL 2GLSL ES 3.00 (
#version 300 es
)
全部内容都由你声明。使用
in/out
、
texture()
,并自定义
out vec4
。
原生 WebGL 1GLSL ES 1.00使用
attribute/varying
、
texture2D()
、
gl_FragColor
。属于旧版技术,新项目应避免使用。
WebGPU(原生)WGSL使用
@vertex
/
@fragment
、bind groups,以及具有严格对齐要求的 uniform buffers。
Three.js
ShaderMaterial
默认使用 GLSL ES 1.00Three.js 会自动注入相机、模型矩阵和 attributes(见 §2)。使用 GLSL 3 时设置
glslVersion: THREE.GLSL3
。
Three.js
RawShaderMaterial
由你自行声明不注入任何 uniforms、attributes 或
precision
。需要编写完整的 header。
Three.js TSL(WebGPURenderer)JavaScript nodes使用
tslFn
/节点操作编写;可编译为 WGSL 和 GLSL。不使用原始 shader 字符串。
React Three Fiber通过 drei
shaderMaterial
使用 GLSL
封装
ShaderMaterial
;uniforms 会成为 material props。
如果项目使用
WebGPURenderer
,应优先采用 TSL,而不是原始 WGSL 字符串——这是受支持的实现路径,并且支持跨语言编译。

2. Three.js ShaderMaterial: what is already provided

2. Three.js ShaderMaterial:已经提供的内容

ShaderMaterial
(not
RawShaderMaterial
) auto-injects, so do not redeclare them:
  • Vertex attributes:
    position
    ,
    normal
    ,
    uv
    .
  • Uniforms:
    modelMatrix
    ,
    modelViewMatrix
    ,
    projectionMatrix
    ,
    viewMatrix
    ,
    normalMatrix
    ,
    cameraPosition
    .
  • precision
    is set for you.
A minimal correct pair:
glsl
// vertex
varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
glsl
// fragment
varying vec2 vUv;
uniform float uTime;
void main() {
  gl_FragColor = vec4(vUv, 0.5 + 0.5 * sin(uTime), 1.0);
}
For GLSL 3 (
glslVersion: THREE.GLSL3
): replace
varying
→
in/out
,
texture2D
→
texture
, and declare
out vec4 fragColor;
instead of
gl_FragColor
.
In R3F, prefer drei's
shaderMaterial(uniforms, vertex, fragment)
+
extend
, then drive uniforms from a
useFrame
ref, not React state (avoid re-renders).
ShaderMaterial
(而非
RawShaderMaterial
)会自动注入以下内容,因此不要重复声明:
  • 顶点 attributes:
    position
    、
    normal
    、
    uv
    。
  • Uniforms:
    modelMatrix
    、
    modelViewMatrix
    、
    projectionMatrix
    、
    viewMatrix
    、
    normalMatrix
    、
    cameraPosition
    。
  • precision
    已自动设置。
一组最小且正确的代码:
glsl
// vertex
varying vec2 vUv;
void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
glsl
// fragment
varying vec2 vUv;
uniform float uTime;
void main() {
  gl_FragColor = vec4(vUv, 0.5 + 0.5 * sin(uTime), 1.0);
}
对于 GLSL 3(
glslVersion: THREE.GLSL3
):将
varying
替换为
in/out
,将
texture2D
替换为
texture
,并声明
out vec4 fragColor;
来代替
gl_FragColor
。
在 R3F 中,优先使用 drei 的
shaderMaterial(uniforms, vertex, fragment)
配合
extend
,然后通过
useFrame
ref 驱动 uniforms,而不是使用 React state(避免重新渲染)。

3. WGSL essentials (WebGPU)

3. WGSL 核心要点(WebGPU)

  • Entry points:
    @vertex fn vs(...) -> @builtin(position) vec4f
    and
    @fragment fn fs(...) -> @location(0) vec4f
    .
  • Uniforms live in a
    struct
    bound via
    @group(g) @binding(b) var<uniform> u: U;
    — respect std140-like alignment (a
    vec3<f32>
    occupies 16 bytes; pad explicitly).
  • No implicit conversions:
    1
    is
    i32
    ,
    1.0
    is
    f32
    ,
    1u
    is
    u32
    . Be explicit.
  • Texture sampling:
    textureSample(tex, samp, uv)
    in fragment stage only.
  • 入口点:
    @vertex fn vs(...) -> @builtin(position) vec4f
    和
    @fragment fn fs(...) -> @location(0) vec4f
    。
  • Uniforms 放在一个
    struct
    中,并通过
    @group(g) @binding(b) var<uniform> u: U;
    绑定——必须遵守类似 std140 的对齐规则(一个
    vec3<f32>
    占用 16 字节;需要显式填充)。
  • 不存在隐式类型转换:
    1
    是
    i32
    ,
    1.0
    是
    f32
    ,
    1u
    是
    u32
    。应明确指定类型。
  • 纹理采样:仅可在 fragment stage 中使用
    textureSample(tex, samp, uv)
    。

4. Coordinate and color correctness (the usual bugs)

4. 坐标与颜色正确性(常见问题)

  • UV origin: GL textures are bottom-left origin. Three sets
    texture.flipY = true
    by default for images; for render targets it is false. Flip
    vUv.y
    if the image is upside down.
  • Aspect ratio: correct with
    uv.x *= resolution.x / resolution.y
    (or
    uv = (uv - .5) * vec2(aspect, 1.) + .5
    ) or circles become ellipses.
  • NDC vs UV:
    gl_Position
    is clip space;
    gl_FragCoord.xy
    is in pixels — divide by resolution for 0..1.
  • Color space: three renders linear then converts on output (
    renderer.outputColorSpace = SRGBColorSpace
    ). Sample color textures as sRGB (
    texture.colorSpace = SRGBColorSpace
    ); keep data textures (normals, roughness) linear. Do lighting math in linear space.
  • Tone mapping: if
    renderer.toneMapping
    is set, raw emissive colors get remapped — account for it.
  • Premultiplied alpha / blending: set
    transparent: true
    and the right
    blending
    mode; unmultiplied vs premultiplied changes edges.
  • UV 原点:GL 纹理以左下角为原点。Three.js 默认对图像设置
    texture.flipY = true
    ,对 render target 则为 false。如果图像上下颠倒,请翻转
    vUv.y
    。
  • 宽高比:使用
    uv.x *= resolution.x / resolution.y
    进行校正(或使用
    uv = (uv - .5) * vec2(aspect, 1.) + .5
    ),否则圆形会变成椭圆形。
  • NDC 与 UV:
    gl_Position
    位于裁剪空间;
    gl_FragCoord.xy
    使用像素坐标——除以 resolution 可归一化到 0..1。
  • 色彩空间:Three.js 先在线性空间中渲染,再在输出时转换(
    renderer.outputColorSpace = SRGBColorSpace
    )。颜色纹理应按 sRGB 采样(
    texture.colorSpace = SRGBColorSpace
    );数据纹理(法线、粗糙度)应保持线性。光照计算应在线性空间中进行。
  • Tone mapping:如果设置了
    renderer.toneMapping
    ,原始 emissive colors 会被重新映射——需要考虑这一影响。
  • Premultiplied alpha / blending:设置
    transparent: true
    和正确的
    blending
    模式;unmultiplied 与 premultiplied 的差异会影响边缘效果。

5. Portability pitfalls that fail silently on some GPUs

5. 在部分 GPU 上静默失败的可移植性问题

  • Always set
    precision highp float;
    in
    RawShaderMaterial
    /raw WebGL; test
    mediump
    behavior for mobile (banding, precision loss).
  • WebGL 1 GLSL: no dynamic (non-constant) array indexing, no
    while
    /non-constant loop bounds; unroll or use textures.
  • mod(x, y)
    and
    %
    differ for negatives;
    pow(negative, y)
    is undefined — clamp/abs first.
  • Integer division truncates; write
    1.0/3.0
    , not
    1/3
    .
  • Derivatives (
    dFdx
    /
    dFdy
    ,
    fwidth
    ) need
    #extension GL_OES_standard_derivatives : enable
    in WebGL 1 (built in for WebGL 2 / GLSL 3).
  • Avoid heavy branching in fragment shaders; prefer
    mix
    /
    step
    /
    smoothstep
    . Both branches of a divergent
    if
    may execute.
  • Never
    normalize()
    a possibly-zero vector without guarding.
  • 在
    RawShaderMaterial
    /原生 WebGL 中始终设置
    precision highp float;
    ;针对移动端测试
    mediump
    下的表现(色带、精度损失)。
  • WebGL 1 GLSL:不支持动态(非恒定)数组索引,也不支持
    while
    /非恒定循环边界;应展开循环或改用 textures。
  • mod(x, y)
    与
    %
    对负数的处理不同;对负数使用
    pow(negative, y)
    属于未定义行为——应先执行 clamp/abs。
  • 整数除法会截断小数;应编写
    1.0/3.0
    ,而不是
    1/3
    。
  • WebGL 1 中的 derivatives(
    dFdx
    /
    dFdy
    、
    fwidth
    )需要
    #extension GL_OES_standard_derivatives : enable
    ;WebGL 2 / GLSL 3 已内置支持。
  • 避免在 fragment shader 中使用复杂分支;优先采用
    mix
    /
    step
    /
    smoothstep
    。发生分支发散的
    if
    ,其两个分支都可能会执行。
  • 对可能为零的向量调用
    normalize()
    前,必须进行保护。

6. Technique quick-reference

6. 技术快速参考

  • Shaping:
    step
    ,
    smoothstep
    ,
    mix
    ,
    clamp
    ,
    fract
    ,
    mod
    — the vocabulary of procedural graphics.
  • SDF / raymarching: distance functions + sphere tracing. Reach for Inigo Quilez's primitives and the raymarching loop; use
    Lygia
    for ready functions.
  • Noise: gradient/simplex (
    glsl-noise
    ,
    webgl-noise
    ,
    lygia
    ). Value noise for cheap, simplex for smooth.
  • Post-processing: render to a target, sample in a fullscreen pass; in three use
    pmndrs/postprocessing
    or
    EffectComposer
    .
  • GPGPU: encode state in float textures, ping-pong render targets (three
    GPUComputationRenderer
    ), or WebGPU compute shaders.
Pull ready-made functions from Lygia (cross-language GLSL/WGSL) instead of reimplementing noise, easing, SDFs, and color conversions.
  • 塑形:
    step
    、
    smoothstep
    、
    mix
    、
    clamp
    、
    fract
    、
    mod
    ——这些是程序化图形的基础工具。
  • SDF / raymarching:距离函数 + sphere tracing。可以直接采用 Inigo Quilez 的图元和 raymarching 循环;也可以使用 Lygia 提供的现成函数。
  • 噪声:gradient/simplex(
    glsl-noise
    、
    webgl-noise
    、
    lygia
    )。Value noise 成本较低,simplex noise 更平滑。
  • Post-processing:渲染到 target,再在全屏 pass 中采样;在 Three.js 中可使用
    pmndrs/postprocessing
    或
    EffectComposer
    。
  • GPGPU:将状态编码到 float textures 中,使用 ping-pong render targets(Three.js 中的
    GPUComputationRenderer
    ),或者使用 WebGPU compute shaders。
直接从 Lygia(跨语言 GLSL/WGSL)获取现成函数,无需重复实现噪声、easing、SDF 和颜色转换。

7. Debugging workflow

7. 调试工作流

  1. Isolate: output a known value (
    gl_FragColor = vec4(vUv, 0, 1)
    ) to confirm the pass runs and UVs are sane.
  2. Visualize the variable: pipe any intermediate to color (
    vec4(vec3(d), 1.0)
    ) rather than guessing.
  3. Check the console for shader compile/link errors — WebGL reports the exact line; WebGPU logs validation errors. Read them.
  4. Confirm uniforms are actually set (typos in uniform names fail silently — the value stays 0).
  5. Use Spector.js (WebGL) or the WebGPU Inspector to capture the draw call, uniforms, and compiled shader.
  1. 隔离问题:输出一个已知值(
    gl_FragColor = vec4(vUv, 0, 1)
    ),确认 pass 正在运行且 UV 正常。
  2. 可视化变量:将任意中间值输出到颜色通道(
    vec4(vec3(d), 1.0)
    ),而不是依靠猜测。
  3. 检查控制台:查看 shader 编译或链接错误——WebGL 会报告具体行号;WebGPU 会记录 validation errors。务必阅读这些错误。
  4. 确认 uniforms 已正确设置(uniform 名称拼写错误会静默失败,值将始终为 0)。
  5. 使用 Spector.js(WebGL)或 WebGPU Inspector 捕获 draw call、uniforms 和编译后的 shader。

8. Authoritative sources to consult

8. 应查阅的权威资料

When unsure of an API, consult these rather than guessing (many are LLM-ingestible
llms.txt
):
不确定某个 API 时,应查阅以下资料,而不是依靠猜测(其中许多都提供适合 LLM 读取的
llms.txt
):

9. Before you finish

9. 完成前检查

  • State the target you assumed (WebGL2/WebGPU, Three.js material type) if it was ambiguous.
  • Verify it compiles (no console errors) and describe or screenshot the visual result — a shader that compiles can still render black.
  • Keep uniforms named consistently between JS and GLSL; a mismatch is the most common "nothing happens" bug.
  • 如果目标不明确,请说明你所假设的目标(WebGL2/WebGPU、Three.js material type)。
  • 确认 shader 能够编译(控制台中没有错误),并描述或截图展示视觉结果——shader 即使编译成功,也可能渲染为黑色。
  • 确保 JS 和 GLSL 中的 uniforms 名称保持一致;名称不匹配是导致“什么都没有发生”的最常见 bug。