smithery.ai

engine-shader

Create custom shaders for this engine. Use when the user asks to add visual effects, custom materials, toon shading, hologram effects, or any custom rendering. Covers GLSL shader writing, shadow integration, and material creation.

First seen Apr 4, 2026

Installation

$ npx skills add https://smithery.ai

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery.ai · top by installs.

npx skills add https://smithery.ai

Browse all from smithery.ai

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 9,076 B
  • docs SUMMARY.md 251 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

Creating Custom Shaders

This skill guides creation of GLSL shaders that integrate with the engine's shadow system.

Architecture Overview

The engine uses a shader injection system:

  1. You write fragment shaders with custom effects
  2. Engine automatically injects shadow uniforms and getShadow() function
  3. Use standard vertex shader or write custom one
  4. Register shader → Create material → Assign to entities

File Locations

Place shaders in your game's assets folder:

assets/
└── shaders/
    ├── my_effect.fs     # Fragment shader (required)
    └── my_effect.vs     # Vertex shader (optional, use standard)

Fragment Shader Template

#version 330

// Varyings from vertex shader (standard vertex shader provides these)
in vec3 fragPosition;  // World-space position
in vec2 fragTexCoord;  // Texture coordinates
in vec4 fragColor;     // Vertex color
in vec3 fragNormal;    // World-space normal

// Material uniforms (auto-set by raylib)
uniform sampler2D texture0;  // Diffuse texture
uniform vec4 colDiffuse;     // Material color

// Engine injects these automatically:
// uniform vec3 lightDir;    // Directional light direction
// uniform vec4 lightColor;  // Light color
// uniform vec4 ambient;     // Ambient color
// uniform vec3 viewPos;     // Camera position
// float getShadow(vec3 fragPos, vec3 normal); // Returns 0.0 (shadow) to 1.0 (lit)

// Time uniform (auto-updated by engine each frame)
uniform float time;

// Output
out vec4 finalColor;

void main()
{
    // Sample texture
    vec4 texColor = texture(texture0, fragTexCoord);
    vec3 baseColor = texColor.rgb * colDiffuse.rgb;

    // Basic lighting
    vec3 normal = normalize(fragNormal);
    float NdotL = max(dot(normal, -lightDir), 0.0);

    // Get shadow factor (1.0 = lit, 0.0 = shadowed)
    float shadow = getShadow(fragPosition, normal);

    // Apply lighting
    vec3 litColor = baseColor * (NdotL * shadow * lightColor.rgb + ambient.rgb);

    finalColor = vec4(litColor, texColor.a * colDiffuse.a);

    // Gamma correction
    finalColor.rgb = pow(finalColor.rgb, vec3(1.0 / 2.2));
}

Loading and Registering Shaders

In your main.go or init function:

eng.InitFunc = func() {
    // Load with standard vertex shader (pass "" for vsPath)
    myShader, err := eng.LoadShaderFromFiles("", "shaders/my_effect.fs")
    if err != nil {
        log.Fatal(err)
    }
    eng.ShaderRegistry().Add("my_effect", myShader)

    // Create material with shader and color
    err = eng.CreateMaterial("my_material", myShader, rl.Red)
    if err != nil {
        log.Fatal(err)
    }

    // Or create textured material
    err = eng.CreateMaterialTextured("my_textured_mat", myShader, "textures/wood.png")
    if err != nil {
        log.Fatal(err)
    }
}

Using Shaders on Entities

Via RenderMesh Component

// Use material ID (shader embedded in material)
scene.SetRenderMesh(entity, component.RenderMeshData{
    MeshId:     "cube",
    MaterialId: "my_material",
})

// Or override shader on any material
scene.SetRenderMesh(entity, component.RenderMeshData{
    MeshId:     "cube",
    MaterialId: "default",
    ShaderId:   "my_effect",  // Override material's shader
})

Via Entity Builder

entityId := scene.Spawn("MyEntity").
    At(math.NewVector3(0, 1, 0)).
    Mesh("cube", "my_material").  // Uses material's embedded shader
    Build()

Available Injected Uniforms

These are automatically available in your fragment shader after injection:

Uniform Type Description
lightDir vec3 Directional light direction (normalized)
lightColor vec4 Light color (RGBA)
ambient vec4 Ambient light color
viewPos vec3 Camera position in world space
lightVP mat4 Light's view-projection matrix (for shadows)
shadowMap sampler2D Shadow depth texture
shadowMapResolution int Shadow map size (e.g., 2048)

Injected Functions

// Get shadow factor at world position
// Returns: 1.0 = fully lit, 0.0 = fully in shadow
// Uses PCF for soft shadow edges
float getShadow(vec3 fragPos, vec3 normal);

Time Uniform

The engine automatically updates a time uniform each frame:

uniform float time;  // Seconds since game start

void main() {
    // Animate based on time
    float wave = sin(time * 2.0) * 0.5 + 0.5;
    // ...
}

Custom Vertex Shader

Only needed for special effects (vertex animation, displacement). Use standard for most cases.

#version 330

// Required inputs
in vec3 vertexPosition;
in vec2 vertexTexCoord;
in vec3 vertexNormal;
in vec4 vertexColor;

// Required uniforms
uniform mat4 mvp;
uniform mat4 matModel;
uniform mat4 matNormal;

// Required outputs (for fragment shader and shadow system)
out vec3 fragPosition;
out vec2 fragTexCoord;
out vec4 fragColor;
out vec3 fragNormal;

// Custom uniforms
uniform float time;

void main()
{
    // Custom vertex manipulation
    vec3 pos = vertexPosition;
    pos.y += sin(pos.x * 3.0 + time) * 0.2;  // Wave effect

    // Transform to world space
    fragPosition = vec3(matModel * vec4(pos, 1.0));
    fragTexCoord = vertexTexCoord;
    fragColor = vertexColor;
    fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 1.0)));

    // Final position
    gl_Position = mvp * vec4(pos, 1.0);
}

Load with custom vertex shader:

shader, err := eng.LoadShaderFromFiles("shaders/wave.vs", "shaders/wave.fs")

Common Shader Recipes

Toon/Cel Shading

// Quantize lighting into discrete bands
float toonFactor;
if (NdotL * shadow > 0.8) {
    toonFactor = 1.0;
} else if (NdotL * shadow > 0.4) {
    toonFactor = 0.7;
} else if (NdotL * shadow > 0.2) {
    toonFactor = 0.45;
} else {
    toonFactor = 0.25;
}

vec3 litColor = baseColor * toonFactor + baseColor * ambient.rgb;

Rim/Fresnel Glow

vec3 viewDir = normalize(viewPos - fragPosition);
float fresnel = 1.0 - max(dot(normal, viewDir), 0.0);
fresnel = pow(fresnel, 2.0);

vec3 rimColor = vec3(0.0, 0.8, 1.0) * fresnel * 2.0;
finalColor.rgb += rimColor;

Hologram Scan Lines

float scanLineSpeed = 2.0;
float scanLineFreq = 50.0;
float scanLine = sin((fragPosition.y + time * scanLineSpeed) * scanLineFreq) * 0.5 + 0.5;
scanLine = pow(scanLine, 1.5);

finalColor.a *= scanLine;

Pulse/Glow Effect

float pulse = sin(time * 3.0) * 0.3 + 0.7;
vec3 glowColor = vec3(1.0, 0.5, 0.0);  // Orange
finalColor.rgb += glowColor * pulse * fresnel;

Dissolve Effect

uniform float dissolveAmount;  // 0.0 to 1.0

// Pseudo-random based on world position
float noise = fract(sin(dot(fragPosition.xz, vec2(12.9898, 78.233))) * 43758.5453);

if (noise < dissolveAmount) {
    discard;  // Remove fragment
}

// Glow at dissolve edge
if (noise < dissolveAmount + 0.05) {
    finalColor.rgb += vec3(1.0, 0.5, 0.0) * 2.0;
}

Setting Custom Uniforms

For dynamic uniforms beyond time:

// In a system's Update or init
shader, _ := eng.ShaderRegistry().Get("my_shader")
loc := rl.GetShaderLocation(shader, "customValue")
rl.SetShaderValue(shader, loc, []float32{1.5}, rl.ShaderUniformFloat)

// For vec3:
rl.SetShaderValue(shader, loc, []float32{1.0, 2.0, 3.0}, rl.ShaderUniformVec3)

Transparent Materials

For transparent shaders, mark the RenderMesh:

scene.SetRenderMesh(entity, component.RenderMeshData{
    MeshId:        "cube",
    MaterialId:    "hologram_material",
    IsTransparent: true,  // Renders in transparent pass
})

Transparent entities:

  • Sorted back-to-front by view depth
  • Don't cast shadows
  • Rendered after opaque geometry

Debugging Shaders

  1. Compile Errors: Check console output when loading shader
  2. Visual Debug: Output intermediate values as colors:

``glsl finalColor = vec4(normal * 0.5 + 0.5, 1.0); // Visualize normals finalColor = vec4(vec3(shadow), 1.0); // Visualize shadows ``

  1. Debug Color Shader: Use shaders/debug_color.fs as reference

Example Shaders in Project

See existing implementations:

  • Toon shader: game/assets/shaders/example_toon.fs
  • Hologram: game/assets/shaders/hologram.vs, hologram.fs
  • Debug: game/assets/shaders/debug_color.fs
  • Standard vertex: engine/core/shader.go (embedded)

Important Notes

  1. Shadow Injection: Engine auto-injects shadow code after #version line
  2. Standard Vertex Shader: Pass "" for vsPath to use built-in
  3. Gamma Correction: Apply pow(color, 1/2.2) at end for correct display
  4. Required Varyings: Fragment shader MUST declare expected varyings from vertex shader
  5. Time Uniform: Declare uniform float time; to use it (engine updates automatically)