How to implement a ShaderToy shader in three.js?

INF1

I am not sure if you have answered your question, but it might be good for others to know the integration steps for shadertoys to THREEJS.

First, you need to know that shadertoys is a fragment shaders. That being said, you have to set a "general purpose" vertex shader that should work with all shadertoys (fragment shaders).

Step 1 Create a "general purpose" vertex shader

varying vec2 vUv; 
void main()
{
    vUv = uv;

    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 );
    gl_Position = projectionMatrix * mvPosition;
}

This vertex shader is pretty basic. Notice that we defined a varying variable vUv to tell the fragment shader where is the texture mapping. This is important because we are not going to use the screen resolution (iResolution) for our base rendering. We will use the texture coordinates instead. We have done that in order to integrate multiple shadertoys on different objects in the same THREEJS scene.

Step 2 Pick the shadertoys that we want and create the fragment shader. (I have chosen a simple toy that performs well: Simple tunnel 2D by niklashuss).

Here is the given code for this toy:

void main(void)
{
    vec2 p = gl_FragCoord.xy / iResolution.xy;
    vec2 q = p - vec2(0.5, 0.5);

    q.x += sin(iGlobalTime* 0.6) * 0.2;
    q.y += cos(iGlobalTime* 0.4) * 0.3;

    float len = length(q);

    float a = atan(q.y, q.x) + iGlobalTime * 0.3;
    float b = atan(q.y, q.x) + iGlobalTime * 0.3;
    float r1 = 0.3 / len + iGlobalTime * 0.5;
    float r2 = 0.2 / len + iGlobalTime * 0.5;

    float m = (1.0 + sin(iGlobalTime * 0.5)) / 2.0;
    vec4 tex1 = texture2D(iChannel0, vec2(a + 0.1 / len, r1 ));
    vec4 tex2 = texture2D(iChannel1, vec2(b + 0.1 / len, r2 ));
    vec3 col = vec3(mix(tex1, tex2, m));
    gl_FragColor = vec4(col * len * 1.5, 1.0);
}

Step 3 Customize the shadertoy raw code to have a complete GLSL fragment shader. The first thing missing out the code are the uniforms and varyings declaration. Add them at the top of your frag shader file (just copy and paste the following):

uniform float iGlobalTime;
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;

varying vec2 vUv;

Note, only the shadertoys variables used for that sample are declared, plus the varying vUv previously declared in our vertex shader.

The last thing we have to twick is the proper UV mapping, now that we have decided to not use the screen resolution. To do so, just replace the line that uses the IResolution uniforms i.e.:

vec2 p = gl_FragCoord.xy / iResolution.xy;

with:

vec2 p = -1.0 + 2.0 *vUv;

That's it, your shaders are now ready for usage in your THREEJS scenes.

Step 4 Your THREEJS code:

Set up uniform:

var tuniform = {
        iGlobalTime:    { type: 'f', value: 0.1 },
        iChannel0:  { type: 't', value: THREE.ImageUtils.loadTexture( 'textures/tex07.jpg') },
        iChannel1:  { type: 't', value: THREE.ImageUtils.loadTexture( 'textures/infi.jpg' ) },
    };

Make sure the textures are wrapping:

tuniform.iChannel0.value.wrapS = tuniform.iChannel0.value.wrapT = THREE.RepeatWrapping;
tuniform.iChannel1.value.wrapS = tuniform.iChannel1.value.wrapT = THREE.RepeatWrapping;

Create the material with your shaders and add it to a planegeometry. The planegeometry() will simulate the shadertoys 700x394 screen resolution, in other words it will best transfer the work the artist intented to share.

var mat = new THREE.ShaderMaterial( {
            uniforms: tuniform,
            vertexShader: vshader,
            fragmentShader: fshader,            
            side:THREE.DoubleSide
        } );

var tobject = new THREE.Mesh( new THREE.PlaneGeometry(700, 394,1,1), mat);

Finally, add the delta of the THREE.Clock() to iGlobalTime value and not the total time in your update function.

tuniform.iGlobalTime.value += clock.getDelta();

That is it, you are now able to run most of the shadertoys with this setup...

This is an old thread, but there's now an automated way to do this. Simply go to http://shaderfrog.com/app/editor/new and on the top right click "Import > ShaderToy" and paste in the URL. If it's not public you can paste in the raw source code. Then you can save the shader (requires sign up, no email confirm), and click "Export > Three.js".

You might need to tweak the parameters a little after import, but I hope to have this improved over time. For example, ShaderFrog doesn't support audio nor video inputs yet, but you can preview them with images instead.

Proof of concept:

ShaderToy https://www.shadertoy.com/view/MslGWN

ShaderFrog http://shaderfrog.com/app/view/247

Full disclosure: I am the author of this tool which I launched last week. I think this is a useful feature.

This is based on various sources , including the answer of @INF1.

Basically you insert missing uniform variables from Shadertoy (iGlobalTime etc, see this list: https://www.shadertoy.com/howto) into the fragment shader, the you rename mainImage(out vec4 z, in vec2 w) to main(), and then you change z in the source code to 'gl_FragColor'. In most Shadertoys 'z' is 'fragColor'.

I did this for two cool shaders from this guy (https://www.shadertoy.com/user/guil) but unfortunately I didn't get the marble example to work (https://www.shadertoy.com/view/MtX3Ws).

A working jsFiddle is here: https://jsfiddle.net/dirkk0/zt9dhvqx/ Change the shader from frag1 to frag2 in line 56 to see both examples.

And don't 'Tidy' in jsFiddle - it breaks the shaders.

EDIT: https://medium.com/@dirkk/converting-shaders-from-shadertoy-to-threejs-fe17480ed5c6

标签