Porting a Shadertoy shader to Unity (GLSL to HLSL): a conversion guide

Updated 2026-10-12

Shadertoy is the best place to find inspiring shaders, and Unity is where many people want to use them. The languages look different at first glance, but Shadertoy code is GLSL and Unity's is HLSL, and they are close relatives. Most of the work in a port is renaming. A few differences really can break the result, so this guide covers both.

Step 1: the straight renames

These are the same operation under a different name:

GLSL (Shadertoy)HLSL (Unity)
vec2, vec3, vec4float2, float3, float4
mat2, mat3, mat4float2x2, float3x3, float4x4
mix(a, b, t)lerp(a, b, t)
fract(x)frac(x)
atan(y, x)atan2(y, x)
clamp(x, 0.0, 1.0)saturate(x) (or keep clamp, both work)
inversesqrt(x)rsqrt(x)
dFdx(x), dFdy(x)ddx(x), ddy(x)

Everything else you commonly see (sin, cos, pow, dot, cross, normalize, length, smoothstep, step, min, max, abs, floor) has the same name and meaning.

Step 2: the Shadertoy inputs

Shadertoy gives your code special variables. In Unity you supply the equivalents yourself:

  • iTime becomes _Time.y, a built-in that holds the time in seconds (the four values of _Time are t/20, t, 2t and 3t).
  • iResolution.xy becomes _ScreenParams.xy for the screen, or the size of the texture you are drawing into.
  • fragCoord (the pixel position) is easiest to replace with a UV from the mesh: multiply it by the size to get pixels, or divide pixels by the size to get UV.
  • iChannel0...3 (textures) become sampler2D properties that you assign in the material.

The entry point changes too. Shadertoy writes void mainImage(out vec4 fragColor, in vec2 fragCoord) and assigns to fragColor. In Unity your fragment function returns the color.

A worked example

Here is a small Shadertoy shader, a color-cycling gradient:

// Shadertoy (GLSL)
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
    vec2 uv = fragCoord / iResolution.xy;
    vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));
    fragColor = vec4(col, 1.0);
}

and the Unity version of the fragment function:

// Unity (HLSL), inside a shader pass
float4 frag(float2 uv : TEXCOORD0) : SV_Target
{
    float3 col = 0.5 + 0.5 * cos(_Time.y + uv.xyx + float3(0, 2, 4));
    return float4(col, 1.0);
}

Only three things changed: the entry point, the types, and the names of the inputs. The maths in the middle is identical. If you would rather not wire up the surrounding shader file by hand, ShaderBattle's export button writes a complete Unity file for you: write the effect in the editor, and it wraps your function in the boilerplate.

Step 3: the differences that actually bite

mod is not fmod for negative numbers

GLSL mod(x, y) returns x - y * floor(x / y), which is always positive when y is positive. HLSL fmod(x, y) keeps the sign of x, like the % operator in C. For positive numbers they agree; for negative numbers they do not, and tiling patterns break in just the places where coordinates go below zero. Write the GLSL definition by hand:

float mod(float x, float y) { return x - y * floor(x / y); }

Matrix multiplication reads the other way

In GLSL you write M * v. In HLSL you call mul(M, v), and the order of the arguments matters. If a rotation spins the wrong way after porting, try swapping the arguments to mul. This site's matrices lesson explains how the two conventions relate.

Integers and floats

Strict GLSL (including WebGL 2) refuses to mix 1 and 1.0 in many places, which is why Shadertoy code is full of 0. and 2.0. HLSL is more forgiving, so code that was already correct in GLSL will compile. Going the other way, from HLSL to GLSL, is where you meet the errors.

Which way is up?

The vertical direction of UV, and of render textures, can differ between graphics APIs and platforms. If the picture appears upside down, flip the y coordinate. Test with a plain gradient first so you know what you are seeing.

Loops and arrays

Many Shadertoy shaders use large loops (ray marching, fractals) that are fine on a desktop GPU but slow on a phone. When you port for mobile, lower the iteration counts and test on real hardware. See the performance guide for what to look for.

A porting checklist

  1. Rename the types and functions using the table.
  2. Replace mainImage with a function that returns the color.
  3. Replace iTime, iResolution and fragCoord with Unity's equivalents.
  4. Replace GLSL mod with your own version that handles negative numbers.
  5. Check any matrix multiplication and flip the order if the result is mirrored or spun the wrong way.
  6. Look at a plain gradient to confirm which way is up, then compare against Shadertoy.

Learn the pieces

If a ported shader does something you do not understand, the lessons explain the building blocks one at a time, in your choice of HLSL or GLSL: start at Every pixel, one function, and see Taking it into Unity for the Unity side.

Keep reading

Start the interactive lessons