BEGINNER'S GUIDE

Using custom shaders

Unleash the full capabilities of ayceVR

Developing WebGL applications that perform well on mobile devices can be hard. Especially graphical calculations can be CPU-heavy in particular. That's why running these calculations on the GPU is generally a good idea to get the best possible performance. AyceVR has a built-in way to let you run your own custom GLSL shaders on the GPU. This also gives you many more possibilities on how your objects are rendered.

Loading up your own custom shader is really quite simple. After you've written your GLSL code, save your vertex and fragment shader code in separate files with the vertex shader having the file ending .vert and the fragment shader the ending .frag.

AyceVR generates default shaders for all O3Ds added to the scene. If you'd like to modify a default shader or use it as boilerplate code for your own shader, you can log the shader to the JavaScript console. To do this just set the object3D properties .logVertexShader or .logFragmentShader true on the respective object. The following guide will cover how to use a custom shader on an O3D.


    ...
    cube.shader = "shaders/example";
    var offset = [];
    for(var i=0; i < cube.vertices.length; i++){
        offset[i] = 0.1 - Math.random()/5;
    }
    cube.addShaderAttribute("aVertexOffset", 3, offset);
    ...

You can add the shader to the respective o3D by setting its shader property. Make sure you leave out the file ending when setting the shader source, both fragment and vertex shader are loaded automatically. This example loads the shader files called example.vert and example.frag. The offset array holds the values manipulating the vertex positions of the cube. These values will be sent to the shader as vertex attributes. This is optional and only needed if your shader uses additional attributes. The array is initialized in the for loop. Note that the attribute has as many values as there are vertices in the cube. Finally the attribute is added by calling addShaderAttribute(). The first argument is the name of the attribute used in your shader code, the second one indicates how many values there are per vertex, in this example the shader uses an attribute of type vec3, so there are three values. The last argument is the array containing the values that will be sent.

    ...
    var color = {
        red: 0.5,
        green: 0.0,
        blue: 0.1
    };
    cube.addShaderUniform("uColor", "uniform3f", color, ["red", "green", "blue"]);

    scene.addToScene(cube);
    ...

Similarly to setting up custom attributes you can easily use your own uniform variables. To send an uniform we're first creating an object with the values to be sent. Via addShaderUniform() you can send the uniform values. Again, pass the name of the attribute in your GLSL code as the first argument. The second argument indicatesthe type of uniform function to call for the respective uniform type. In this case we're using a vec3 with three floats so the appropriate function for that is uniform3f. Pass the object we've set up earlier as the third argument. The fourth argument is an array which contains the object property names as strings. These names refer to the color object. Now you know how to use your own shaders and pass custom values to them.

...
    <script>
        var canvas = document.getElementById("ayce_canvas")
        var scene = new Ayce.Scene(canvas);
        scene.setClearColor(0.2, 0.2, 0.35);

        var light = new Ayce.Light();
        light.position.set(0, 1, 2);
        scene.addToScene(light);

        var cube = new Ayce.Geometry.Box(1, 1, 1);
        cube.offset.set(-cube.a/2, -cube.b/2, -cube.c/2);
        cube = cube.getO3D();
        cube.position.z = -2;

        cube.shader = "shaders/example";
        var offset = [];
        for(var i=0; i < cube.vertices.length; i++){
            offset[i] = 0.1 - Math.random()/5;
        }
        cube.addShaderAttribute("aVertexOffset", 3, offset);

        var color = {
            red: 0.5,
            green: 0.0,
            blue: 0.1
        };
        cube.addShaderUniform("uColor", "uniform3f", color, ["red", "green", "blue"]);

        scene.addToScene(cube);

        function update() {
            cube.rotation.fromEulerAngles(0, Date.now()/1000, 0);
            color.red   = ( Math.sin(Date.now()/1000                    ) + 1)/4;
            color.green = ( Math.sin(Date.now()/1000 + (Math.PI*2*(1/3))) + 1)/4;
            color.blue  = ( Math.sin(Date.now()/1000 + (Math.PI*2*(2/3))) + 1)/4;

            Ayce.requestAnimFrame(update);
            scene.updateScene();
            scene.drawScene();
        }

        update();
    </script>
    ...