上下文
我正在尝试在 Canvas 中绘制贝塞尔曲线。 我实现了从着色器中绘制二次和三次曲线,但到目前为止我确实为每个控制点设置了统一变量。
因此,我在 Canvas 上单击,添加点,当我有足够的点(分别为 3 和 4)时,我绘制曲线。
现在我正在尝试概括贝塞尔曲线。虽然我在 JavaScript 端实现了这一点,但我觉得从着色器端完成它会更好,因为渲染速度会大大提高。
因此,我想在至少有两个点后立即绘制曲线。但我可以继续添加点并使用每个点绘制曲线,以作为控制点。
解释
所以我知道在 GLSL 中设置动态数组是不可能的,但是是否可以基于 JS 变量动态声明 GLSL 数组?
如果我的问题不清楚(我知道我很难马上表述清楚),让我用一个例子来解释。
uniform vec2 uMyPoints[<length>];
所以这就是我想要实现的,但是当然,根据 glsl 规范,数组大小必须是常量。
但是,从我的 Angular 来看,我觉得我应该能够从 JS 变量设置 length
。我的直觉是,在渲染期间不同着色器的执行期间,GLSL 中的数组大小将保持不变,但可以从一个渲染更改为另一个渲染。
问题
所以我要问你的问题是:基于这些,你是否知道能够从 javascript 变量在 GLSL 中设置常量的任何好方法或技巧?
如果这似乎是可能的,这将对我有很大帮助。感谢您的考虑。
作为比较:我们如何从 JS 设置“numLights" in this example”?
回答
字符串替换非常适合我的需要。虽然这有点棘手,但它会做得很好。进一步的研究让我知道隐式数组大小在 Open GL ES 版本 3 中可用, future 版本的 WebGL 应该会使用它,但不是现在。
另一方面,第二个建议不符合我的需要,因为我正是想避免在着色器中有 N 个点,因为点的数量可能会改变。
感谢您的回答;)
最佳答案
字符串替换有效
<script id="vs" type="notjs">
uniform vec2 uMyPoints[<length>];
...
</script>
js
var numPoints = 10;
var vSrc = document.getElementById("vs").text;
vSrc = vSrc.replace(/<length>/g, numPoints);
这是大多数复杂程序为着色器做的事情。他们通过字符串操作生成着色器。
当然,您可能希望使用更好的函数来进行字符串替换。例如可能是这样的
/**
* Replace %(id)s in strings with values in objects(s)
*
* Given a string like `"Hello %(name)s from $(user.country)s"`
* and an object like `{name:"Joe",user:{country:"USA"}}` would
* return `"Hello Joe from USA"`.
*
* @function
* @param {string} str string to do replacements in
* @param {Object|Object[]} params one or more objects.
* @returns {string} string with replaced parts
* @memberOf module:Strings
*/
var replaceParams = (function() {
var replaceParamsRE = /%\(([^\)]+)\)s/g;
return function(str, params) {
if (!params.length) {
params = [params];
}
return str.replace(replaceParamsRE, function(match, key) {
var keys = key.split('.');
for (var ii = 0; ii < params.length; ++ii) {
var obj = params[ii];
for (var jj = 0; jj < keys.length; ++jj) {
var part = keys[jj];
obj = obj[part];
if (obj === undefined) {
break;
}
}
if (obj !== undefined) {
return obj;
}
}
console.error("unknown key: " + key);
return "%(" + key + ")s";
});
};
}());
现在如果你的着色器看起来像这样
uniform Lights u_lights[%(numLights)s];
uniform vec2 u_points[%(numPoints)s];
你可以替换为
vSrc = replaceParams(vsrc, {
numLights: 4,
numPoints: 10,
});
当然你也可以在着色器中使用`#define
#define NUM_LIGHTS %(numLights)s
#define NUM_POINTS %(numPoints)s
uniform Lights u_lights[NUM_LIGHTS];
uniform vec2 u_points[NUM_POINTS];
void main() {
for (int i = 0; i < NUM_LIGHTS; ++i) {
...
}
}
等..
但是,老实说,大多数人不会通过贝塞尔曲线控制点作为制服,因为对制服的数量有严格的限制。大多数人会通过属性中的贝塞尔曲线控制点。你甚至可以在调用 gl.vertexAttribPointer
时设置步幅和偏移量,这样如果你的点数不对
[pt0, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8, ..]
可以制作4个属性
attribute vec2 p0;
attribute vec2 p1;
attribute vec2 p2;
attribute vec2 p3;
然后用偏移量和步幅指向所有这些点以设置您的 4 个属性,以便提取点
p0 = pt0, p1 = pt1, p2 = pt2, p3 = pt3,
p0 = pt1, p1 = pt2, p2 = pt3, p3 = pt4,
p0 = pt2, p1 = pt3, p2 = pt4, p3 = pt5,
p0 = pt3, p1 = pt4, p2 = pt5, p3 = pt6,
等..
关于javascript - WebGL - 顶点着色器调用的变量数组大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33299796/