Skip to content

Shader examples#

Back to shader guides · Try in playground

Example 1: Fragment, Simple Filter (blue.hlsl)#

Stage: Fragment · Hook: INJECT_BEFORE_RETURN (default)

Simple post-TEV tint: guard with input.position.w, then write prev.rgb.

// Guard check: only apply to 3D perspective elements (ignores 2D HUD/Text)
bool is3D = input.position.w != 1.0f;
if (is3D) {
    // Modify the output color: clear red and green channels, keeping blue intact
    prev.rgb = float3(0.0f, 0.0f, prev.b);
}

Example 2: Fragment, Screen Space Effect (crt.hlsl)#

Stage: Fragment · Hook: INJECT_BEFORE_RETURN (default)

Screen-space effect: normalized UVs from input.position and _timePad, then modify prev after TEV.

bool is3D = input.position.w != 1.0f;
if (is3D) {
    // Normalize absolute screen pixel coordinates to standard [0.0, 1.0] UV range
    float2 uv = input.position.xy * float2(_timePad.x, _timePad.y);

    // Convert coordinates to centered range [-1.0, 1.0] for radial math
    float2 centeredUV = uv * 2.0f - 1.0f;

    // Apply CRT Screen Curvature (barrel distortion)
    float2 distortion = float2(0.04f, 0.05f); // horizontal and vertical distortion factors
    centeredUV.x += (centeredUV.y * centeredUV.y) * distortion.x * centeredUV.x;
    centeredUV.y += (centeredUV.x * centeredUV.x) * distortion.y * centeredUV.y;

    // Convert back to standard [0, 1] range to sample and clip edges
    float2 distortedUV = (centeredUV + 1.0f) * 0.5f;

    // Generate outer screen bezel vignette shadow
    float2 vignetteUV = centeredUV * centeredUV;
    float vign = saturate((1.0f - vignetteUV.x) * (1.0f - vignetteUV.y) * 15.0f);
    vign = pow(vign, 0.25f); // Soften the edge

    // Only render the screen effects if the warped coordinates land inside visible bounds
    if (distortedUV.x >= 0.0f && distortedUV.x <= 1.0f && distortedUV.y >= 0.0f && distortedUV.y <= 1.0f) {

        // Generate vertical scanlines based on the screen y coordinate
        float scanline = sin(input.position.y * 1.4f) * 0.12f + 0.88f;
        prev.rgb *= scanline;

        // Phosphor Grid sub-pixels
        int pixelX = int(input.position.x);
        float3 phosphor = float3(1.0f, 1.0f, 1.0f);
        if (pixelX % 3 == 0) {
            phosphor = float3(1.08f, 0.85f, 0.85f); // Red sub-pixel boost
        } else if (pixelX % 3 == 1) {
            phosphor = float3(0.85f, 1.08f, 0.85f); // Green sub-pixel boost
        } else {
            phosphor = float3(0.85f, 0.85f, 1.08f); // Blue sub-pixel boost
        }
        prev.rgb *= phosphor;

        // Adjust contrast and apply bezel shadow
        prev.rgb = saturate(prev.rgb * 1.05f + 0.02f);
        prev.rgb *= vign;

    } else {
        // Outside the screen warp boundaries, draw black bezels
        prev.rgb = float3(0.0f, 0.0f, 0.0f);
    }
}

Example 3: Fragment, Animated Shader (drunk.hlsl)#

Stage: Fragment · Hook: INJECT_BEFORE_RETURN (default)

Animated screen-space effect using u_time, input.position, and prev after TEV.

#define PI 3.14159265f

bool is3D = input.position.w != 1.0f;
if (is3D) {
    float2 uv = input.position.xy * float2(_timePad.x, _timePad.y);
    float2 centeredUV = uv * 2.0f - 1.0f;

    // Swaying Vignette: drift the vignette center over time using sine/cosine loops (periods of 0.5s and 0.75s)
    float2 vigCenter = float2(
        sin(u_time * (2.0f * PI / 0.5f)) * 0.12f,
        cos(u_time * (2.0f * PI / 0.75f)) * 0.10f
    );
    float dist = length(centeredUV - vigCenter);
    float pulse = sin(u_time * (2.0f * PI / 1.0f)) * 0.03f; // 1.0s period
    float vignette = saturate(1.0f - smoothstep(0.4f + pulse, 1.25f + pulse, dist));

    // Chromatic double vision wave offsets (shifting coordinate maps over time)
    float t = u_time * (2.0f * PI / 2.5f); // 2.5s base wave period
    float waveX = sin(centeredUV.y * 8.0f + t) * cos(centeredUV.x * 3.0f - t * 0.5f) * 0.15f;
    float waveY = cos(centeredUV.x * 8.0f - t * 1.2f) * sin(centeredUV.y * 3.0f + t * 0.6f) * 0.15f;

    // Separate color channel offsets
    float rShift = sin((centeredUV.x + waveX) * 25.0f + t) * 0.12f + 0.88f;
    float gShift = cos((centeredUV.y + waveY) * 22.0f - t * 0.8f) * 0.12f + 0.88f;
    float bShift = sin((centeredUV.x - centeredUV.y + waveX - waveY) * 20.0f + t * 1.3f) * 0.12f + 0.88f;

    prev.r *= rShift;
    prev.g *= gShift;
    prev.b *= bShift;

    // Color tint and screen brightness flicker
    prev.g *= 1.05f; // Sickly green tint
    prev.rgb *= (sin(u_time * (2.0f * PI / 1.111f)) * 0.08f + 0.92f); // ~1.111s pulsing flicker period (frequency 1.8)

    // Apply vignette boundary shadow
    float3 vignetteColor = float3(0.015f, 0.008f, 0.025f);
    prev.rgb = lerp(vignetteColor, prev.rgb, vignette);
    prev.rgb = saturate(prev.rgb);
}

Example 4: Vertex, Outline Hull (outline_hull.vs.hlsl)#

Stage: Vertex · Hook: INJECT_BEFORE_TRANSFORM (automatic for .vs.hlsl)

Geometry effect: modifies position along normal in model space before skinning / MV.

if (u_projection[3][3] == 0.0f) {
    float outlineWidth = 3.5f;
    position += normal * outlineWidth;
}
Outline Hull In-Game Preview