-
Notifications
You must be signed in to change notification settings - Fork 22
Making or Porting a Shader
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
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 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 frameAn 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
{
"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:
-
nameshown in the dropdown, falls back to the folder name -
fragmentthe shader file, falls back to the first.fragor.glslin the folder -
parametersvalues 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!
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
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 texturealso 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!
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.
verbatim = "in the exact same words as the original content"