Skip to content

Making or Porting a Shader

flaffy edited this page Aug 8, 2026 · 3 revisions

This shows how to make or port a shader to ShaderMod

Its worth noting that the shader mod is not tied to any game, it works on any port made with RecompOne

Shader layout

A shader is a folder inside the shader mod's shaders/ directory, it has to contain:

 settings.json > name and parameters
 myshader.frag > the fragment shader

drop the folder in, press Rescan in the mod settings, and it shows up in the dropdown

The fragment shader

The shader is a single fragment shader that runs over the finished frame. the runtime provides the vertex stage and these uniforms bellow

#version 330 core
in vec2 vUv;
out vec4 oColor;

uniform sampler2D uTex; // the rendered frame
uniform vec2 uTexSize; // size of uTex in pixels
uniform vec2 uOutputSize; // size of the output, same as uTexSize as of now
uniform float uTime; // seconds since start
uniform int uFrame; // frames since start, use this for anything per frame

An very simple example of a shader can be:

#version 330 core
in vec2 vUv;
out vec4 oColor;
uniform sampler2D uTex;

void main()
{
    vec3 c = texture(uTex, vUv).rgb;
    oColor = vec4(1.0 - c, 1.0);
}

note to use uFrame and not uTime for effects that must advance once per frame, like interlacing for example, uTime moves with real time, so it changes with the framerate

settings.json

{
    "name": "My Shader",
    "author": "you",
    "description": "what it does",
    "fragment": "myshader.frag",
    "parameters": [
        { "uniform": "strength", "name": "Strength", "default": 0.5, "min": 0.0, "max": 1.0, "step": 0.05 },
        { "uniform": "enableThing", "name": "Enable thing", "type": "bool", "default": 1.0 }
    ]
}

Fields:

  • name shown in the dropdown, falls back to the folder name
  • fragment the shader file, falls back to the first .frag or .glsl in the folder
  • parameters values pushed into the shader as uniforms

each parameter need a uniform that matches a uniform float declared in the shader, the value comes from value if present, oterwise from default, so you can keep the original default around while overriding it

{ "uniform": "strength", "default": 0.5, "value": 0.8 }

min, max, step and type are not used yet, they are there for a future settings ui. as of now you edit the json by hand

Parameters are declared in the shader as plain floats, there are no bools or ints so:

uniform float strength;
uniform float enableThing;

A uniform that is in the json but not in the shader is ignored, no error so that means a typo in uniform will fail with no warns, check the name if a parameter seems to do nothing!

Iterating

editing a shader does not need to restart the game, change the .frag or the settings.json, then press Reload in the mod settings and it recompiles from disk and auto-aplies

if the shader fail to compile, the error from the driver is shown in red in the mod settings. so fix and press Reload again

Porting an existing shader

Most shaders you find are written for RetroArch or libretro, they are close enough to port, the work is mapping their uniforms onto RecompOne's

An libretro shader is usually one file holding both stages, split by #if defined(VERTEX) and #elif defined(FRAGMENT), take the fragment half and drop the rest, recompone only needs the frag

then map the names, the best way to do it is a block of defines at the top, so the body of the original stays there, so it can looks something like this:

#define Texture     uTex
#define TextureSize uTexSize
#define InputSize   uTexSize
#define OutputSize  uOutputSize
#define FrameCount  uFrame
#define FragColor   oColor
#define COMPAT_TEXTURE texture

also note that InputSize and TextureSize are the same value, the frame RecompOne hands for you has no padding around it

Some of the things usually need more edits are:

Values computed in the vertex stage. libretro shaders precompute things there and pass them as varyings, almost all of them derive only from uniforms, so declare them as globals and fill them at the top of main, RecompOne, as of now, does not provide them by itself

vec2 sinangle;
vec2 cosangle;

void main()
{
    sinangle = sin(vec2(x_tilt, y_tilt));
    cosangle = cos(vec2(x_tilt, y_tilt));
    // rest of the original main
}

of a varying depends on the texture coordinate rather than only on uniforms, recompute it from vUv, this should work

Helper functions that lived in the vertex stage. Copy them over verbatim, they usually only call each other

Output wrappers. some shaders write through a struct, replace it with a plain write to oColor

Their #pragma parameter lines map one to one onto settings.json. The format is:

#pragma parameter NAME "Label" default min max step

so a line like

#pragma parameter CRTgamma "Target Gamma" 2.4 0.1 5.0 0.1

becomes

{ "uniform": "CRTgamma", "name": "Target Gamma", "default": 2.4, "min": 0.1, "max": 5.0, "step": 0.1 }

Keep every parameter, even the ones you do not plan to expose. they are already the shader's defaults and leaving them out means the uniform stays at zero!

the crt-geom shader that ships with the mod was ported this way, read it side by side with the libretro original if you get stuck, this can help you understanding RecompOne's shader system!

Licensing

Shaders carry licenses, crt-geom is GPL, many others are too. If you ship a port of someone else's shader, keep the original header comment and credit the author, there is a license field in settings.json for this, only ship shaders that the license allow.

note

verbatim = "in the exact same words as the original content"

Clone this wiki locally