WebGL-размеры массива переменных над вызовами вершинных шейдеров

контекст

Я пытаюсь нарисовать кривые Безье на холсте. Мне удалось нарисовать квадратичные и кубические кривые внутри шейдера, но у меня была равномерная переменная для каждой контрольной точки.

Итак, я нажимаю на свой холст, добавляю точки и когда у меня достаточно (соответственно 3 и 4), я рисую свою кривую.

теперь я пытаюсь обобщить кривые Безье. Хотя я достиг этого на стороне JavaScript, я чувствую, что это было бы лучше сделать это со стороны шейдера, так как скорость рендеринга будет значительно увеличена.

Итак, я хотел бы нарисовать свою кривую, как только у меня будет хотя бы две точки. Но я мог бы продолжать добавлять точки и рисовать свою кривую, используя каждую точку в качестве контрольных точек.

объяснение

поэтому я знаю, что невозможно установить динамический массив в GLSL, но возможно ли динамически объявить массив GLSL на основе переменной JS ?

Если мой вопрос неясен (я знаю, что у меня проблемы с формулировкой вещей сразу), позвольте мне объяснить на примере.

uniform vec2 uMyPoints[<length>];

Итак, это то, чего я хотел бы достичь, но, конечно, размер массива должен быть постоянным, согласно спецификациям glsl.

однако, с моей точки зрения, я чувствую, что я должен быть в состоянии создать length из переменной JS. Моя интуиция заключается в том, что размер массива в GLSL будет постоянным в течение выполнение различных шейдеров во время рендеринга, но может меняться от одного перевода к другому.

вопрос

Итак, мой вопрос к вам: основываясь на них, знаете ли вы какой-либо хороший способ или трюки, чтобы иметь возможность установить константу в GLSL из переменной javascript ?

Если это представляется возможным, это очень помогло бы мне. Спасибо за внимание.

для сравнения: как мы могли бы установить"numLights " в этом пример из JS ?

ответ

string substitution хорошо подходит для моих нужд. Хотя это немного сложно, это будет просто прекрасно. Дальнейшие исследования привели меня к тому, что размер неявного массива доступен Open GL ES версии 3, который должен использоваться будущими версиями WebGL, но не сейчас.

С другой стороны, второе предложение не соответствует моим потребностям, поскольку я точно хотел избежать наличия N точек в шейдере, так как это количество точки могут меняться.

Спасибо за ответ ;)

1 ответов


работает подстановка строк

<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) {
    ...
  }
}

etc..

но, честно говоря, большинство людей не прошли бы контрольные точки Безье в качестве униформы, потому что существует серьезное ограничение на количество униформы. Большинство людей прошли бы контрольные точки Безье в атрибутах. Вероятно, вы даже можете установить шаг и смещение при вызове 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,

etc..