【问题标题】:WebGL - is there an alternative to embedding shaders in HTML?WebGL - 有没有在 HTML 中嵌入着色器的替代方法?
【发布时间】:2011-08-18 05:36:33
【问题描述】:

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

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

这与我在 Mozilla 开发者网络页面的 WebGL 示例中看到的约定相同。

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

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

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

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

但上述方法的问题是(据我了解)XMLHttpRequest() 无法加载本地 .c 文件 - 即在客户端 - 在您开发和测试 WebGL 应用程序时。但是在这个过程中一直上传到服务器很麻烦。

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

如果有任何关于在 WebGL 应用程序中管理多个 GLSL 着色器的建议,我将不胜感激。

问候

编辑(2011 年 5 月 5 日)

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

【问题讨论】:

  • 您的编辑很有帮助。我正在翻阅答案,试图弄清楚有什么大不了的。
  • 我喜欢使用“http-server”而不是 apache,您可以使用 npm install -g http-server 从 npm 获取它。然后使用它,cd到你的项目并运行http-server -a localhost -p 8080 .
  • 这是迄今为止我发现的最好的方法:- stackoverflow.com/a/54092649/5878848

标签: glsl webgl


【解决方案1】:

EDIT 2021 这个答案已经过时了。您可能应该寻找其他东西。


我一直在用require.jstext plugin

这是一个sn-p:

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

就我个人而言,我对 require 有一点学习曲线,但它确实帮助我保持代码更简洁。

【讨论】:

  • require.js 在 2021 年也已经过时了,原生导入更加现代
【解决方案2】:

您可以使用 JSONP 作为 XMLHttpRequest 的替代品,这对于基于本地文件系统的加载来说很好。

您的着色器文件可以任意命名(用于自动语法高亮)

顶点着色器.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>

ma​​in.js 中包含处理着色器文件的 JSONP 函数

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

// ... main webGL handler code

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

【讨论】:

    【解决方案3】:

    使用 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这里只是删除了预处理器产生的多余的cmets

    有效! ;)

    【讨论】:

      【解决方案4】:

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

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

      结果是一样的,一个内嵌代码的 HTML,但是你可以单独使用着色器。

      【讨论】:

        【解决方案5】:

        是的,如果您想使用 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;
          }
        `;
        

        【讨论】:

        • 但是如果你只是坚持http:,就没有网络安全问题,是吗?我知道强制文件:工作可能不安全。
        • 确实——但是告诉人们设置自己的 Web 服务器只是为了运行他们的第一个 WebGL 演示会是一个额外的障碍,会让人们望而却步。入门和学习 WebGL 可编程管道模型已经够难的了!
        • cd path/to/files 一样简单地运行本地服务器,然后是python -m SimpleHTTPServer,然后转到http://localhost:8000
        • 对于python3:python -m http.server
        • 我为此运行了一个本地服务器,但是反引号的模板文字正在阻塞我的 glsl 编译器。
        【解决方案6】:

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

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

        [edit 11-June-2016] https://github.com/kulicuu/Spacewar_WebGL_React 有一个通过 Webpack 构建导入 glsl 文件的工作示例。游戏本身应该会在接下来的一周内开发出来。

        【讨论】:

        • github链接坏了,能补一下吗?
        【解决方案7】:

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

        它的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')
        );
        

        【讨论】:

          【解决方案8】:

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

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

          非常适合我。

          【讨论】:

            【解决方案9】:

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

            【讨论】:

            • 啊,我一直在使用brfs 内联着色器,我得看看那个插件是否更方便。
            【解决方案10】:

            您可以将着色器放在不同的文件中,就像将 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;
                    }
                );
            

            【讨论】:

              【解决方案11】:

              我也一直在使用 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/

              【讨论】:

                【解决方案12】:

                我的朋友为这种场景创建了一个不错的 utils 对象,其中包含一些方便的功能。您可以将着色器存储在名为“shaders”的文件夹中的纯文本文件中:

                文件名:vertex.shader

                attribute vec3 blah;
                
                uniform mat4 uMVMatrix;
                uniform mat4 uPMatrix;
                uniform mat3 uNMatrix;
                
                void main(void) {
                    magic goes here
                }
                

                文件名: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');    
                

                这是处理 biz 的甜蜜 util 对象:

                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
                

                感谢哥们的甜蜜 codeezy.. 享受他对 webgl 社区的贡献.. 简化程序/着色器管理变得更加容易。

                【讨论】:

                • OOPs... 忘了提到使用我们包含了一些 jQuery 来使用它很好的 $.ajax 小函数:-p
                • 与其依靠$.ajax()对正确的dataType进行“智能猜测”,不如在设置对象中指定dataType: "text"
                【解决方案13】:

                如果您可以使用服务器端脚本,您可以编写一个小脚本来读取着色器文件并返回一个 JavaScript 文件,其中包含全局对象中的脚本。这样您就可以使用普通的

                类似这样的 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
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-12-22
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-03-24
                  • 2014-08-17
                  • 1970-01-01
                  • 2012-06-21
                  相关资源
                  最近更新 更多