javascript 为什么着色器必须在 webgl 程序的 html 文件中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14219947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do shaders have to be in html file for webgl program?
提问by Dov
I have seen the following question where someone asked how to remove shaders from html: WebGL - is there an alternative to embedding shaders in HTML?
我看到以下问题,有人问如何从 html 中删除着色器: WebGL - 是否有替代在 HTML 中嵌入着色器的方法?
There are elaborate workarounds to load in a file containing the shader suggested in the answers to the question.
有详细的解决方法可以加载到包含问题答案中建议的着色器的文件中。
In the tutorial I saw, the shader code is embedded directly in the html. The javascript code refers to it using getElementById. But it's ugly embedding the shader directly in the html for many reasons. Why can't I just refer to it externally using the src= attribute?
在我看到的教程中,着色器代码是直接嵌入到html中的。javascript 代码使用 getElementById 引用它。但是由于多种原因,将着色器直接嵌入到 html 中是很丑陋的。为什么我不能使用 src= 属性在外部引用它?
<script type="x-shader/x-fragment" id="shader-fs" src="util/fs"></script>
The above doesn't work, I just want to know why not. This is clearly something to do with limitations on script itself, but I don't get it.
上面的不行,我只是想知道为什么不行。这显然与脚本本身的限制有关,但我不明白。
回答by Philipp
You don't have to use <script>
tags at all to load a shader program. Most tutorials and examples just use them as a container to store a string in the DOM of the webpage. The script type "x-shader/x-fragment"
is meaningless for web browsers, so they don't execute the script. They do, however, store the content of that tag as a string in the DOM which can then later be accessed by "real" scripts. This only works when the script content is in the HTML file. When you load the script via a src attribute, the content does not become a text childnode of the script tag and thus can not be accessed through the DOM tree.
您根本不必使用<script>
标签来加载着色器程序。大多数教程和示例只是将它们用作在网页的 DOM 中存储字符串的容器。脚本类型"x-shader/x-fragment"
对于 Web 浏览器没有意义,因此它们不执行脚本。然而,它们确实将该标签的内容作为字符串存储在 DOM 中,然后可以由“真实”脚本访问。这仅在脚本内容在 HTML 文件中时有效。当您通过 src 属性加载脚本时,内容不会成为脚本标签的文本子节点,因此无法通过 DOM 树访问。
You can just as well store the sourcecode for the shader as a string in a Javascript file:
您也可以将着色器的源代码作为字符串存储在 Javascript 文件中:
// myVertextShader.glsl.js
var myVertexShaderSrc =
"attribute vec3 pos;"+
"void main() {"+
" gl_Position = vec4(pos, 1.0);"+
"}"
;
You would then compile the shader like this:
然后你可以像这样编译着色器:
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, myVertexShaderSrc);
gl.compileShader(vertexShader);
gl.attachShader(program, vertexShader);
回答by gman
Update 2018
2018 年更新
In 2018 I'd suggest using multiline template literals as in surround the shader with backticks and it can cross multiple lines
在 2018 年,我建议使用多行模板文字,如用反引号围绕着色器,它可以跨越多行
const someShaderSource = `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
If you want to put shaders in separate files you can easily do this in 2018 using JavaScript modules. A shader file might look like this
如果您想将着色器放在单独的文件中,您可以在 2018 年使用 JavaScript 模块轻松实现。着色器文件可能如下所示
// someshader.glsl.js
export default `
attribute vec4 position;
uniform mat4 matrix;
void main() {
gl_Position = matrix * position;
}
`;
To use JavaScript modules your main JavaScript must be in a separate file. You can access the shader source by importing it
要使用 JavaScript 模块,您的主要 JavaScript 必须位于单独的文件中。您可以通过导入来访问着色器源
// main.js
import someShaderSource from './someshader.glsl.js';
// use someShadeSource
And you include it in your HTML with
你把它包含在你的 HTML 中
<script src="main.js" type="module"></script>
Or you can use it from a script tag in the page itself like this
或者您可以像这样从页面本身的脚本标记中使用它
<script type="module">
import someShaderSource from './someshader.glsl.js';
// use someShadeSource
</script>
If you want to support older browsers you can use a tool like rollupwhich will read all the import
statements and generate one large JavaScript file. This is what three.js does.
如果你想支持旧浏览器,你可以使用像rollup这样的工具,它会读取所有import
语句并生成一个大的 JavaScript 文件。这就是three.js所做的。
If you need to support IE11 you can use babelto convert the multiline templates. All other browsers have supported multiline templates for many years.
如果你需要支持 IE11,你可以使用babel来转换多行模板。多年来,所有其他浏览器都支持多行模板。
Original Answer
原答案
Why do shaders have to be in html file for webgl program?
为什么着色器必须在 webgl 程序的 html 文件中?
They don't
他们不
You can put shaders in external javascript. For example
您可以将着色器放在外部 javascript 中。例如
// --myshader.js--
var myFragmentShader =
"void main() {\n" +
" gl_FragColor = vec4(1,0,0,1);\n" +
"}n\";
Or another common format
或其他常见格式
// --myshader.js--
var myFragmentShader = [
"void main() {",
" gl_FragColor = vec4(1,0,0,1);",
"}",
].join("\n");
In all browsers that support WebGL you can use template literals
在所有支持 WebGL 的浏览器中,您都可以使用模板文字
// --myshader.js--
var myFragmentShader = `
void main() {
gl_FragColor = vec4(1,0,0,1);
}
`;
Otherwise you can put them in text files and load them with XMLHTTPRequest
否则,您可以将它们放在文本文件中并使用 XMLHTTPRequest 加载它们
// --myshader.txt
void main() {
gl_FragColor = vec4(1,0,0,1);
}
Then in JavaScript do the following
然后在 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...
});
The reason people put them in the HTML is because it's easy, efficient, and synchronous.
人们将它们放在 HTML 中的原因是因为它简单、高效且同步。
easy: unlike the JS file versions you don't have to surround every line with quotes and other punctuation. Although now with es6 that's no longer an issue. Every browser that supports WebGL supports es6 template strings.
简单:与 JS 文件版本不同,您不必用引号和其他标点符号将每一行括起来。尽管现在有了 es6,这不再是问题。每个支持 WebGL 的浏览器都支持 es6 模板字符串。
efficient: unlike the text and js files there's only one request to the server. Of course some people might run a concatenator on their js files to fix some of that.
高效:与 text 和 js 文件不同,只有一个请求到服务器。当然,有些人可能会在他们的 js 文件上运行连接器来解决其中的一些问题。
synchronous: unlike the text files their usages is synchronous. No need for callbacks or promises or otherwise dealing with asynchronous issues of downloading files.
同步:与文本文件不同,它们的用法是同步的。不需要回调或承诺或以其他方式处理下载文件的异步问题。
As for why your example doesn't work I'm pretty sure the reason is it would allow cross origin resource access. The <script>
tag was designed before people figured out cross origin access was a problem so they couldn't turn off cross origin scripts without breaking a bunch of sites. They could make everything else more strict.
至于为什么您的示例不起作用,我很确定原因是它允许跨源资源访问。该<script>
标签是在人们发现跨源访问是一个问题之前设计的,因此他们无法在不破坏一堆站点的情况下关闭跨源脚本。他们可以使其他一切变得更加严格。
XMLHttpRequest for example does not allow cross origin access unless the server you're contacting gives permission. If script tags let you access that content you could use script tags to work around that restriction. In other words instead of making a XMLHttpRequest and reading the request.responseText
for the result you'd just programmatically make a script tag, set its src
to the URL you want and then read its text
field when it finished. To make sure you can not do that you're not allowed to read the text
field of a script tag that had a src
attribute
例如 XMLHttpRequest 不允许跨源访问,除非您正在联系的服务器给予许可。如果脚本标签允许您访问该内容,您可以使用脚本标签来解决该限制。换句话说,不是创建 XMLHttpRequest 并读取request.responseText
结果,您只需以编程方式创建脚本标记,将其设置src
为所需的 URL,然后text
在完成后读取其字段。为了确保您不能这样做,您不允许读取text
具有src
属性的脚本标记的字段
回答by JayC
Shader language scripts are just text.The text can be grabbed or generated from anywhere (that you can read or generate text). Many tutorials just skip over the part where the magic happens and and the WebGL shader instances are created from the string obtained. There's no reason you couldn't refer to the scripts externally like you propose, but you would need additional JavaScript to load the contents, not the browser. Script tags are likely used in the tutorials primarily because if you give a script tag a type that the browser doesn't understand, the browser skips execution of the tag's contents or retrieval of the script's source, so the tag's contents and attributes can be used however you desire.
着色器语言脚本只是文本。可以从任何地方抓取或生成文本(您可以阅读或生成文本)。许多教程只是跳过了魔法发生的部分,并且从获得的字符串创建了 WebGL 着色器实例。您没有理由不能像您建议的那样从外部引用脚本,但是您需要额外的 JavaScript 来加载内容,而不是 browser。脚本标签可能在教程中使用主要是因为如果你给一个脚本标签一个浏览器不理解的类型,浏览器会跳过标签内容的执行或脚本源的检索,因此可以使用标签的内容和属性随心所欲。
Edit: Well, I have to take some things back. I decided to go through four browsers (Chrome, Firefox, IE9, Opera), and see what happens when you have a line
编辑:嗯,我得拿回一些东西。我决定通过四种浏览器(Chrome、Firefox、IE9、Opera),看看当你有一条线时会发生什么
<script type="x-shader/x-fragment" id="shader-fs" src="util/fs"></script>
in your html. Turns out, the browser doesload the file in every browser I tried, so I was wrong. However, that doesn't mean the browser knows what to do with the file besides cache it. I don't know what you mean by "Why doesn't src="util/fs" work???". In every browser I've tried,
在你的 html 中。事实证明,浏览器确实在我尝试过的每个浏览器中加载了文件,所以我错了。但是,这并不意味着浏览器除了缓存文件之外,还知道如何处理该文件。我不知道您所说的“为什么 src="util/fs" 不起作用???”是什么意思。在我尝试过的每个浏览器中,
alert(document.getElementById('shader-fs').src);
alerts the full path to the file when given a partial path. (Maybe this is your problem? You're expecting a partial path when the browser gives you a full one?) Besides that, I'm not sure how to interpret your problem.
当给定部分路径时,警告文件的完整路径。(也许这是您的问题?当浏览器为您提供完整路径时,您期望得到部分路径?)除此之外,我不确定如何解释您的问题。