WebGL - 顶点着色器调用的变量数组大小
WebGL - Variables array sizes over vertex shader calls
上下文
我正在尝试在 canvas 中绘制贝塞尔曲线。
我实现了从着色器中绘制二次和三次曲线,但到目前为止我确实为每个控制点都有一个统一变量。
所以,我点击我的 canvas,添加点,当我有足够的点(分别是 3 和 4)时,我画我的曲线。
现在我正在尝试概括贝塞尔曲线。虽然我在 JavaScript 方面做到了这一点,但我觉得从着色器方面做到这一点会更好,因为渲染速度会大大提高。
所以,我想在至少有两个点后立即绘制曲线。但是我可以继续添加点并使用每个点绘制我的曲线,以作为控制点。
说明
所以我知道在 GLSL 中设置动态数组是不可能的,但是是否可以基于 JS 变量动态声明 GLSL 数组?
如果我的问题不清楚(我知道我很难马上表述清楚),让我用一个例子来解释。
uniform vec2 uMyPoints[<length>];
所以这就是我想要实现的,但是当然,根据 glsl 规范,数组大小必须是常量。
但是,从我的角度来看,我觉得我应该能够从 JS 变量设置 length
。我的直觉是,GLSL 中的数组大小在渲染期间不同着色器的执行期间将保持不变,但可以从一个渲染更改为另一个渲染。
问题
所以我要问你的问题是:基于这些,你知道在 GLSL 中从 javascript 变量设置常量的任何好方法或技巧吗?
如果可能的话,这对我帮助很大。感谢您的考虑。
作为一个比较问题:我们如何从 JS 设置“numLights" in this example?
回答
字符串替换非常适合我的需要。虽然这有点棘手,但它会做得很好。进一步的研究让我知道隐式数组大小在 Open GL ES 版本 3 中可用,未来版本的 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,
等..
上下文
我正在尝试在 canvas 中绘制贝塞尔曲线。 我实现了从着色器中绘制二次和三次曲线,但到目前为止我确实为每个控制点都有一个统一变量。
所以,我点击我的 canvas,添加点,当我有足够的点(分别是 3 和 4)时,我画我的曲线。
现在我正在尝试概括贝塞尔曲线。虽然我在 JavaScript 方面做到了这一点,但我觉得从着色器方面做到这一点会更好,因为渲染速度会大大提高。
所以,我想在至少有两个点后立即绘制曲线。但是我可以继续添加点并使用每个点绘制我的曲线,以作为控制点。
说明
所以我知道在 GLSL 中设置动态数组是不可能的,但是是否可以基于 JS 变量动态声明 GLSL 数组?
如果我的问题不清楚(我知道我很难马上表述清楚),让我用一个例子来解释。
uniform vec2 uMyPoints[<length>];
所以这就是我想要实现的,但是当然,根据 glsl 规范,数组大小必须是常量。
但是,从我的角度来看,我觉得我应该能够从 JS 变量设置 length
。我的直觉是,GLSL 中的数组大小在渲染期间不同着色器的执行期间将保持不变,但可以从一个渲染更改为另一个渲染。
问题
所以我要问你的问题是:基于这些,你知道在 GLSL 中从 javascript 变量设置常量的任何好方法或技巧吗?
如果可能的话,这对我帮助很大。感谢您的考虑。
作为一个比较问题:我们如何从 JS 设置“numLights" in this example?
回答
字符串替换非常适合我的需要。虽然这有点棘手,但它会做得很好。进一步的研究让我知道隐式数组大小在 Open GL ES 版本 3 中可用,未来版本的 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,
等..