Xor Shaders
  • Tutorials
  • About

Converting Shadertoy Shaders

3/8/2015

17 Comments

 
Shaders on websites like Shadertoy.com or GLSLsandbox.com work a little different than shaders in GameMaker: Studio. Today I'll be talking about Shadertoy.com. For this example, we will port my "Pixel Noise" shader to GameMaker (code also below), so use that for the code. 
Note that because the shaders are designed for Shadertoy.com, they won't always be fully optimized for GameMaker. It can often be best to just design a shader yourself. This tutorial may not cover all the differences, but it should help you convert most shaders from Shadertoy.com.
The shaders on Shadertoy.com don't need textures and most of them don't even use textures (my shader does not use any textures either). If a shader does use textures it will show the textures at the bottom right (at the iChannel0-iChannel3). In GameMaker: Studio, shaders must  be applied to something, so most of the shaders designed for GameMaker, are applied to sprites, backgrounds, or in 3D they can be applied to planes. That means shaders in GameMaker must have at least one texture (although that texture can be a blank rectangle). Another difference is Shadertoy shaders recently started using fragColor for gl_FragColor. To remove errors simply change those back to gl_FragColor. Also "void mainImage( out vec4 fragColor, in vec2 fragCoord )" should be replaced with this "void main( void)".

Shaders on shadertoy.com

Varying Vectors

Check out tutorial 4 if you haven't already for information about varying vectors. You will need to create a varying vec2 called fragCoord. Then set it in the vertex shader to "in_Position.xy". You can also remove v_vColour and v_vTexcoord because they won' be used in this shader.

Uniforms

To see all the uniforms you may need, click the "Shader inputs" menu above the shader code. This will show all the uniforms ShaderToy uses. You should copy all the uniforms the specific shader uses in to the shader in GameMaker. Make sure you see tutorial 5 part 2 for details about uniforms.

Texture Differences

 If a shader does not use textures, then you just apply the shader to a rectangle or blank texture. In order to get textures, you must use uniform samplers (unless it's just one texture). With one texture you could just use the base texture (which is whatever you applied it to). For now we won't worry about textures since we're not using them.

Summary

So to make things easier I'll make a list for you. So when converting shaders you'll most likely need to change all these things:
  • Change "fragColor" to "gl_FragColor"
  • Change "void mainImage( out vec4 fragColor, in vec2 fragCoord )" to "void main( void)"
  • Create the varying vec2 called "fragCoord"
  • Remove unneeded attribute vectors
  • Add the needed uniforms including the samplers
That's it! Here is the Shadertoy code:

float rand(vec3 n)
{
  return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);   
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);
    float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);
fragColor = vec4(vec3(b*0.6),1.0);
}


Here's the converted fragment code:

uniform float iGlobalTime;
varying vec2 fragCoord;
float rand(vec3 n)
{
 return fract(abs(sin(dot(n,vec3(4.3357,-5.8464,6.7645))*52.47))*256.75+0.325);   
}
void main(void)
{
    vec3 p = vec3(fragCoord.xy+vec2(iGlobalTime)*64.0,0.0);
    float b = (rand(floor(p/64.0))*0.5+rand(floor(p/32.0))*0.3+rand(floor(p/16.0))*0.2);
    gl_FragColor = vec4(vec3(b*0.6),1.0);
}


Now here is the vertex code:

attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
//attribute vec4 in_Colour;                    // (r,g,b,a) unused in this shader.
//attribute vec2 in_TextureCoord;              // (u,v)     unused in this shader.

varying vec2 fragCoord;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
    
    fragCoord = in_Position.xy;
}


Here's the example file:
DOWNLOAD EXAMPLE
For a challenge you can convert my "Cool Lake"shader.
17 Comments
Odolwa
6/8/2016 12:10:22 pm

Thanks for providing us with these tutorials, Xor. Just one question I had. In the link to the ShaderToy site, the shader is moving across the screen but the code doesn't seem to have this feature. Is there a way to implement that in your code?

Reply
Xor
6/8/2016 12:52:30 pm

It appears that something happened to the uniform and varying vectors sections. I'll try to fix them when I can because you need uniforms. In the example it should be animated. That is because you add a uniform for time which is just a changing float from outside of the shader which is set to the time in seconds since the game starts. Then this is added to the position to make motion. I hope this clears things up. Thanks for reading!

Reply
Odolwa
6/8/2016 01:27:58 pm

Thanks for the reply. I gave it another shot based on your suggestion and got it working. Also, just so you know, I think the download links on some of your earlier tutorials have stopped working. This one appears to be fine though.

Xor
6/8/2016 07:07:59 pm

Thanks for the heads up. I'll take a look.

Xor
6/8/2016 07:17:42 pm

I just checked them all. I can't find any broken links. Can you check again? Thanks

Odolwa
6/9/2016 12:36:43 pm

Sorry for the delay in responding. Going back over them all, the following didn't work for me: Tutorials 1, 4 & 5 (part 1), as well as the sepia (in 3) & radial blur (in 5 part 2) examples. Everything else was fine.

Reply
Jesse
7/6/2016 02:02:07 am

Hi Xor! Thanks again for these tutorials. They're the most helpful thing I've found on the internet for shader tutorials.

I got the Cool Lake shader to work, but the camera is upside down! I don't know enough about 3D, but I'm guessing there's an easy fix. Maybe just a negative sign, or add 180 degrees somewhere?

Reply
Xor link
7/6/2016 10:00:42 am

Glad to hear you got it to work. The difference is that GM flips the y axis compared to pretty much anything else, including ShaderToy.
To counter this just have to flip the p vec2 like this (after it is set):
p.y = -p.y;
This way you don't need to do any 3D math either.

Reply
Jesse
7/6/2016 10:06:47 pm

Great! It worked! But then, when looking with the camera, the y-axis was "inverted" and the x-axis became "normal". So I found I could fix that by doing:
p *= -1.0
Now both camera axes are "inverted."
I'be slowly going through all the tutorials over the next few days. Looking forward to the new website. Cheers.

Rianon
1/24/2017 04:40:03 pm

I really want to look how you did it.
Tryed it myself, but no luck. In fact, it's works some way, but camera shows some weird angle and mouse input seems not working.

Reply
Xor link
1/24/2017 07:15:19 pm

Interesting. There may have been changes to shadertoy that I didn't notice. Send me an email with the code and I'll have a look.

WordedPuppet
12/31/2016 11:42:39 pm

I need help, i'm trying to convert this shader:

https://www.shadertoy.com/view/ldXGW4

here's what i tried:

// change these values to 0.0 to turn off individual effects
float vertJerkOpt = 1.0;
float vertMovementOpt = 1.0;
float bottomStaticOpt = 1.0;
float scalinesOpt = 1.0;
float rgbOffsetOpt = 1.0;
float horzFuzzOpt = 1.0;
uniform float iGlobalTime

// Noise generation functions borrowed from:
// https://github.com/ashima/webgl-noise/blob/master/src/noise2D.glsl

vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}


vec2 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}

vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}

float snoise(vec2 v)
{
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);

// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;

// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));

vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;

// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)

vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;

// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );

// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}

float staticV(vec2 uv) {
float staticHeight = snoise(vec2(9.0,iGlobalTime*1.2+3.0))*0.3+5.0;
float staticAmount = snoise(vec2(1.0,iGlobalTime*1.2-6.0))*0.1+0.3;
float staticStrength = snoise(vec2(-9.75,iGlobalTime*0.6-3.0))*2.0+2.0;
return (1.0-step(snoise(vec2(5.0*pow(iGlobalTime,2.0)+pow(uv.x*7.0,1.2),pow((mod(iGlobalTime,100.0)+100.0)*uv.y*0.3+3.0,staticHeight))),staticAmount))*staticStrength;
}


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

vec2 uv = fragCoord.xy/iResolution.xy;

float jerkOffset = (1.0-step(snoise(vec2(iGlobalTime*1.3,5.0)),0.8))*0.05;

float fuzzOffset = snoise(vec2(iGlobalTime*15.0,uv.y*80.0))*0.003;
float largeFuzzOffset = snoise(vec2(iGlobalTime*1.0,uv.y*25.0))*0.004;

float vertMovementOn = (1.0-step(snoise(vec2(iGlobalTime*0.2,8.0)),0.4))*vertMovementOpt;
float vertJerk = (1.0-step(snoise(vec2(iGlobalTime*1.5,5.0)),0.6))*vertJerkOpt;
float vertJerk2 = (1.0-step(snoise(vec2(iGlobalTime*5.5,5.0)),0.2))*vertJerkOpt;
float yOffset = abs(sin(iGlobalTime)*4.0)*vertMovementOn+vertJerk*vertJerk2*0.3;
float y = mod(uv.y+yOffset,1.0);


float xOffset = (fuzzOffset + largeFuzzOffset) * horzFuzzOpt;

float staticVal = 0.0;

for (float y = -1.0; y <= 1.0; y += 1.0) {
float maxDist = 5.0/200.0;
float dist = y/200.0;
staticVal += staticV(vec2(uv.x,uv.y+dist))*(maxDist-abs(dist))*1.5;
}

staticVal *= bottomStaticOpt;

float red = texture2D(iChannel0, vec2(uv.x + xOffset -0.01*rgbOffsetOpt,y)).r+staticVal;
float green = texture2D(iChannel0, vec2(uv.x + xOffset, y)).g+staticVal;
float blue =texture2D(iChannel0, vec2(uv.x + xOffset +0.01*rgbOffsetOpt,y)).b+staticVal;

vec3 color = vec3(red,green,blue);
float scanline = sin(uv.y*800.0)*0.04*scalinesOpt;
color -= scanline;

fragColor = vec4(color,1.0);

i'm very new to shaders, i need help >_<

Reply
WordedPuppet
1/2/2017 11:38:18 am

Also i couldn't fix 1 error so i couldn't fix the others because i didn't know what it was

Reply
Xor link
1/5/2017 08:58:10 pm

Okay. I put together an example (https://www.dropbox.com/s/8n1abvj5jmzw3e7/DistortedTV.gmz?dl=0)
And of course remember to credit the original creator!

WordedPuppet
1/6/2017 06:18:51 pm

Thank you!

WordedPuppet
11/26/2017 02:32:02 am

I feel really stupid right now.

Navid Ansari
10/5/2021 11:48:30 pm

hi i check this and everything is flipped . how we can fix that?




Leave a Reply.

    Tutorials

    New tutorial at GMshaders.com!

    Categories

    All
    3D
    Beginner
    Intermediate

    Archives

    October 2021
    November 2019
    January 2019
    May 2016
    August 2015
    June 2015
    March 2015
    February 2015
    January 2015
    December 2014

    RSS Feed

Powered by Create your own unique website with customizable templates.