Часть вторая
Приветствую в серии статей о WebGL рендринге ландшафта, используемом в игре Trigger Rally!
Если еще не успели, то прочитайте первую часть, в которой я рассказываю о важности минимизации передачи данных между процессором и видеокартой, а также предлагаю хранить статические данные о высоте каждой точки ландшафта в текстуре.
В этой статье я расскажу структуре данных вершин, а так же о морфинге.
От переводчика: в статье много косноязычия и технических неточностей. Помогите их исправить!
Кольца
Geoclipmap рендринг оперирует с квадратными «кольцами» вокруг точки обзора, причем каждое кольцо в два раза больше предыдущего, но имеет в два раза меньшее разрешение. Благодаря этому мы видим плавное уменьшение количества вершин на единицу плоскости, по принципу: дальше — меньше. Внутренне кольцо полностью заполнено и имеет наивысшее разрешение, становясь простой квадратной сеткой треугольников:
Повторяющуюся геометрию можно транслировать без особо заметных изменений, но будет казаться, что края немного переместились:
Мы можем использовать это свойство, для того что бы сдвигать геометрию относительно камеры.
Так как каждое кольцо треугольников имеет собственный размер, а от размера зависит расстояние для переноса, мы должны перемещать кольца независимо друг от друга. Следовательно, вершинный шейдер должен знать, какой именно слой вершин он перемещает, что бы изменять его правильно.
В общем, нам нужно знать такие параметры вершин:
- Положение по оси X
- Положение по оси Y
- Номер слоя
В Trigger Rally мы используем [X,Y,Z] вектор, а так же получаем номер слоя, в котором находится вершина, согласно ее положению по оси Z.
Зашивание дыр
Каждое кольцо имеет собственный масштаб, кроме того, каждое кольцо сдвигается на величину зависящую от масштаба. Таким образом может возникнуть проблема — одно кольцо транслировано, а ему соседнее нет и между ними образуется зазор:
Один из способов решения этой проблемы состоит в том, что бы дополнительно наращивать на край кольца «юбку». Согласно этой статье, юбка собирается из множества маленьких кусочков, для этого используется несколько буферов вершин и сложная процессорная логика. А нам ведь этого совсем не хочется!
При реализации ландшафта в Trigger Rally, я провела часы, пытаясь найти производительный и качественный способ собирать юбку, но увы, безрезультатно.
Но на последнем WebGL Camp Europe я встретила Florian Bösch, и он предложил сделать кольца немного больше, тем самым позволяя им пересекаться.
Сейчас любой более-менее опытный программист графики начнет вопить: «Нет! Нельзя допускать пересечения геометрии! Это расточительно и могут появиться жуткие артефакты!». Но на самом деле все не так плохо — нам действительно придется рисовать немного больше, но если геометрия хорошо подогнана, то это действительно отличное решение!
Морфинг
На границе колец мы имеем геометрию одного разрешения, которая соприкасается с геометрией имеющей размер в два раза меньший. Нам нужно сделать переходные области на краю каждого кольца, в которых геометрия будет плавно «перетекать» из высокого разрешения в низкое, так что бы край одного кольца строго совпадал с краем другого.
Вот так каждая вершина должна быть перемещена для соответствия следующему кольцу:
Мы должны делать это в вершинном шейдере. Самый просто способ заключается в том, что бы включить вектор морфинга в структуру данных вершины, но, снова таки, Florian предложил кое что получше — использовать модульную арифметику!
Чтобы показать, как это работает, давайте представим данные в виде таблицы:
Таким образом мы можем вычислить вектор морфинга, имея только координаты вершин, воспользовавшись таким GLSL кодом:
vec2 morphVector = mod(position.xy, 2.0) * (mod(position.xy, 4.0) - 2.0);
И это все без дополнительных свойств в структуре вершин! (прим. пер.: снимаю шляпу перед этим Florian)
Дальше будет...
В следующем посте, я расскажу о том, как в Trigger Rally хранится карта высот и как она обрабатывается в вершинном шейдере. Потом мы рассмотрим surface shading в шейдере фрагментов, ну и наконец как эффективнее рендрить окружение.
Спасибо за чтение!
Автор: agentx001