Javascript and WebGL, external scripts

Skeen picture Skeen · Feb 2, 2011 · Viewed 17.7k times · Source

Just curious; How do I place my webgl shaders, in an external file?

Currently I'm having;

    <script id="shader-fs" type="x-shader/x-fragment">
        #ifdef GL_ES
            precision highp float;
        #endif

        void main(void)
        {
            gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    </script>

    <script id="shader-vs" type="x-shader/x-vertex">
        attribute vec3 aVertexPosition;

        uniform mat4 uMVMatrix;
        uniform mat4 uPMatrix;

        void main(void)
        {
            gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
        }
    </script>

In my html header, how do I link in this from an external file? - I tried the usual javascript approach;

<script type="text/javascript" src="webgl_shader.js"></script>

Answer

David Roe picture David Roe · Feb 2, 2011

For external files, you need to stop using the script tag. I suggest using something like XMLHttpRequest. I would also suggest renaming your files, they are shaders not Javascript so use a different extension to avoid confusion. I use something like "shiny_surface.shader".

This is what I do:

function loadFile(url, data, callback, errorCallback) {
    // Set up an asynchronous request
    var request = new XMLHttpRequest();
    request.open('GET', url, true);

    // Hook the event that gets called as the request progresses
    request.onreadystatechange = function () {
        // If the request is "DONE" (completed or failed)
        if (request.readyState == 4) {
            // If we got HTTP status 200 (OK)
            if (request.status == 200) {
                callback(request.responseText, data)
            } else { // Failed
                errorCallback(url);
            }
        }
    };

    request.send(null);    
}

function loadFiles(urls, callback, errorCallback) {
    var numUrls = urls.length;
    var numComplete = 0;
    var result = [];

    // Callback for a single file
    function partialCallback(text, urlIndex) {
        result[urlIndex] = text;
        numComplete++;

        // When all files have downloaded
        if (numComplete == numUrls) {
            callback(result);
        }
    }

    for (var i = 0; i < numUrls; i++) {
        loadFile(urls[i], i, partialCallback, errorCallback);
    }
}

var gl;
// ... set up WebGL ...

loadFiles(['vertex.shader', 'fragment.shader'], function (shaderText) {
    var vertexShader = gl.createShader(gl.VERTEX_SHADER);
    gl.shaderSource(vertexShader, shaderText[0]);
    // ... compile shader, etc ...
    var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(fragmentShader, shaderText[1]);

    // ... set up shader program and start render loop timer
}, function (url) {
    alert('Failed to download "' + url + '"');
}); 

If you're using a library like JQuery, they probably have a function similar to my loadFiles one.