WebGL - 有替代方法在HTML中嵌入着色器吗?

时间:2011-05-04 04:49:18

标签: glsl webgl

在WebGL中使用GLSL着色器的流行方式似乎是将它们嵌入到主html文件中。 顶点和片段着色器嵌入在以下标记中:

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

这与我在Mozilla Developer Network页面中的WebGL示例中看到的相同。

这适用于简单的应用程序,但是当你有一个带有许多着色器的复杂应用程序时,html文件会变得混乱。 (我一直在编辑错误的着色器!)另外,如果你想重复使用着色器,这个方案很不方便。

所以我考虑将这些着色器放在单独的XML文件中并使用XMLHttpRequest()加载它们。然后我看到其他人有同样的想法:

http://webreflection.blogspot.com/2010/09/fragment-and-vertex-shaders-my-way-to.html

我喜欢使用.c文件的建议,因为这为GLSL提供了语法高亮和其他编辑器便利。

但上述方法的问题是(据我所知)XMLHttpRequest()在开发和测试WebGL应用程序时无法加载本地.c文件 - 即在客户端。但是在此过程中继续将其上传到服务器是很麻烦的。

因此,如果我想将着色器保留在html文件之外,那么将它们作为字符串嵌入代码中是唯一的选择吗?但这会使编写和调试变得困难......

我很感激有关在WebGL应用程序中管理多个GLSL着色器的任何建议。

此致

编辑(2011年5月5日)

由于我使用Mac进行开发,我决定启用Apache服务器,并将我的webgl代码放在http://localhost/~username/下。这避免了在开发期间禁用文件:协议的问题。现在javascript文件加载代码在本地工作,因为使用了http:而不是file:。我以为我会把它放在这里以防万一有人发现它有用。

13 个答案:

答案 0 :(得分:16)

是的,如果你想使用XHR,本地服务器真的是唯一的出路。我写过一堆WebGL课程,经常考虑不再在HTML中嵌入着色器,但是我已经被大量关于网络安全的解释吓到了......

幸运的是,运行服务器非常容易。只需打开一个shell然后

cd path-to-files
python -m SimpleHTTPServer

然后将浏览器指向

http://localhost:8000

适用于纹理和GLSL等简单案例。有关视频和音频流的信息,请参阅

What is a faster alternative to Python's http.server (or SimpleHTTPServer)?

另一方面,every browser that supports WebGL支持ES6 mutli-line template literals,所以如果你不关心旧的浏览器,你可以使用像这样的反引号在JavaScript中添加着色器

var vertexShaderSource = `
  attribute vec4 position;
  uniform mat4 u_matrix;

  void main() {
    gl_Position = u_matrix * position;
  }
`;

答案 1 :(得分:14)

我一直在使用require.jstext plugin

这是一个片段:

define(
    /* Dependencies (I also loaded the gl-matrix library) */
    ["glmatrix", "text!shaders/fragment.shader", "text!shaders/vertex.shader"],

    /* Callback when all has been loaded */
    function(glmatrix, fragmentShaderCode, vertexShaderCode) {
        ....
        var vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, vertexShaderCode);
        gl.compileShader(vertexShader);
        ....
    }
);

目录结构如下:

~require-gl-shaders/
 |~js/
 | |+lib/
 | |~shaders/
 | | |-fragment.shader
 | | `-vertex.shader
 | |-glmatrix.js - gl-matrix library
 | |-shader.js
 | |-text.js     - require.js's text plugin
 |-index.html
 |-main.js
 `-require.js    - the require.js library

就个人而言,我有一点需要学习曲线,但它确实帮助我保持清洁代码。

答案 2 :(得分:6)

我的好友创建了一个很好的utils对象,为这种场景提供了一些方便的功能。您可以将着色器存储在纯文本文件中名为“着色器”的文件夹中:

filename:vertex.shader

attribute vec3 blah;

uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat3 uNMatrix;

void main(void) {
    magic goes here
}

filename:fragment.shader

#ifdef GL_ES
    precision highp float;
#endif

varying vec4 vYadaYada;
uniform sampler2D uSampler;

void main(void) {
    fragic magic goes here      
}

你只需调用它来创建一个包含这些着色器文件的新程序:

var shaderProgram = utils.addShaderProg(gl, 'vertex.shader', 'fragment.shader');    

这是处理商业的甜实用工具:

utils = {};

utils.allShaders = {};
utils.SHADER_TYPE_FRAGMENT = "x-shader/x-fragment";
utils.SHADER_TYPE_VERTEX = "x-shader/x-vertex";

utils.addShaderProg = function (gl, vertex, fragment) {

    utils.loadShader(vertex, utils.SHADER_TYPE_VERTEX);
    utils.loadShader(fragment, utils.SHADER_TYPE_FRAGMENT);

    var vertexShader = utils.getShader(gl, vertex);
    var fragmentShader = utils.getShader(gl, fragment);

    var prog = gl.createProgram();
    gl.attachShader(prog, vertexShader);
    gl.attachShader(prog, fragmentShader);
    gl.linkProgram(prog);

    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {alert("Could not initialise main shaders");}

    return prog;
};

utils.loadShader = function(file, type) {
    var cache, shader;

    $.ajax({
        async: false, // need to wait... todo: deferred?
        url: "shaders/" + file, //todo: use global config for shaders folder?
        success: function(result) {
           cache = {script: result, type: type};
        }
    });

    // store in global cache
    uilts.allShaders[file] = cache;
};

utils.getShader = function (gl, id) {

    //get the shader object from our main.shaders repository
    var shaderObj = utils.allShaders[id];
    var shaderScript = shaderObj.script;
    var shaderType = shaderObj.type;

    //create the right shader
    var shader;
    if (shaderType == "x-shader/x-fragment") {
        shader = gl.createShader(gl.FRAGMENT_SHADER);
    } else if (shaderType == "x-shader/x-vertex") {
        shader = gl.createShader(gl.VERTEX_SHADER);
    } else {
        return null;
    }

    //wire up the shader and compile
    gl.shaderSource(shader, shaderScript);
    gl.compileShader(shader);

    //if things didn't go so well alert
    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        alert(gl.getShaderInfoLog(shader));
        return null;
    }

    //return the shader reference
    return shader;

};//end:getShader

感谢好友为甜蜜的代码...享受他对webgl社区的贡献..让简化程序/着色器管理变得更容易。

答案 3 :(得分:6)

按照@ droidballoon的提示,我最终使用了stack.gl,其中#34;是一个开放的WebGL软件生态系统,建立在browserify和npm&#34;之上。

它的glslify提供了一个browserify转换,可以与gl-shader一起使用以加载着色器。 Javascript看起来像这样:

var glslify       = require('glslify');
var loadShader    = require('gl-shader');
var createContext = require('gl-context');

var canvas = document.createElement('canvas');
var gl = createContext(canvas);

var shader = loadShader(
    gl,
    glslify('./shader.vert'),
    glslify('./shader.frag')
);

答案 4 :(得分:4)

我正在使用这个: https://www.npmjs.com/package/webpack-glsl-loader 它优先保持语法突出显示不具有正确的glsl文件而不是文本片段。 我稍后会报告它是如何工作的。

[编辑2015年8月17日]这种方法对我来说很好。它假设webpack在你的构建流程中,但这不是一件坏事。

[2016年6月11日编辑] https://github.com/kulicuu/Spacewar_WebGL_React有一个通过Webpack构建导入glsl文件的工作示例。游戏本身应该在未来一周内开发出来。

答案 5 :(得分:2)

一种很好的方法是通过Browserify的browserify-shader扩展。

答案 6 :(得分:1)

如果您可以使用服务器端脚本,则可以编写一个小脚本,该脚本读入着色器文件并返回包含全局对象中脚本的JavaScript文件。这样你可以使用普通的&lt; script src =“shader?prefix = foo”&gt;来包含它。并将脚本编辑为.c文件。

像这样的Ruby CGI脚本

require 'cgi'
require 'json'

cgi = CGI.new
prefix = File.expand_path(cgi["prefix"])
cwd = Dir.getwd + "/"
exit!(1) unless prefix.start_with?(cwd)

shader = prefix + ".c"
source = File.read(shader)
cgi.out("text/javascript") {
  <<-EOF
    if (typeof Shaders == 'undefined') Shaders = {};
    Shaders[#{cgi["prefix"]}] = #{source.to_json};
  EOF
}

答案 7 :(得分:1)

可能不是最好的方式,但我正在使用PHP。我将着色器放在一个单独的文件中,然后你只需使用:

<?php include('shaders.html'); ?>

对我很有用。

答案 8 :(得分:0)

我也一直在使用Require.js来组织我的文件,但是没有使用文本插件,比如@Vlr建议,我有一个脚本,它接受着色器并将其转换为Require.js模块,我可以然后在别处使用。所以着色器文件simple.frag是这样的:

uniform vec3 uColor;

void main() {
  gl_FragColor = vec4(uColor, 1.0);
}

将转换为文件shader.js

define( [], function() {
  return {
    fragment: {
      simple: [
        "uniform vec3 uColor;",

        "void main() {",
        "  gl_FragColor = vec4(uColor, 1.0);",
        "}",
      ].join("\n"),
    },
  }
} );

看起来很混乱,但这个想法不是人类可读的。然后,如果我想在某个地方使用此着色器,我只需拉入shader模块并使用shader.fragment.simple访问它,如下所示:

var simple = new THREE.ShaderMaterial( {
  vertexShader: shader.vertex.simple,
  fragmentShader: shader.fragment.simple
} );

我在这里写了一篇博文,其中包含更多详细信息和演示代码链接:http://www.pheelicks.com/2013/12/webgl-working-with-glsl-source-files/

答案 9 :(得分:0)

您可以将着色器放在不同的文件中,就像将javascript代码放在不同的文件中一样。这个库https://github.com/codecruzer/webgl-shader-loader-js以熟悉的语法完成:

示例用法(从上页逐字逐句):

[index.html]:

    <script data-src="shaders/particles/vertex.js" data-name="particles"
            type="x-shader/x-vertex"></script>
    <script data-src="shaders/particles/fragment.js" data-name="particles"
            type="x-shader/x-fragment"></script>

[example.js]:

    SHADER_LOADER.load (
        function (data)
        {
            var particlesVertexShader = data.particles.vertex;
            var particlesFragmentShader = data.particles.fragment;
        }
    );

答案 10 :(得分:0)

不是确切的解决方案,但对我有好处。 我使用Pug(旧Jade)编译HTML,我使用包含在着色器脚本标签

script#vertexShader(type="x-shader/x-vertex")
    include shader.vert

script#fragmentShader(type="x-shader/x-fragment")
    include shader.frag

结果是相同的,带有代码内联的HTML,但您可以单独使用着色器。

答案 11 :(得分:0)

使用C宏#includegcc -E(-E键在没有编译器的情况下运行预处理器)

将此添加到您的js文件:

const shader = `
#include "shader.fg"
`

,然后在以下位置使用shell:

mov main.js main.c
gcc -E --no-warnings main.c | sed '/^#.*/d' > main.js

sed在这里只是删除预处理器生成的多余注释

有效! ;)

答案 12 :(得分:0)

您可以使用 JSONP 代替 XMLHttpRequest ,这对于基于本地文件系统的加载非常有用。

您的着色器文件可以命名为任意名称(用于自动突出显示语法)

vertex-shader.glsl:

JSONP('vertex-shader',`
attribute vec4 a_position;
void main() {
    gl_Position = a_position;
}
`)

然后我们有了我们的index.html文件,其中包括2个脚本文件

index.html

<canvas id="c"></canvas>
<script src="vertex-shader.glsl">
<script src="main.js"></script>

main.js 中,您包含JSONP函数,该函数可处理着色器文件

var shaders = {}
function JSONP(name,contents) {
    shaders[name] = contents
}

// ... main webGL handler code

如果要加载新的着色器,可以动态插入新的脚本标签。