避免两个 glsl 着色器之间共享的函数的代码重复
Avoiding code duplication for a function shared between two glsl shaders
我的HTML页面(pipad.org/tmp/fourier02.html)包含两个着色器:
<script type="application/glsl" id="shaderA">
uniform vec4 a;
vec4 f(vec4 x, vec4 y){ ... } // DUP
vec4 main(vec4 u, vec4 v) { return f(a,u); }
</script>
<script type="application/glsl" id="shaderB">
uniform vec4 a;
vec4 f(vec4 x, vec4 y){ ... } // DUP
vec4 main(vec4 u) { return f(a,u); } // notice main's sig is different
</script>
我希望我没有过于简单化,如果是的话我可能需要修改。这些着色器以不同的方式使用(shaderB 是 RTT)。
可以看出,f
在这两种情况下是相同的。
有什么办法可以避免写两次?
我能看到的唯一方法是将着色器保留为字符串,这很麻烦,因为语法突出显示不再有效,您必须这样做:
:
var
f =
"vec4 f(vec4 x, vec4 y){\n" +
"...\n" +
"}\n",
shaderA = f +
"uniform vec4 a;\n" +
"vec4 main(vec4 u, vec4 v) { return f(a,u); }\n",
shaderB = f +
"uniform vec4 a;\n" +
"vec4 main(vec4 u) { return f(a,u); }\n"
;
等等
这是……嗯。与原版相比没有明显优势。我们刚刚将重复换成了恶心。
有没有更好的方法?
听起来您要找的是 template strings。默认支持多行字符串。
如果兼容性是个问题,shader = ["blah", "blah"].join('\n');
在我看来比字符串连接更干净。
最后我将我的常用函数移到了一个单独的 <script type="application/glsl" id="common">
标签中并做了:
<script>
:
function joinElements(A,B) {
return document.getElementById(A).innerHTML
+ document.getElementById(B).innerHTML;
}
:
... 然后将 "#shaderA"
替换为 joinElements("#common", "shaderA");
正如您已经发现的那样,字符串操作是制作着色器的常用方法。几乎所有大型引擎都使用大量字符串替换来在运行时构建着色器。 WaclawJasper 指出了模板字符串。它们是 JavaScript 的新功能,但由于几乎所有支持 WebGL 的浏览器都会定期更新,因此您可以非常安全地使用它们,或者您可以使用 polyfill。
例子
var t = {
PI: '3.14159',
plusToPlusMinus: `
float plusToPlusMinus(float v) {
return v * 2.0 - 1.0;
}
`,
};
var shader = `
${t.plusToPlusMinus}
...
void main() {
a = b * ${t.PI};
}
`;
console.log(shader);
输出:
float plusToPlusMinus(float v) {
return v * 2.0 - 1.0;
}
...
void main() {
a = b * 3.14159;
}
我的HTML页面(pipad.org/tmp/fourier02.html)包含两个着色器:
<script type="application/glsl" id="shaderA">
uniform vec4 a;
vec4 f(vec4 x, vec4 y){ ... } // DUP
vec4 main(vec4 u, vec4 v) { return f(a,u); }
</script>
<script type="application/glsl" id="shaderB">
uniform vec4 a;
vec4 f(vec4 x, vec4 y){ ... } // DUP
vec4 main(vec4 u) { return f(a,u); } // notice main's sig is different
</script>
我希望我没有过于简单化,如果是的话我可能需要修改。这些着色器以不同的方式使用(shaderB 是 RTT)。
可以看出,f
在这两种情况下是相同的。
有什么办法可以避免写两次?
我能看到的唯一方法是将着色器保留为字符串,这很麻烦,因为语法突出显示不再有效,您必须这样做:
:
var
f =
"vec4 f(vec4 x, vec4 y){\n" +
"...\n" +
"}\n",
shaderA = f +
"uniform vec4 a;\n" +
"vec4 main(vec4 u, vec4 v) { return f(a,u); }\n",
shaderB = f +
"uniform vec4 a;\n" +
"vec4 main(vec4 u) { return f(a,u); }\n"
;
等等
这是……嗯。与原版相比没有明显优势。我们刚刚将重复换成了恶心。
有没有更好的方法?
听起来您要找的是 template strings。默认支持多行字符串。
如果兼容性是个问题,shader = ["blah", "blah"].join('\n');
在我看来比字符串连接更干净。
最后我将我的常用函数移到了一个单独的 <script type="application/glsl" id="common">
标签中并做了:
<script>
:
function joinElements(A,B) {
return document.getElementById(A).innerHTML
+ document.getElementById(B).innerHTML;
}
:
... 然后将 "#shaderA"
替换为 joinElements("#common", "shaderA");
正如您已经发现的那样,字符串操作是制作着色器的常用方法。几乎所有大型引擎都使用大量字符串替换来在运行时构建着色器。 WaclawJasper 指出了模板字符串。它们是 JavaScript 的新功能,但由于几乎所有支持 WebGL 的浏览器都会定期更新,因此您可以非常安全地使用它们,或者您可以使用 polyfill。
例子
var t = {
PI: '3.14159',
plusToPlusMinus: `
float plusToPlusMinus(float v) {
return v * 2.0 - 1.0;
}
`,
};
var shader = `
${t.plusToPlusMinus}
...
void main() {
a = b * ${t.PI};
}
`;
console.log(shader);
输出:
float plusToPlusMinus(float v) {
return v * 2.0 - 1.0;
}
...
void main() {
a = b * 3.14159;
}