如何将着色器包含为外部文件

How to include shaders as external files

有没有办法将此着色器代码作为外部 vertexShader.js 不带引号包含在 "script" 标记之间?

var vertCode =
'attribute vec3 coordinates;' +

'void main(void) {' +
' gl_Position = vec4(coordinates, 1.0);' +
'gl_PointSize = 10.0;'+
'}';

您可以将着色器代码添加到 <script> 标记中,顶点着色器类型为 "x-shader/x-vertex",片段着色器类型为 "x-shader/x-fragment"。 另见 WebGL and HTML shader-type

<script id="my_vertex_shader" type="x-shader/x-vertex">
attribute vec3 coordinates;
 
void main(void) {
    gl_Position = vec4(coordinates, 1.0);
    gl_PointSize = 10.0;
}
</script>

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

着色器代码可以轻松“加载”:

var vertCode = document.getElementById("my_vertex_shader").text;
var fragCode = document.getElementById("my_fragment_shader").text;

WebGL - is there an alternative to embedding shaders in HTML? 可能也很有趣。

您询问了如何将着色器包含为外部文件

有几种方法,但首先要注意的是,对称为 multiline template literals 的字符串使用反引号可以让您拥有多行字符串

  const str = `
  this
  string
  is
  on
  multiple lines
  `;

所以没有必要像你一样使用 'this' + 'that'。

如果您真的想将它们放在单独的文件中,那么至少有 3 种方法可以做到这一点

  • 将它们放在单独的脚本文件中,分配给一些全局的。例子

    vertexShader.js

    window.shaders = window.shaders || {};
    window.shaders.someVertexShader = `
    attribute vec3 coordinates;
    
    void main(void) {
        gl_Position = vec4(coordinates, 1.0);
        gl_PointSize = 10.0;'
    }
    `;
    

    在你的 html

    <script src="vertexShader.js"></script>
    <script>
     // use shader as window.shaders.someVertexShader
     ...
    </script>
    

    请注意,您的最终 JavaScript 脚本也可以位于单独的文件中,只要它位于着色器文件之后即可。

  • 将它们放在单独的 JavaScript 模块中

    现代浏览器支持 ES6 modules

    vertexShader.js

    export default `
    attribute vec3 coordinates;
    
    void main(void) {
        gl_Position = vec4(coordinates, 1.0);
        gl_PointSize = 10.0;'
    }
    `;
    

    在这种情况下,您的 JavaScript 脚本 必须 位于外部文件中,以便您 HTML 可能看起来像这样

    <script src="main.js" type="module"></script>
    

    和main.js看起来像这样

    import someVertexShader from './vertexShader.js';
    
    // use someVertexShader
    

    有个例子here

  • fetch

    加载它们

    在这种情况下,着色器文件中没有 JavaScript

    vertexShader.shader

    attribute vec3 coordinates;
    
    void main(void) {
        gl_Position = vec4(coordinates, 1.0);
        gl_PointSize = 10.0;'
    }
    

    然后在你的脚本中

    fetch('./vertexShader.shader')
    .then(response => response.text())
    .then((shaderSource) => {
       // use shadeSource
    });
    

    这种方法最大的问题是脚本是异步下载的,所以你必须手动等待它们下载。不过使用 async/await 非常简单。

    假设您想下载 6 个着色器文件然后使用它们。此代码将等待所有 6 个文件在开始之前下载

    function loadTextFile(url) {
      return fetch(url).then(response => response.text());
    }
    
    const urls = [
      './someShader1.shader',
      './someShader2.shader',
      './someShader3.shader',
      './someShader4.shader',
      './someShader5.shader',
      './someShader6.shader',
    });   
    
    async function main() {
      const files = await Promise.all(urls.map(loadTextFile));
      // use files[0] thru files[5]
    }
    main();
    

如果是我,我真的想把我的着色器放在外部文件中,我可能会使用 import,然后要么只针对现代浏览器,要么使用像 webpack or rollup to package them into a single file for shipping. This is what THREE.js 这样的程序.