在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:。我以为我会把它放在这里以防万一有人发现它有用。
答案 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.js的text 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宏#include
和gcc -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
如果要加载新的着色器,可以动态插入新的脚本标签。