如何在外部文件中移动着色器脚本?
我想知道如何将包含在我的html中的着色器移动到外部文件中。通过这种方式,我可以将它们包含在我的大量任务中。我看了一下如何编写JavaScript着色器文件,但我不太明白。如何在外部文件中移动着色器脚本?
例如下面的Glow着色器代码,我如何将它移动到外部文件?
<script type="x-shader/x-vertex" id="vertexShaderGlow">
uniform vec3 viewVector;
uniform float c;
uniform float p;
varying float intensity;
void main()
{
vec3 vNormal = normalize(normalMatrix * normal);
vec3 vNormel = normalize(normalMatrix * viewVector);
intensity = pow(c - dot(vNormal, vNormel), p);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<script type="x-shader/x-vertex" id="fragmentShaderGlow">
uniform vec3 glowColor;
varying float intensity;
void main()
{
vec3 glow = glowColor * intensity;
gl_FragColor = vec4(glow, 1.0);
}
</script>
建议加入glsl行数组的答案是可以遇到的三种模式,但应该在此用例中避免。
它可能在某种模块中很有用,它是着色器的“编译”快照,不打算修改。
否则这种方法的主要缺点是缺少语法高亮和冗长。
现在大多数js代码都是以某种方式转换的。着色器代码应该像这样内联。
const myShader = new THREE.ShaderMaterial({
vertexShader: require('./myVert.vs'),
fragmentShader: require('./myFrag.vs'),
})
编辑
myVert.vs:
//this is all nicely highlighted in sublime text for example
void main(){
gl_Position = vec4(position.xy, 0., 1.);
}
myVert.fs:
void main(){
gl_FragColor = vec4(1.,0.,0.,1.);
}
myClass.js:
class myMaterial extends THREE.ShaderMaterial{
constructor(){
super({
vertexShader: require('./myVert.vs'),
//^ becomes vertexShader: 'void main() {...}'
...
该方法看起来不错,但它会不会增加HTTP请求的数量?包含在Javascript文件中的着色器对我来说是件好事,因为我能够缩小/合并它们,并为我的所有网站加载一个Javascript。 .vs文件中的代码与脚本代码中的代码相同,或者我需要修改它? – ClassikD
不,这是与webpack或其他捆绑工具一起使用的。 '.vs'只是一个txt文件。捆绑工具处理完成后,'require(..)'将被替换为文件的内容。没有额外的请求,这只是在你的主包中内联。 – pailhead
**请注意**着色器不过是字符串,这里的问题是获取字符串,同步,异步,格式化或不。 Three本身提供了一些用于处理这些特殊字符串的特性,例如'#import ...'语句,它们不属于GLSL的一部分。因此,选择最合适的格式就有点儿了。简单的旧javascript不能做一个多行字符串,这就是为什么你需要添加'\ n'或者加入数组中的行。下一步是es6字符串,您可以简单地写入多行。 – pailhead
您可以将着色器代码移动到单独的JS文件中,并在three.js之后包含该文件。
这里是https://github.com/timoxley/threejs/blob/master/examples/js/shaders/ColorifyShader.js
/**
* @author alteredq/http://alteredqualia.com/
*
* Colorify shader
*/
THREE.ColorifyShader = {
uniforms: {
"tDiffuse": { type: "t", value: null },
"color": { type: "c", value: new THREE.Color(0xffffff) }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
"}"
].join("\n"),
fragmentShader: [
"uniform vec3 color;",
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
"vec4 texel = texture2D(tDiffuse, vUv);",
"vec3 luma = vec3(0.299, 0.587, 0.114);",
"float v = dot(texel.xyz, luma);",
"gl_FragColor = vec4(v * color, texel.w);",
"}"
].join("\n")
};
通过以上你一个例子就是创造我们的材料是这样的:
material = new THREE.ShaderMaterial({
uniforms : THREE.ColorifyShader.uniforms,
vertexShader : THREE.ColorifyShader.vertexShader,
fragmentShader : THREE.ColorifyShader.fragmentShader
});
ofcourse你不需要调用对象THREE.ColorifyShader
,你可以把它叫做什么你要。
提供的另一个答案是简单地取GLSL代码并将每行转换为一个字符串。每个字符串都是数组中的一个值。调用join
调用将所有字符串与\n
字符连接起来,以便在调试时更易于读取代码。我以前多次这样做过,并且是您尝试做的合法解决方案。
但是,如果您希望使用原始GLSL代码创建外部文件,您也可以这样做。考虑两个文件:
- glow_vertex.glsl
- glow_fragment.glsl
这些文件包含着色器的代码,您通常会在脚本标记。您可以使用XMLHTTPRequest来获取文件,并使用返回的文本作为着色器代码。
var vertexShader = null;
var fragmentShader = null;
function shadersDone(){
var material = new THREE.ShaderMaterial({
uniforms: { /* define your uniforms */},
vertexShader: vertexShader,
fragmentShader: fragmentShader
});
}
function vertexDone(code){
vertexShader = code;
if(fragmentShader !== null){
shadersDone();
}
}
function fragmentDone(code){
fragmentShader = code;
if(vertexShader !== null){
shadersDone();
}
}
var xhr1 = new XMLHttpRequest();
var xhr2 = new XMLHttpRequest();
xhr1.open("GET", "/server/glow_vertex.glsl", true);
xhr2.open("GET", "/server/glow_fragment.glsl", true);
xhr1.responseType = "text";
xhr2.responseType = "text";
xhr1.onload = function(){
if(xhr1.readyState === xhr1.DONE && xhr1.status === 200){
vertexDone(xhr1.resultText);
}
};
xhr2.onload = function(){
if(xhr2.readyState === xhr2.DONE && xhr2.status === 200){
fragmentDone(xhr2.resultText);
}
};
xhr1.send(null);
xhr2.send(null);
请注意,这都是异步的。此外,您的服务器将需要配置为以纯文本格式发送GLSL文件。
尽管我们正在谈论现代网络长...
也有选项import
您的着色器代码。 非常大但是它目前仅在Chrome和Opera中支持(尽管polyfills确实存在)。 Microsoft Edge将功能列为“正在考虑中”,并且Firefox不缩进以在当前状态下实现它。因此,需要用大粒盐的下面,并留意:http://caniuse.com/#search=import
在HTML和JavaScript的前这将使用它...
<link id="vertexImport" rel="import" href="/server/glow_vertex.glsl" />
<link id="fragmentImport" rel="import" href="/server/glow_fragment.glsl" />
再后来在你的JavaScript:
var material = new THREE.ShaderMaterial({
vertexShader: document.getElementById("vertexImport").import.body.childNodes[0].data,
fragmentShader: document.getElementById("fragmentImport").import.body.childNodes[0].data,
});
此外,这是异步的。您可能需要为每个链接添加onload
处理程序,因此您不会在加载代码之前尝试访问代码。
您只需将着色器代码作为字符串使用,因此可以使用允许您从外部文件引入字符串的任何方式。 – 2pha
你能举个例子吗? – ClassikD
我编辑了由邵 – 2pha