为什么着色器必须在web文件程序的html文件中?

时间:2013-01-08 16:47:33

标签: javascript three.js glsl webgl

我看到以下问题,有人问如何从html中删除着色器: WebGL - is there an alternative to embedding shaders in HTML?

在包含问题答案中建议的着色器的文件中加载了精心设计的变通方法。

在我看到的教程中,着色器代码直接嵌入在html中。 javascript代码使用getElementById引用它。但由于很多原因,将着色器直接嵌入到html中很难看。为什么我不能使用src =属性在外部引用它?

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

以上不起作用,我只是想知道为什么不行。这显然与脚本本身的限制有关,但我不明白。

3 个答案:

答案 0 :(得分:32)

您根本不必使用<script>标记来加载着色器程序。大多数教程和示例只是将它们用作容器来将字符串存储在网页的DOM中。脚本类型"x-shader/x-fragment"对于Web浏览器没有意义,因此它们不执行脚本。但是,它们会将该标记的内容作为字符串存储在DOM中,以后可以通过“真实”脚本进行访问。这仅在脚本内容位于HTML文件中时才有效。 当您通过src属性加载脚本时,内容不会成为脚本标记的文本子节点,因此无法通过DOM树访问

您也可以将着色器的源代码存储为Javascript文件中的字符串:

// myVertextShader.glsl.js
var myVertexShaderSrc =         
        "attribute vec3 pos;"+      
        "void main() {"+        
        "   gl_Position = vec4(pos, 1.0);"+     
        "}"
    ;

然后你会像这样编译着色器:

var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, myVertexShaderSrc);
gl.compileShader(vertexShader);

gl.attachShader(program, vertexShader);

答案 1 :(得分:25)

更新2018

在2018年,我建议使用多行模板文字,如在带有反引号的着色器环绕中,它可以跨越多行

const someShaderSource = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
  gl_Position = matrix * position;
}
`;

如果要将着色器放在单独的文件中,可以使用JavaScript模块在2018年轻松完成此操作。着色器文件可能如下所示

// someshader.glsl.js
export default `
attribute vec4 position;
uniform mat4 matrix;
void main() {
  gl_Position = matrix * position;
}
`;

要使用JavaScript模块,您的主JavaScript必须位于单独的文件中。您可以通过导入来访问着色器源

// main.js

import someShaderSource from './someshader.glsl.js';

// use someShadeSource

您可以使用

将其包含在HTML中
<script src="main.js" type="module"></script>

如果您想支持旧浏览器,可以使用rollup之类的工具,它会读取所有import语句并生成一个大型JavaScript文件。这就是three.js的作用。

如果您需要支持IE11,可以使用babel转换多行模板。多年来,所有其他浏览器都支持多行模板。

原始答案

  

为什么着色器必须在webgl程序的html文件中?

他们没有

您可以将着色器放在外部javascript中。例如

// --myshader.js--
var myFragmentShader = 
  "void main() {\n" +
  "  gl_FragColor = vec4(1,0,0,1);\n" +
  "}n\";

或其他常见格式

// --myshader.js--
var myFragmentShader = [
  "void main() {",
  "  gl_FragColor = vec4(1,0,0,1);", 
  "}",
].join("\n");

在所有支持WebGL的浏览器中,您都可以使用template literals

// --myshader.js--
var myFragmentShader = `
  void main() {
    gl_FragColor = vec4(1,0,0,1); 
  }
`;

否则,您可以将它们放在文本文件中并使用XMLHTTPRequest加载它们

// --myshader.txt
  void main() {
    gl_FragColor = vec4(1,0,0,1); 
  }

然后在JavaScript中执行以下操作

function loadTextFile(url, callback) {
  var request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.addEventListener('load', function() {
     callback(request.responseText);
  });
  request.send();
}

loadTextFile("myshader.txt", function(text) {
  // use text...
});

人们将它们放入HTML中的原因是因为它简单,高效且同步。

简单:与JS文件版本不同,您不必用引号和其他标点符号围绕每一行。虽然现在es6不再是问题了。每个支持WebGL的浏览器都支持es6模板字符串。

高效:与text和js文件不同,只有一个请求到服务器。当然有些人可能会在他们的js文件上运行一个连接器来修复其中的一些。

同步:与文本文件不同,它们的用法是同步的。无需回调或承诺或以其他方式处理下载文件的异步问题。

至于为什么你的例子不起作用我很确定原因是它允许跨源资源访问。 <script>标签是在人们发现跨源访问是一个问题之前设计的,所以他们无法在不破坏一堆网站的情况下关闭跨源脚本。他们可以使其他一切更加严格。

例如,

XMLHttpRequest不允许跨源访问,除非您正在联系的服务器给予权限。如果脚本标记允许您访问该内容,则可以使用脚本标记来解决该限制。换句话说,不是创建XMLHttpRequest并读取结果的request.responseText,而只是以编程方式创建脚本标记,将其src设置为您想要的URL,然后阅读其text完成后的字段。为确保您无法执行此操作,您不能阅读具有text属性的脚本标记的src字段

答案 2 :(得分:3)

着色器语言脚本只是文本。可以从任何地方(您可以阅读或生成文本)获取或生成文本。许多教程只是跳过魔术发生的部分,并且从获得的字符串创建WebGL着色器实例。没有理由你不能像你提议的那样在外部引用脚本,但是你需要额外的JavaScript来加载内容,而不是浏览器。脚本标签很可能在教程中使用,主要是因为如果您为脚本标记提供浏览器无法理解的类型,浏览器将跳过标记内容的执行或检索脚本的源 ,因此可以根据需要使用标签的内容和属性。

编辑:好吧,我必须回过头来。我决定通过四种浏览器(Chrome,Firefox,IE9,Opera),看看有线时会发生什么

<script type="x-shader/x-fragment" id="shader-fs" src="util/fs"></script>
你的HTML中的

。事实证明,浏览器 在我尝试的每个浏览器中加载文件,所以我错了。但是,这并不意味着浏览器除了缓存之外就知道如何处理文件。我不知道你的意思是“为什么不src =”util / fs“工作???”。在我试过的每个浏览器中,

alert(document.getElementById('shader-fs').src);
在给定部分路径时,

会提醒文件的完整路径。 (也许这是你的问题?当浏览器给你一个完整的时候,你期待一个部分路径?)除此之外,我不确定如何解释你的问题。