返回 Skills 目錄
gamedev-skills/awesome-gamedev-agent-skills已通過檢查

SKILL DETAIL

shader-programming

gamedev-skills/awesome-gamedev-agent-skills/shader-programming

Write game shaders from cross-engine fundamentals — the vertex→fragment pipeline, coordinate spaces, UV math, and common 2D/3D effects (tint, UV scroll, dissolve, outline, fresnel rim, vignette) in GLSL with HLSL equivalents. Use when the user mentions shaders, fragment/pixel shader, vertex shader, UV, GLSL, HLSL, or effects like dissolve, outline, or rim light.

安裝量 · 315查看來源

Installation

npx skills add https://github.com/gamedev-skills/awesome-gamedev-agent-skills --skill shader-programming

技能檔案

SKILL.md

最近同步 · 2026年10月22日

references/effects.md›
# Shader effects and GLSL↔HLSL mapping

Full versions of the effects sketched in `SKILL.md`, plus the dialect mapping you
need to port a shader between engines. Examples are GLSL-style; the mapping table
shows the HLSL form.

## GLSL ↔ HLSL quick reference

| GLSL | HLSL | Note |
|---|---|---|
| `vec2/3/4`, `mat3/4` | `float2/3/4`, `float3x3/4x4` | vectors and matrices |
| `mix(a, b, t)` | `lerp(a, b, t)` | linear interpolation |
| `fract(x)` | `frac(x)` | fractional part |
| `mod(a, b)` | `fmod(a, b)` | sign differs for negatives — verify |
| `texture(samp, uv)` | `tex.Sample(samp, uv)` | sampler is a separate object in HLSL |
| `inversesqrt(x)` | `rsqrt(x)` | |
| `mat * vec` (column-major) | `mul(vec, mat)` (row-major) | matrix order/convention differs |
| `gl_Position` / `out` color | `SV_Position` / `SV_Target` | stage outputs via semantics |
| `dFdx/dFdy` | `ddx/ddy` | screen-space derivatives |

Also watch: clip-space depth range (OpenGL `-1..1` vs D3D `0..1`) and UV/clip Y
orientation differ between APIs — engines usually normalize this for you, but it
surfaces when porting raw shaders.

## 2D sprite outline (sample the alpha neighborhood)

```glsl
// Draw an outline where a transparent pixel is adjacent to an opaque one.
uniform sampler2D tex;
uniform vec2 texel_size;     // (1/width, 1/height) of the texture
uniform vec4 outline_color;
in vec2 uv;
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);
    if (c.a > 0.5) { frag = c; return; }          // inside the sprite: unchanged
    // sample 4 neighbors; if any is opaque, this empty pixel is on the border
    float a = max(max(texture(tex, uv + vec2( texel_size.x, 0)).a,
                      texture(tex, uv + vec2(-texel_size.x, 0)).a),
                  max(texture(tex, uv + vec2(0,  texel_size.y)).a,
                      texture(tex, uv + vec2(0, -texel_size.y)).a));
    frag = (a > 0.5) ? outline_color : vec4(0.0);  // border pixel -> outline
}
```

For thicker or smoother outlines, sample 8 neighbors (include diagonals) or do a
small distance-field pass. In 3D, outlines are usually done differently: render
back-faces scaled outward along normals, or detect edges from depth/normal
discontinuities in a post-process.

## Vignette (darken the screen edges) — post-process

```glsl
// Darken pixels by distance from screen center. screen_uv is 0..1 across the view.
uniform sampler2D screen_tex;
uniform float strength = 0.6;   // 0 = none, 1 = strong
uniform float radius = 0.75;    // where darkening begins
in vec2 screen_uv;
out vec4 frag;
void main() {
    vec3 c = texture(screen_tex, screen_uv).rgb;
    float d = distance(screen_uv, vec2(0.5));      // 0 center .. ~0.707 corner
    float v = smoothstep(radius, radius * 0.5, d); // 1 in center, ->0 at edges
    frag = vec4(c * mix(1.0 - strength, 1.0, v), 1.0);
}
```

## Color grading via a curve (brightness/contrast/saturation)

```glsl
// Order matters: contrast around 0.5, then saturation, then brightness.
uniform sampler2D screen_tex;
uniform float brightness = 0.0;   // additive
uniform float contrast   = 1.0;   // multiplicative around mid-gray
uniform float saturation = 1.0;
in vec2 screen_uv;
out vec4 frag;
void main() {
    vec3 c = texture(screen_tex, screen_uv).rgb;
    c = (c - 0.5) * contrast + 0.5;                       // contrast
    float luma = dot(c, vec3(0.2126, 0.7152, 0.0722));    // Rec.709 luminance
    c = mix(vec3(luma), c, saturation);                   // saturation
    c += brightness;                                      // brightness
    frag = vec4(clamp(c, 0.0, 1.0), 1.0);
}
```

The luminance weights `(0.2126, 0.7152, 0.0722)` are the Rec.709 coefficients —
green dominates perceived brightness. For LUT-based grading, sample a color
lookup texture indexed by the pixel's RGB instead.

## Per-engine notes

- **Godot (4.x) — `gdshader`.** Godot's shading language is GLSL-like with a
  `shader_type` (`canvas_item` for 2D, `spatial` for 3D). It provides built-ins:
  `UV`, `COLOR`, `TIME`, `TEXTURE`, `SCREEN_TEXTURE`, `NORMAL`, `VIEW`. Write
  `fragment()` / `vertex()` functions. Map the GLSL examples here onto those
  built-ins. See `godot-shaders`.
- **Unity — ShaderLab + HLSL.** Shaders live in `.shader` files (ShaderLab
  blocks) or Shader Graph. Code is HLSL: `float4`, `lerp`, `tex2D`/`.Sample`,
  `SV_Target`. URP/HDRP supply include files for lighting and transforms.
- **Unreal — Material Editor (node graph) + HLSL.** Most authoring is visual
  nodes; Custom nodes embed HLSL. Concepts (UVs, fresnel via a Fresnel node,
  panner for UV scroll) map directly to the patterns here.

## Performance checklist

- Minimize texture samples; they are the dominant cost. Reuse a sample instead of
  re-reading.
- Prefer math (`smoothstep`, `mix`) to branches and `discard`.
- Compute heavy values per vertex (then interpolate) when per-pixel precision
  isn't required.
- Use appropriate precision on mobile (`mediump` for color, higher for UV/time).
- Avoid dependent texture reads (sampling using a value read from another texture)
  on low-end GPUs where possible.
SKILL.md›
---
name: shader-programming
description: >
  Write game shaders from cross-engine fundamentals — the vertex→fragment
  pipeline, coordinate spaces, UV math, and common 2D/3D effects (tint, UV
  scroll, dissolve, outline, fresnel rim, vignette) in GLSL with HLSL
  equivalents. Use when the user mentions shaders, fragment/pixel shader, vertex
  shader, UV, GLSL, HLSL, or effects like dissolve, outline, or rim light.
---

# 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.

## 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.

## 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.

## 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.

### 1. Fragment basics: sample, tint, and combine

```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

```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)

```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

```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.

## 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).

## 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.