shader-programming

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shader programming (cross-engine)

跨引擎着色器编程

Shaders are small programs that run per vertex and per pixel on the GPU. The concepts — the pipeline, coordinate spaces, UVs, and how common effects are built — port across engines; only the language dialect and built-in variable names change. This skill teaches those portable fundamentals in GLSL with HLSL equivalents; use
godot-shaders
(or Unity/Unreal material docs) for the exact engine syntax and built-ins.
着色器是在GPU上逐顶点逐像素运行的小型程序。其核心概念——渲染管线、坐标空间、UV,以及常见特效的实现方式——可跨引擎移植;仅语言变体和内置变量名称会有所不同。本技能将以GLSL为基础教授这些可移植的核心知识,并提供HLSL等效实现;如需了解特定引擎的语法和内置内容,请使用
godot-shaders
(或Unity/Unreal材质文档)。

When to use

使用场景

  • Use to understand or write vertex/fragment shaders and to reason about UVs, coordinate spaces, and the GPU pipeline.
  • Use to build common effects: tint/recolor, scrolling textures, dissolve, outlines, fresnel/rim light, vignette, color grading.
  • Use to translate a shader concept between GLSL and HLSL, or between engines.
When not to use: for an engine's exact shader language and built-ins, use
godot-shaders
(Godot shading language) or the engine's material docs. For full particle VFX systems, see
unreal-niagara
. For post-process stacks, defer to the engine's renderer settings.
  • 用于理解或编写顶点/片段着色器,以及梳理UV、坐标空间和GPU渲染管线的相关逻辑。
  • 用于实现常见特效:色调调整/重着色、纹理滚动、溶解、描边、菲涅尔/边缘光、暗角、颜色分级。
  • 用于在GLSL与HLSL之间,或不同引擎之间转换着色器概念。
不适用场景:如需了解引擎专属的着色器语言和内置内容,请使用
godot-shaders
(Godot着色语言)或对应引擎的材质文档。如需完整的粒子特效系统,请参考
unreal-niagara
。如需后期处理堆栈相关内容,请参考引擎的渲染器设置。

Core workflow

核心工作流程

  1. Know which stage you're in. The vertex shader transforms each vertex into clip space and passes data (UVs, normals) onward; the fragment/pixel shader runs per rasterized pixel and outputs a color. Most game effects live in the fragment stage.
  2. Track coordinate spaces. Positions move model → world → view → clip space; normals belong in world or view space. Mixing spaces is the most common bug.
  3. Drive effects with UVs and time. UVs are
    0..1
    texture coordinates; offset, scale, or distort them, and animate with a
    time
    uniform.
  4. Work per pixel, branch-light. Prefer
    mix
    ,
    step
    ,
    smoothstep
    , and
    clamp
    over
    if
    where possible; GPUs run pixels in lockstep and dislike divergent branches.
  5. Pass data via uniforms (constant per draw) and varyings (interpolated vertex→fragment). Keep texture samples few; they dominate cost.
  6. Verify visually and on target hardware. Shaders that look right on desktop can break on mobile (precision, missing features). Test where it ships.
  1. 明确当前所处的阶段顶点着色器负责将每个顶点转换到裁剪空间,并传递后续所需的数据(UV、法线);片段/像素着色器针对每个光栅化后的像素运行,并输出颜色。大多数游戏特效都在片段阶段实现。
  2. 跟踪坐标空间。顶点位置会依次经过模型空间→世界空间→视图空间→裁剪空间;法线则属于世界空间或视图空间。混合不同空间是最常见的错误。
  3. 通过UV和时间驱动特效。UV是
    0..1
    范围内的纹理坐标;可对其进行偏移、缩放或扭曲,并借助
    time
    全局变量实现动画。
  4. 逐像素处理,减少分支。尽可能优先使用
    mix
    step
    smoothstep
    clamp
    而非
    if
    语句;GPU以同步方式处理像素,发散分支会导致性能下降。
  5. 通过全局变量(uniforms)和插值变量(varyings)传递数据。全局变量在每次绘制时保持恒定;插值变量则在顶点→片段阶段进行插值。尽量减少纹理采样次数,这是性能开销的主要来源。
  6. 进行视觉验证和目标硬件测试。在桌面端表现正常的着色器可能在移动端出现问题(精度不足、缺少特性)。务必在目标运行环境中进行测试。

Patterns

实现模式

GLSL-style fragment snippets (close to Godot's
canvas_item
/
spatial
shaders and OpenGL). See
references/effects.md
for the HLSL equivalents and the full outline/fresnel/vignette shaders.
以下是GLSL风格的片段着色器代码片段(与Godot的
canvas_item
/
spatial
着色器及OpenGL语法接近)。完整的描边/菲涅尔/暗角着色器及HLSL等效实现,请参考
references/effects.md

1. Fragment basics: sample, tint, and combine

1. 片段着色器基础:采样、调色与混合

glsl
// Per-pixel: read the texture at this UV, multiply by a color (tint), keep alpha.
uniform sampler2D tex;
uniform vec4 tint;          // e.g. (1,0,0,1) reddens; multiply is non-destructive
in vec2 uv;                 // interpolated 0..1 texture coordinate (a "varying")
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);   // HLSL: tex.Sample(samp, uv)
    frag = c * tint;             // component-wise multiply tints without clipping
}
glsl
// Per-pixel: read the texture at this UV, multiply by a color (tint), keep alpha.
uniform sampler2D tex;
uniform vec4 tint;          // e.g. (1,0,0,1) reddens; multiply is non-destructive
in vec2 uv;                 // interpolated 0..1 texture coordinate (a "varying")
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);   // HLSL: tex.Sample(samp, uv)
    frag = c * tint;             // component-wise multiply tints without clipping
}

2. Scrolling UVs (animated texture) — frame-rate independent

2. UV滚动(纹理动画)——帧率无关

glsl
// Add time * speed to the UV to scroll. fract() wraps it into 0..1 so it tiles.
uniform sampler2D tex;
uniform float time;          // seconds, supplied by the engine
uniform vec2 scroll_speed;   // UV units per second, e.g. (0.1, 0.0)
in vec2 uv;
out vec4 frag;
void main() {
    vec2 scrolled = fract(uv + scroll_speed * time);  // HLSL: frac(...)
    frag = texture(tex, scrolled);
}
// Drive with a real time uniform, not a per-frame accumulator, so speed is stable.
glsl
// Add time * speed to the UV to scroll. fract() wraps it into 0..1 so it tiles.
uniform sampler2D tex;
uniform float time;          // seconds, supplied by the engine
uniform vec2 scroll_speed;   // UV units per second, e.g. (0.1, 0.0)
in vec2 uv;
out vec4 frag;
void main() {
    vec2 scrolled = fract(uv + scroll_speed * time);  // HLSL: frac(...)
    frag = texture(tex, scrolled);
}
// Drive with a real time uniform, not a per-frame accumulator, so speed is stable.

3. Dissolve (threshold a noise map, glow the edge)

3. 溶解特效(基于噪声图阈值,边缘发光)

glsl
// Hide pixels where noise < threshold; tint a thin band at the boundary.
uniform sampler2D tex;
uniform sampler2D noise_tex;     // grayscale noise, 0..1
uniform float amount;            // 0 = fully visible, 1 = fully dissolved
uniform float edge = 0.05;       // width of the glowing edge band
uniform vec4 edge_color;
in vec2 uv;
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);
    float n = texture(noise_tex, uv).r;
    if (n < amount) discard;                 // cut away dissolved pixels
    float e = smoothstep(amount, amount + edge, n);  // 0 at the edge -> 1 inside
    frag = mix(edge_color, c, e);            // HLSL: lerp(edge_color, c, e)
}
glsl
// Hide pixels where noise < threshold; tint a thin band at the boundary.
uniform sampler2D tex;
uniform sampler2D noise_tex;     // grayscale noise, 0..1
uniform float amount;            // 0 = fully visible, 1 = fully dissolved
uniform float edge = 0.05;       // width of the glowing edge band
uniform vec4 edge_color;
in vec2 uv;
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);
    float n = texture(noise_tex, uv).r;
    if (n < amount) discard;                 // cut away dissolved pixels
    float e = smoothstep(amount, amount + edge, n);  // 0 at the edge -> 1 inside
    frag = mix(edge_color, c, e);            // HLSL: lerp(edge_color, c, e)
}

4. Fresnel rim light (3D) — brighten glancing angles

4. 菲涅尔边缘光(3D)——提亮掠射角区域

glsl
// Rim = 1 where the surface faces away from the camera (silhouette glow).
in vec3 world_normal;        // normalized, world space (from the vertex stage)
in vec3 view_dir;            // normalized, surface -> camera, world space
uniform float power = 3.0;
uniform vec3 rim_color;
out vec4 frag;
void main() {
    float f = pow(1.0 - clamp(dot(world_normal, view_dir), 0.0, 1.0), power);
    frag = vec4(rim_color * f, 1.0);   // add to lighting; f peaks at the silhouette
}
// Correctness: normal and view_dir MUST be in the same space and normalized.
glsl
// Rim = 1 where the surface faces away from the camera (silhouette glow).
in vec3 world_normal;        // normalized, world space (from the vertex stage)
in vec3 view_dir;            // normalized, surface -> camera, world space
uniform float power = 3.0;
uniform vec3 rim_color;
out vec4 frag;
void main() {
    float f = pow(1.0 - clamp(dot(world_normal, view_dir), 0.0, 1.0), power);
    frag = vec4(rim_color * f, 1.0);   // add to lighting; f peaks at the silhouette
}
// Correctness: normal and view_dir MUST be in the same space and normalized.

Pitfalls

常见陷阱

  • Mixing coordinate spaces (lighting a world-space normal against a view-space light) yields subtly wrong shading. Pick one space and convert everything into it.
  • Forgetting to normalize interpolated normals/directions: interpolation shortens vectors, so
    dot()
    results drift.
    normalize()
    in the fragment stage.
  • UV assumptions across engines. Some engines flip V (top-left vs bottom-left origin); a texture may appear upside-down. Know your engine's convention.
  • Heavy branching / dynamic loops stall GPUs. Prefer
    step
    /
    smoothstep
    /
    mix
    ; reserve
    if
    /
    discard
    for genuinely cheap early-outs.
  • discard
    defeats early-Z
    and can hurt performance on tiled mobile GPUs; prefer alpha blending where you can.
  • Precision on mobile:
    highp
    vs
    mediump
    matters; large UVs or time values in low precision shimmer. Use adequate precision for coordinates and time.
  • Assuming GLSL == HLSL.
    mix
    lerp
    ,
    fract
    frac
    ,
    texture()
    .Sample()
    ,
    vec2
    float2
    , column- vs row-major matrices. See the reference mapping.
  • 混合坐标空间(用视图空间的灯光照亮世界空间的法线)会导致着色效果出现细微错误。请选择一个统一的空间,并将所有内容转换到该空间中。
  • 忘记归一化插值后的法线/方向向量:插值会缩短向量长度,导致
    dot()
    运算结果出现偏差。请在片段阶段执行
    normalize()
    操作。
  • 对不同引擎的UV假设错误:部分引擎会翻转V轴(原点位于左上角 vs 右下角);纹理可能会显示为上下颠倒。请了解目标引擎的约定。
  • 大量分支/动态循环会导致GPU卡顿。优先使用
    step
    /
    smoothstep
    /
    mix
    ;仅在确实能实现低成本提前退出时才使用
    if
    /
    discard
  • discard
    会破坏Early-Z优化
    ,并可能影响移动端 tiled GPU的性能;如有可能,优先使用 alpha 混合。
  • 移动端精度问题
    highp
    mediump
    的区别至关重要;低精度下的大UV值或时间值会导致闪烁。请为坐标和时间选择足够的精度。
  • 假设GLSL与HLSL完全一致:两者存在诸多差异,比如
    mix
    对应
    lerp
    fract
    对应
    frac
    texture()
    对应
    .Sample()
    vec2
    对应
    float2
    ,以及矩阵的列主序 vs 行主序。请参考参考文档中的映射表。

References

参考资料

  • references/effects.md
    — full outline (2D sprite + 3D), vignette, and color grading shaders; the GLSL↔HLSL function/type mapping table; per-engine notes (Godot
    canvas_item
    /
    spatial
    , Unity ShaderLab/HLSL, Unreal material nodes).
  • references/effects.md
    —— 完整的描边(2D精灵 + 3D)、暗角和颜色分级着色器;GLSL↔HLSL函数/类型映射表;各引擎注意事项(Godot
    canvas_item
    /
    spatial
    、Unity ShaderLab/HLSL、Unreal材质节点)。

Related skills

相关技能

  • godot-shaders
    — Godot shading language syntax, built-ins, and screen-reading.
  • unreal-niagara
    — GPU particle VFX (a different shader use).
  • procedural-gen
    — the noise that drives dissolve and procedural texturing.
  • godot-shaders
    —— Godot着色语言语法、内置内容及屏幕读取相关知识。
  • unreal-niagara
    —— GPU粒子特效(着色器的另一种应用场景)。
  • procedural-gen
    —— 驱动溶解特效和程序化纹理的噪声生成技术。