> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/dolphin-emu/dolphin/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Pipeline Graphics Mods

> Replace Dolphin pixel shaders with custom GLSL code

## Overview

Custom Pipeline graphics mods allow advanced users to replace Dolphin's internal pixel shader with custom GLSL code. This enables complete control over final pixel output, including custom lighting, normal mapping, texture manipulation, and visual effects.

<Warning>
  This is an advanced feature requiring knowledge of GLSL shading and graphics programming.
</Warning>

## Graphics Mod Integration

Custom pipelines are defined as graphics mod actions. You must be familiar with the [graphics mod format](/advanced/graphics-mods) before using custom pipelines.

### Action Type

The action type is `custom_pipeline` with the following structure:

<ResponseField name="passes" type="array" required>
  Array of rendering passes. Currently only single-pass is supported.

  <Expandable title="Pass Object">
    <ResponseField name="pixel_material_asset" type="string" required>
      Name of the material asset containing shader information
    </ResponseField>
  </Expandable>
</ResponseField>

## Complete Example

<CodeGroup>
  ```json Graphics Mod Descriptor theme={null}
  {
    "assets": [
      {
        "name": "material_replace_normal",
        "data": {
          "": "normal.material.json"
        }
      },
      {
        "name": "shader_replace_normal",
        "data": {
          "metadata": "replace_normal.shader.json",
          "shader": "replace_normal.glsl"
        }
      },
      {
        "name": "normal_texture",
        "data": {
          "texture": "normal_texture.png"
        }
      }
    ],
    "features": [
      {
        "action": "custom_pipeline",
        "action_data": {
          "passes": [
            {
              "pixel_material_asset": "material_replace_normal"
            }
          ]
        },
        "group": "PipelineTarget"
      }
    ],
    "groups": [
      {
        "name": "PipelineTarget",
        "targets": [
          {
            "texture_filename": "tex1_512x512_m_afdbe7efg332229e_14",
            "type": "draw_started"
          },
          {
            "texture_filename": "tex1_512x512_m_afdbe7efg332229e_14",
            "type": "create_texture"
          }
        ]
      }
    ]
  }
  ```
</CodeGroup>

## Shader Format

### Entry Point

All custom shaders must provide this entry point:

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
```

### Output Structure

<ResponseField name="CustomShaderOutput" type="struct">
  <Expandable title="Fields">
    <ResponseField name="main_rt" type="vec4">
      The main render target's output color (RGBA)
    </ResponseField>
  </Expandable>
</ResponseField>

### Input Data Structure

<ResponseField name="CustomShaderData" type="struct">
  <Expandable title="Fields">
    <ResponseField name="position" type="vec3">
      Pixel position in view space
    </ResponseField>

    <ResponseField name="normal" type="vec3">
      Pixel normal in view space
    </ResponseField>

    <ResponseField name="texcoord" type="vec3[]">
      Array of texture coordinates (size: `texcoord_count`)
    </ResponseField>

    <ResponseField name="texcoord_count" type="uint">
      Number of available texture coordinates
    </ResponseField>

    <ResponseField name="texmap_to_texcoord_index" type="uint[]">
      Maps texture units to texture coordinate indices
    </ResponseField>

    <ResponseField name="lights_chan0_color" type="CustomShaderLightData[]">
      Color lights for channel 0 (size: `light_chan0_color_count`)
    </ResponseField>

    <ResponseField name="lights_chan0_alpha" type="CustomShaderLightData[]">
      Alpha lights for channel 0
    </ResponseField>

    <ResponseField name="lights_chan1_color" type="CustomShaderLightData[]">
      Color lights for channel 1
    </ResponseField>

    <ResponseField name="lights_chan1_alpha" type="CustomShaderLightData[]">
      Alpha lights for channel 1
    </ResponseField>

    <ResponseField name="ambient_lighting" type="vec4[2]">
      Ambient lighting values for each color channel
    </ResponseField>

    <ResponseField name="base_material" type="vec4[2]">
      Base material values for each color channel
    </ResponseField>

    <ResponseField name="tev_stages" type="CustomShaderTevStage[]">
      TEV (Texture Environment) stages (size: `tev_stage_count`)
    </ResponseField>

    <ResponseField name="tev_stage_count" type="uint">
      Number of TEV stages
    </ResponseField>

    <ResponseField name="final_color" type="vec4">
      Final color after all TEV stages (what Dolphin would normally output)
    </ResponseField>

    <ResponseField name="time_ms" type="uint">
      Milliseconds since game started (useful for animations)
    </ResponseField>
  </Expandable>
</ResponseField>

## Lighting Data

<ResponseField name="CustomShaderLightData" type="struct">
  <Expandable title="Fields">
    <ResponseField name="position" type="vec3">
      Light position in view space
    </ResponseField>

    <ResponseField name="direction" type="vec3">
      Light direction in view space (point/spot lights)
    </ResponseField>

    <ResponseField name="color" type="vec3">
      Light color (RGB)
    </ResponseField>

    <ResponseField name="attenuation_type" type="uint">
      Attenuation type:

      * `CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_POINT` - Point light
      * `CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_DIR` - Directional light
      * `CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_SPOT` - Spot light
    </ResponseField>

    <ResponseField name="cosatt" type="vec4">
      Cosine attenuation values
    </ResponseField>

    <ResponseField name="distatt" type="vec4">
      Distance attenuation values
    </ResponseField>
  </Expandable>
</ResponseField>

## TEV Stage Data

TEV (Texture Environment) stages represent GameCube/Wii graphics operations.

<ResponseField name="CustomShaderTevStage" type="struct">
  <Expandable title="Fields">
    <ResponseField name="input_color" type="CustomShaderTevStageInputColor[4]">
      Four color inputs for this stage
    </ResponseField>

    <ResponseField name="input_alpha" type="CustomShaderTevStageInputAlpha[4]">
      Four alpha inputs for this stage
    </ResponseField>

    <ResponseField name="texmap" type="uint">
      Texture unit for this stage
    </ResponseField>

    <ResponseField name="output_color" type="vec4">
      Final output color from this stage
    </ResponseField>
  </Expandable>
</ResponseField>

### TEV Input Types

```glsl theme={null}
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_PREV     // From previous stage
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_COLOR    // From color data
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX      // From texture
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_RAS      // From rasterizer
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_KONST    // Constant from software
CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_NUMERIC  // Numeric constant (0 or 1)
```

## Shader Examples

### Single Color Output

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    CustomShaderOutput custom_output;
    custom_output.main_rt = vec4(1.0, 0.0, 0.0, 1.0);  // Red
    return custom_output;
}
```

### Display Normal Map

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    CustomShaderOutput custom_output;
    // Convert normal from [-1,1] to [0,1] for display
    custom_output.main_rt = vec4(data.normal * 0.5 + 0.5, 1);
    return custom_output;
}
```

### Reading Custom Texture

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    CustomShaderOutput custom_output;
    // samp_MY_TEX is defined in shader asset
    // TEX_COORD0 is first texture coordinate
    custom_output.main_rt = texture(samp_MY_TEX, TEX_COORD0);
    return custom_output;
}
```

### First Game Texture

Display the first texture the game uses, ignoring other operations:

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    vec4 final_color = data.final_color;
    uint texture_set = 0;
    
    for (uint i = 0; i < data.tev_stage_count; i++)
    {
        // Check 4 color inputs per stage
        for (uint j = 0; j < 4; j++)
        {
            if (data.tev_stages[i].input_color[j].input_type == 
                CUSTOM_SHADER_TEV_STAGE_INPUT_TYPE_TEX && texture_set == 0)
            {
                final_color = vec4(data.tev_stages[i].input_color[j].value, 1.0);
                texture_set = 1;
            }
        }
    }
    
    CustomShaderOutput custom_output;
    custom_output.main_rt = final_color;
    return custom_output;
}
```

### Point Light Lighting

Apply point lighting for channel 0 color lights:

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    float total_diffuse = 0;
    vec3 normal = data.normal;
    
    for (int i = 0; i < data.light_chan0_color_count; i++)
    {
        if (data.lights_chan0_color[i].attenuation_type == 
            CUSTOM_SHADER_LIGHTING_ATTENUATION_TYPE_POINT)
        {
            vec3 light_dir = normalize(
                data.lights_chan0_color[i].position - data.position.xyz
            );
            
            float attn = (dot(normal, light_dir) >= 0.0) 
                ? max(0.0, dot(normal, data.lights_chan0_color[i].direction.xyz)) 
                : 0.0;
            
            vec3 cosAttn = data.lights_chan0_color[i].cosatt.xyz;
            vec3 distAttn = data.lights_chan0_color[i].distatt.xyz;
            
            attn = max(0.0, dot(cosAttn, vec3(1.0, attn, attn*attn))) 
                / dot(distAttn, vec3(1.0, attn, attn * attn));
            
            total_diffuse += attn * max(0.0, dot(normal, light_dir));
        }
    }
    
    CustomShaderOutput custom_output;
    custom_output.main_rt = vec4(total_diffuse * vec3(0, 0, 1), 1); // Blue base
    return custom_output;
}
```

## Texture Sampling

Access textures using `samp_<NAME>` samplers defined in your material asset:

```glsl theme={null}
// Defined in material asset as "MY_TEX"
texture(samp_MY_TEX, data.texcoord[0])

// Built-in coordinate macros
texture(samp_MY_TEX, TEX_COORD0)
texture(samp_MY_TEX, TEX_COORD1)
```

## Time-Based Animation

Use `data.time_ms` for animated effects:

```glsl theme={null}
CustomShaderOutput custom_main(in CustomShaderData data)
{
    float time_sec = float(data.time_ms) / 1000.0;
    float pulse = (sin(time_sec * 2.0) + 1.0) / 2.0; // 0 to 1
    
    CustomShaderOutput custom_output;
    custom_output.main_rt = vec4(pulse, pulse, pulse, 1.0);
    return custom_output;
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Performance Considerations">
    * Minimize texture lookups in loops
    * Cache frequently accessed values
    * Use early returns when possible
    * Avoid complex branching in inner loops
  </Accordion>

  <Accordion title="Compatibility">
    * GLSL is converted to backend-specific shaders (D3D, Vulkan, Metal)
    * Test on multiple backends if possible
    * Avoid GLSL extensions unless necessary
    * Use standard GLSL types and functions
  </Accordion>

  <Accordion title="Debugging">
    * Start with simple solid colors to verify shader loads
    * Output intermediate values as colors for visualization
    * Check Dolphin log for shader compilation errors
    * Use `data.final_color` as fallback
  </Accordion>
</AccordionGroup>

<Note>
  Shaders are compiled at runtime. Syntax errors will prevent the graphics mod from loading.
</Note>

## Limitations

* **Single pass only**: Multi-pass rendering not currently supported
* **Pixel shaders only**: Vertex shaders cannot be customized
* **View space only**: Position and normal data are in view space, not world space
* **Backend conversion**: GLSL is converted to target API, some features may not translate

## Related Topics

<CardGroup cols={2}>
  <Card title="Graphics Mods" icon="palette" href="/advanced/graphics-mods">
    Learn the graphics mod system basics
  </Card>

  <Card title="Resource Packs" icon="box" href="/advanced/resource-packs">
    Package mods for distribution
  </Card>
</CardGroup>
