javascript - WebGL - 顶点着色器调用的变量数组大小

标签 javascript arrays glsl webgl vertex-shader

上下文

我正在尝试在 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/

相关文章:

javascript - 你不能在单独的文件中有 JavaScript 对象吗?

javascript - 无法让 jQuery 切换脚本在 jQuery 对话框内运行

glsl - 您可以在 GLSL 着色器中通过引用传递矩阵吗?

安卓 OpenGL ES 2.0 : Is "switch-case" syntax possible in GLSL fragment shader on Samsung Galaxy S2?

glsl - GLSL-长度函数

javascript - 如何轻松地与父范围通信

javascript - 无法使用angularjs在 typescript 的方法中使用从构造函数注入(inject)的服务

javascript - 使用 JavaScript 循环两个数组

c# - 通过 ZeroMQ 套接字发送 Pillow/numpy 图像

python - 查找二维 numpy 数组中最大和的位置