Skip to content

Preset Transition Shaders

Kai Blaschke edited this page Feb 6, 2024 · 1 revision

projectM Transition Shaders

Introduction

To blend presets, projectM uses a slightly different approach than Milkdrop.

Milkdrop blends presets on many different levels, e.g. individual effects, color values and shaders. While this works if both the new and old presets are rendered within the same function calls, this doesn't work with projectM, which encapsulates each single preset in a class hierarchy and also renders it individually. Additionally, if projectM supports additional preset formats in the future, this blending method will not work for obvious reasons.

In projectM, we chose to render both presets individually, then use the outputs of both render passes and use a (simple) fragment shader to combine the output of those presets into a single image, with a shader-defined transition effect rendered over the actual blend time.

How it Works

The transition shader is responsible for mixing the presets being blended into each other. Normally, the blend starts at progress 0.0 with all fragments showing the old preset and ends with displaying only the new preset if the progress reaches 1.0. The shader determines the animation in between, e.g. mixing or warping both images into a single one.

The transition can be a very simple linear blend of the whole image or consist of a more complex animated pattern.

In addition to only using the preset images, the shader may also add other effects on top, e.g. a burning effect, sparkles or any other pattern which makes the blend more interesting.

Transition Shader Structure

projectM uses GLSL shaders for all its rendering (preset HLSL code is transpiled to GLSL), so the transition shaders are naturally also written in that language. To maximize compatibility, projectM uses GLSL 3.30 (via OpenGL Core Profile 3.3 or OpenGL ES 3.x).

The actual shader files only need to contain the acutal "user" code, e.g. at least the mainImage() function, additional functions and #defines as needed. projectM will prepend a header, including the GLSL #version header appropriate for the current implementation, shader inputs and outputs, additional uniforms with useful data (see below) and some convenience definitions, plus the actual shader main()function.

The shader inputs and outputs are named in accordance with ShaderToy to make it easier to write and test shaders on this website.

Shader Entry Point

The shader main() function will always be defined by projectM at the end of the shader program, hence it is important not to declare it in the transition shader code.

The actual entry point for the user-specified shader code is, just as with ShaderToy, the following function stub:

void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
}

The fragColor output parameter will be initialized with vec4(1.0). if it's not set inside the function, the resulting fragment will be displayed as a white pixel.

Shader Inputs

These are the available inputs for the transition shader program:

  • vec2 fragCoord: The x and y components of the gl_fragCoord input variable.
  • vec3 iResolution: The actual viewport resolution projectM is rendering at.
  • float iProgressLinear: Linear transition progress, 0.0 to 1.0.
  • float iProgressCosine: Cosine transition progress, 0.0 to 1.0.
  • float iProgressBicubic: Bicubic transition progress, 0.0 to 1.0.
  • float iTransitionDuration: The whole transition duration in seconds.
  • float iTime: The time in seconds since the transition has been started. This is not the transition progress, but can be used to code effects which rely on real time.
  • float iTimeDelta: The time in seconds since the rendering the last frame.
  • float iFrameRate: Current rendering frame rate.
  • int iFrame: Current transition's playback frame.
  • ivec4 iRandStatic: Four random values, static for the whole transition.
  • ivec4 iRandFrame: Four random values, regenerated on each rendered frame.
  • float iBass: Low frequency loudness, centered around 1.0 with a range of about ±0.3.
  • float iMid: Mid frequency loudness, centered around 1.0 with a range of about ±0.3.
  • float iTreb: High frequency loudness, centered around 1.0 with a range of about ±0.3.
  • float iBassAtt: Low frequency loudness, attenuated over multiple frames, centered around 1.0 with a range of about ±0.3.
  • float iMidAtt: Mid frequency loudness, attenuated over multiple frames, centered around 1.0 with a range of about ±0.3.
  • float iTrebAtt: High frequency loudness, attenuated over multiple frames, centered around 1.0 with a range of about ±0.3.

All ShaderToy parameters not listed here will be unavailable.

Available Texture Samplers

The following texture samplers are available:

  • sampler2D iChannel0: Texture sampler for the old preset which is faded out.
  • sampler2D iChannel1: Texture sampler for the new preset which is faded in.
  • sampler2D iNoiseLQ: 256x256 pixel random, high-frequency 2D noise texture.
  • sampler2D iNoiseLQNearest: 256x256 pixel random, high-frequency 2D noise texture with "nearest" interpolation.
  • sampler2D iNoiseMQ: 256x256 pixel random, medium-frequency 2D noise texture.
  • sampler2D iNoiseMQNearest: 256x256 pixel random, medium-frequency 2D noise texture with "nearest" interpolation.
  • sampler2D iNoiseHQ: 256x256 pixel random, low-frequency 2D noise texture.
  • sampler2D iNoiseHQNearest: 256x256 pixel random, low-frequency 2D noise texture with "nearest" interpolation.
  • sampler3D iNoiseVolLQ: 32x32x32 pixel random, high-frequency 3D noise texture.
  • sampler3D iNoiseVolLQNearest: 32x32x32 pixel random, high-frequency 3D noise texture with "nearest" interpolation.
  • sampler3D iNoiseVolHQ: 32x32x32 pixel random, low-frequency 3D noise texture.
  • sampler3D iNoiseVolHQNearest: 32x32x32 pixel random, low-frequency 3D noise texture with "nearest" interpolation.

As of now, custom textures similar to Milkdrop presets are not supported. The texsize uniforms are also not available, as the preset images are the same size as iResolution, and the noise textures have fixed sizes as noted above.

Shader Output

The transition shader only supports a single output, the resulting fragment's color:

  • vec4 fragColor: The fragment's final color value. The alpha component will always be set to 1.0 internally to avoid rendering artifacts.