Javascript and WebGL, external scripts

18,449

Solution 1

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.

Solution 2

I had the same issue and found that this has worked for me with jQuery:

var fragmentShaderSRC = null,
var vertexShaderSRC = null;
...
function executeProgram(){ //main program }
...
$.get("shader.fs", function(data){ 
       fragmentShaderSRC = data.firstChild.textContent;
       $.get("shader.vs", function(data){
             vertexShaderSRC = data.firstChild.textContent;
             executeProgram();
       });
});   

Where shader.fs and shader.vs are my shaders (and include the
<script type="x-shader/x-fragment"> and <script type="x-shader/x-vertex"> declaration lines)

Update With Chrome the intelligent guess does not select 'xml'. The following code works in Chrome as well:

$.ajax({
          url: 'shader.fs', 
          success: function(data){ 
              fragmentShaderSRC = data.firstChild.textContent;
              $.ajax({
                  url: 'shader.vs', 
                  success: function(data){
                      vertexShaderSRC = data.firstChild.textContent;
                      executeProgram();
                   },
                   dataType: 'xml'
              })
           },
           dataType: 'xml'
        });               

Update 2: As < and & in the shader source need to be escaped to load in as XML, this works all of the time even if you use the less than comparision or the and logic operators:

var vs_source = null,
    fs_source = null;
$.ajax({
    async: false,
    url: './my_shader.vs',
    success: function (data) {
        vs_source = $(data).html();
    },
    dataType: 'html'
});

$.ajax({
    async: false,
    url: './my_shader.fs',
    success: function (data) {
        fs_source = $(data).html();
    },
    dataType: 'html'
});
Share:
18,449

Related videos on Youtube

Skeen
Author by

Skeen

merge keep

Updated on November 25, 2020

Comments

  • Skeen
    Skeen over 3 years

    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>
    
  • appas
    appas over 13 years
    Sorry, but no. Using this method, I get an Unexpected EOF error, even though the file specified in the src part is found (status code 200).
  • David Roe
    David Roe over 13 years
    This is because the methods used to retrieve the script text operate at the DOM level and that just sees an empty script tag. It doesn't know anything specific about the meaning of a "src" attribute on a script tag.
  • MonkeyD
    MonkeyD about 11 years
    Possible silly question, but why avoid using script tags? Also, how does one give id's to the shader files (ex, 'shader-fs') with this method?
  • Parobay
    Parobay about 10 years
    .glsl is a standard extension
  • TachyonVortex
    TachyonVortex over 9 years
    @MonkeyD There's nothing wrong with using <script> tags, but if you have a lot of shaders, the HTML file can get very cluttered. When using XHR, the shader files don't have IDs; the shader source code is available in the responseText property of the XHR object.
  • TachyonVortex
    TachyonVortex over 9 years
    @Parobay No it isn't. The GLSL specification doesn't specify a standard file extension for shaders. Commonly used extensions are .vert and .frag. See this question for more info.
  • TachyonVortex
    TachyonVortex over 9 years
    Although dataType: 'html' seems to work OK, it's better to use dataType: 'text', since shader source code is not HTML.
  • TachyonVortex
    TachyonVortex over 9 years
    See this answer for more info about why this doesn't work.