WebGL - 在顶点着色器调用上变量数组大小

Cru*_*leZ 6 javascript arrays glsl webgl vertex-shader

上下文

我试图在画布上绘制贝塞尔曲线.我实现了从着色器中绘制二次曲线和三次曲线,但到目前为止我确实为每个控制点都有一个统一变量.

所以,我点击我的画布,添加点,当我有足够的(分别为3和4)时,我画出我的曲线.

现在我试图推广Bezier曲线.虽然我在JavaScript方面实现了这一目标,但我觉得从着色器方面做到这一点会更好,因为渲染速度会大大提高.

所以,一旦我至少得到两分,我想画出我的曲线.但我可以继续添加点并使用每个点绘制我的曲线,以作为控制点.

说明

所以我知道在GLSL中设置动态数组是不可能的,但是可以根据JS变量动态声明一个GLSL数组吗?

如果我的问题不清楚(我知道我有麻烦直接制定事情),让我用一个例子来解释.

uniform vec2 uMyPoints[<length>];
Run Code Online (Sandbox Code Playgroud)

所以这就是我想要实现的,但是当然,根据glsl规范,数组大小必须是常量.

但是,从我的角度来看,我觉得我应该能够length从JS变量进行设置.我的直觉是GLSL中的数组大小在渲染期间执行不同着色器的持续时间内是恒定的,但可能会从一个渲染更改为另一个渲染.

所以我的问题是:基于这些,您是否知道能从javascript变量在GLSL中设置常量的任何好方法或技巧?

如果看起来有可能,这对我有很大的帮助.谢谢你的考虑.

作为比较:我们如何在这个例子中从JS 设置" numLights"

回答

字符串替换非常适合我的需求.虽然它有点棘手,但它会做得很好.进一步的研究使我知道隐式数组大小是可用的Open GL ES版本3,它应该被未来版本的WebGL使用,但现在不是.

另一方面,第二个建议不适合我的需要,因为我确实想避免在着色器中有N个点,因为点的数量可能会改变.

谢谢你的回答 ;)

gma*_*man 4

字符串替换有效

<script id="vs" type="notjs">    
uniform vec2 uMyPoints[<length>];
...
</script>
Run Code Online (Sandbox Code Playgroud)

js

var numPoints = 10;
var vSrc = document.getElementById("vs").text;
vSrc = vSrc.replace(/<length>/g, numPoints);
Run Code Online (Sandbox Code Playgroud)

这就是大多数复杂程序为着色器所做的事情。他们通过字符串操作生成着色器。

当然,您可能想使用更好的函数来进行字符串替换。例如也许像

  /**
   * 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";
      });
    };
  }());
Run Code Online (Sandbox Code Playgroud)

现在如果你是着色器看起来像这样

uniform Lights u_lights[%(numLights)s];
uniform vec2 u_points[%(numPoints)s];
Run Code Online (Sandbox Code Playgroud)

你可以用

vSrc = replaceParams(vsrc, {
   numLights: 4,
   numPoints: 10,
});
Run Code Online (Sandbox Code Playgroud)

当然你也可以在着色器中使用`#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) {
    ...
  }
}
Run Code Online (Sandbox Code Playgroud)

ETC..

但是,老实说,大多数人不会将贝塞尔曲线控制点作为制服通过,因为制服的数量有严格的限制。大多数人都会通过属性中的贝塞尔曲线控制点。您甚至可以在调用时设置步幅和偏移量,gl.vertexAttribPointer这样如果您的点去

 [pt0, pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8, ..]
Run Code Online (Sandbox Code Playgroud)

可以设置4个属性

attribute vec2 p0;
attribute vec2 p1;
attribute vec2 p2;
attribute vec2 p3;
Run Code Online (Sandbox Code Playgroud)

并用偏移量和步幅来指向所有这些点以设置您的 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,
Run Code Online (Sandbox Code Playgroud)

ETC..