JavaScript和WebGL外部脚本

26

只是好奇,我该如何将我的WebGL着色器放在外部文件中?

目前我有以下代码:

    <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>

在我的html头部,我如何从外部文件中链接这个?- 我尝试了通常的javascript方法;

<script type="text/javascript" src="webgl_shader.js"></script>
4个回答

23

对于外部文件,你需要停止使用脚本标签。我建议使用类似 XMLHttpRequest 的东西。我还建议将你的文件重命名,它们是着色器而不是Javascript,因此使用不同的扩展名以避免混淆。我会使用类似“shiny_surface.shader”的东西。

这是我的做法:

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 + '"');
}); 
如果您正在使用像JQuery这样的库,它们可能有类似于我的loadFiles函数的功能。

5
可能是一个愚蠢的问题,但为什么要避免使用脚本标签?另外,如何在这种方法中为着色器文件(例如'shader-fs')赋予 ID? 为什么避免使用脚本标签可能是因为它们会阻塞页面的加载。使用这种方法,可以在JavaScript代码中动态地创建和加载着色器文件,并为它们分配ID以便在代码中引用它们。可以通过以下方式给着色器文件添加ID:
  1. 创建一个script元素
  2. 将其类型设置为x-shader/x-fragment或x-shader/x-vertex,具体取决于着色器类型。
  3. 将ID属性设置为所需的值(例如“ shader-fs”)
  4. 将源代码设置为着色器文件的内容。
- MonkeyD
2
“.glsl” 是一个标准扩展名。 - Parobay
@MonkeyD 使用<script>标签没有任何问题,但如果你有很多着色器,HTML文件会变得非常混乱。当使用XHR时,着色器文件没有ID;着色器源代码可以在XHR对象的responseText属性中获得。 - TachyonVortex
@Parobay 不是的。GLSL规范没有为着色器指定标准文件扩展名。常用的扩展名为.vert.frag。更多信息请参见此问题 - TachyonVortex

5

我曾经遇到过同样的问题,并发现使用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();
       });
});   

我的着色器文件是shader.fsshader.vs,其中包含以下声明行:
<script type="x-shader/x-fragment"><script type="x-shader/x-vertex">

更新 在Chrome浏览器中,不会自动选择“xml”。下面的代码同样适用于Chrome:

$.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'
        });               

更新2: 由于着色器源代码中的<&需要转义为XML格式,因此无论您使用小于比较还是逻辑与运算符,这个方法都能起作用:

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'
});

尽管dataType:'html'似乎工作正常,但最好使用dataType:'text',因为着色器源代码不是HTML。 - TachyonVortex

0
你可以使用像我的开源着色器管理库这样的工具:

https://github.com/ILOVEPIE/Shader.js

它允许您从URL加载着色器,并为将来访问网站缓存着色器源代码。它还使使用uniform更简单。


-1

我不是WebGL大师,但这个可以工作吗?

<script id="shader-fs" type="x-shader/x-fragment" src="fragment-shader.fs" />

抱歉,不行。使用这种方法,即使在src部分指定的文件被找到(状态码为200),我仍然会收到意外的EOF错误。 - appas
3
这是因为用于检索脚本文本的方法是在DOM级别操作的,它只能看到一个空的脚本标签,不知道脚本标签上“src”属性的具体含义。 - David Roe
请参考这个答案了解为什么这不起作用。 - TachyonVortex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接