Skip to content
Development
Skill

/shader-programming

Engine-neutral shader technique in GLSL with HLSL equivalents: how dissolve, outline, fresnel rim and post effects work, and porting GLSL to HLSL. Use when the math of an effect is the question, or for specks or mobile shimmer. To write a shader in one engine, its skill alone is

From plugin
awesome-gamedev-agent-skills
8.6k79 skills

Claude Code

$ npx -y skills add gamedev-skills/awesome-gamedev-agent-skills --skill shader-programming --agent claude-code

SKILL.md

shader-programming.SKILL.md
name: shader-programming
description: >
  Engine-neutral shader technique in GLSL with HLSL equivalents: how dissolve, outline, fresnel
  rim and post effects work, and porting GLSL to HLSL. Use when the math of an effect is the
  question, or for specks or mobile shimmer. To write a shader in one engine, its skill alone is
  enough (`godot-shaders`, `threejs-materials-lighting`, `pixijs-rendering`). Not for particles
  (`unreal-niagara`).

Shader programming (cross-engine)

Shaders run per vertex and per pixel on the GPU. The pipeline, coordinate spaces, UV tricks and the maths behind common effects port across engines; only the dialect and built-in names change. This skill owns that technique in GLSL with HLSL equivalents. Load the engine skill (`godot-shaders`, `threejs-materials-lighting`, `pixijs-rendering`) as well for the syntax, built-ins and how the material is wired.

When to use

  • Designing or debugging an effect's maths: tint, scroll, dissolve, outline, rim, vignette,

grading.

  • Reasoning about stages, spaces, interpolation and precision.
  • Porting a shader between GLSL and HLSL, or between engines.

**Not this skill:** Godot shading language syntax and pipeline (`godot-shaders`), three.js `ShaderMaterial` vs TSL (`threejs-materials-lighting`), PixiJS filters (`pixijs-rendering`), particle systems (`unreal-niagara`), post-process stack settings (the engine's renderer docs), per-pixel cost in a profiler (`performance-optimization`).

Core workflow

1. **Name the stage.** The vertex stage moves vertices to clip space and passes data on; the fragment stage runs per pixel and outputs a colour. Most game effects are fragment work. 2. **Pick one space** for lighting maths (world or view) and convert every vector into it. 3. **Drive effects with UVs, uniforms and time.** UVs are 0..1; uniforms are constant per draw; varyings are interpolated from vertex to fragment. 4. **Prefer `mix`, `step`, `smoothstep` and `clamp` to branches** on per-pixel values: pixels run in lockstep, so a branch that differs between neighbours runs both sides. A branch on a uniform is cheap. 5. **Keep texture samples few**; they are usually the main cost. 6. **Compile before looking** (see "Check it"). 7. **Ask for a visual check on the target device** with specific questions.

Patterns

GLSL fragment snippets for desktop `#version 330`; each compiles with `glslangValidator` under that header. For GLSL ES 3.00 (WebGL 2, three.js `ShaderMaterial`, PixiJS filters) add `precision highp float;` and set uniform defaults from code: ES rejects `uniform float edge = 0.05;` with "cannot initialize this type of qualifier", while Godot's shading language accepts it. HLSL forms and the longer effects are in `references/effects.md`.

1. Sample, tint, combine

uniform sampler2D tex;
uniform vec4 tint;          // multiply tints without clipping; (1,1,1,1) is unchanged
in vec2 uv;
out vec4 frag;
void main() {
    vec4 c = texture(tex, uv);   // HLSL: tex.Sample(samp, uv)
    frag = c * tint;
}

2. Scrolling UVs, frame-rate independent

uniform sampler2D tex;
uniform float time;          // seconds since start, supplied by the engine
uniform vec2 scroll_speed;   // UV units per second
in vec2 uv;
out vec4 frag;
void main() {
    vec2 scrolled = fract(uv + scroll_speed * time);  // HLSL: frac(...)
    frag = texture(tex, scrolled);
}
// Drive with an absolute time uniform, not a per-frame accumulator. With mipmaps, fract()
// leaves a thin seam where the UV jumps from 1 to 0; prefer a repeat sampler and drop fract().

3. Dissolve with a glowing edge

// 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;
    float cut = amount * (1.0 + edge) - edge;  // edge band starts below 0 -> none at amount 0
    if (n <= cut) discard;                     // cut away dissolved pixels (all of them at 1)
    float e = smoothstep(cut, cut + edge, n);  // 0 at the edge -> 1 inside; keep edge > 0
    frag = mix(edge_color, c, e);              // HLSL: lerp(edge_color, c, e)
}
// WRONG: thresholding at `amount` directly — at amount 0 every pixel with
// n < edge is still tinted, so the "fully visible" sprite shows glowing specks.

4. Fresnel rim light

in vec3 world_normal;        // world space, interpolated from the vertex stage
in vec3 view_dir;            // surface -> camera, world space
uniform float power = 3.0;
uniform vec3 rim_color;
out vec4 frag;
void main() {
    // Interpolation does not preserve unit length, even with normalized vertex outputs.
    vec3 normal = normalize(world_normal);
    vec3 view = normalize(view_dir);
    float f = pow(1.0 - clamp(dot(normal, view), 0.0, 1.0), power);
    frag = vec4(rim_color * f, 1.0);   // add to lighting; f peaks at the silhouette
}

Check it

1. **Compile every GLSL file** with `glslangValidator path/to/effect.frag` (the stage comes from the suffix: `.vert`, `.frag`). Compile the ES variant too when it ships to the web: put `#version 300 es` and `precision highp float;` at the top. For WGSL run `naga path/to/shader.wgsl`. For Godot `.gdshader` files use the headless compile check in `godot-shaders`. 2. **Check the endpoints numerically** where the effect has parameters: dissolve at `amount` 0 shows no edge colour and at 1 discards everything; a vignette at `strength` 0 leaves the image unchanged. Read the maths off the code, or render a test quad in the engine and read pixels back. 3. **Ask the user to look on the target hardware** and report specific things: "Does the dissolve show specks a

Read more
@gamedev-skills
Ships withawesome-gamedev-agent-skills

<img src="docs/assets/awesome-gamedev-agent-skills-banner.png" width="820" alt="awesome-gamedev-agent-skills — game-dev skills for AI coding agents.

Stars on GitHub

Momentum

stars in total
8.6k
Star history of gamedev-skills/awesome-gamedev-agent-skills on GitHub, a chart by star-history.com

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills13d309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react4d252k
@facebook@facebookInstall Skill

Questions about shader-programming.

Which coding agents can use shader-programming?
shader-programming installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.