Отображение текстуры в плитку - PullRequest
2 голосов
/ 22 января 2020

Я застрял на этой задаче и не знаю, как решить эту проблему. Мне нужно нарисовать мозаичную сетку с текстурами, мой мир представляет мозаичную сетку, где ширина и высота каждой плитки равна 1. При рисовании я вычисляю буфер вершин, который содержит вершины плиток, которые видны для камеры, например, на экране: enter image description here

(поэтому у меня есть один VBO для всех этих вершин) Кроме того, у меня есть буфер элементов, который содержит индексы, и я рисую их в режиме GL_TRIANGLE_STRIP: enter image description here Реальный результат: enter image description here


И на этом шаге все работает просто отлично, но затем мне нужно нанести на каждый квадрат свою текстуру, которую я получаю из Интернета, все текстуры разные. Как я могу это сделать? Я использую OpenGL ES 2.0 и C ++.

1 Ответ

2 голосов
/ 22 января 2020

Хорошо, если другие вопросы и ответы не объясняют это достаточно хорошо, позвольте мне попробовать.

Проблема действительно в том, что вы используете треугольную полосу . Треугольная полоса имеет ряд применений, и наиболее важной причиной ее использования является уменьшение объема данных для хранения вершин:

Основная причина использования треугольных полос состоит в уменьшении количества данные, необходимые для создания серии треугольников. Количество вершин, хранящихся в памяти, уменьшено с 3N до N + 2, где N - количество нарисованных треугольников.

(Википедия)

Треугольная полоса достигает этого чудесное свойство, просто используя данные из предыдущих треугольников. Он берет две вершины из предыдущего треугольника и одну дополнительную, что позволяет сформировать новый треугольник на этом наборе точек. Это работает очень хорошо, если все вершины образуют одну непрерывную поверхность, и каждая вершина имеет смысл как часть первого треугольника, так и следующего.

например, для последовательности вершин:

0:  (0,0)
1:  (0,1)
2:  (1,0)
3:  (1,1)

В итоге мы получим

1---3
|\  |
| \ |
|  \|
0---2

Так что, действительно, треугольники формируются из индексов (0,1,2) и (1,2,3).

Даже когда мы добавили текстурирование, оно все еще работает. Предполагая, что текстура равна четырем плиткам, мы можем получить:

0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

3:  (1,1) (0.5, 0.5)
4:  (2,0) (1  ,   0)
5:  (2,1) (1  , 0.5)

С результатом:

1---3---5
|\  |\  |
| \ | \ |
|  \|  \|
0---2---4

Ключевые вершины, которые нужно наблюдать: 2 и 3. Для вершины 2 координата текстуры равна (0,5, 0), которая является одновременно правым краем 2-го треугольника и левым краем 3-го. Эта вершина естественным образом принадлежит им обоим, как по позициям, так и по текстуре.


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

Таким образом, координаты текстуры для первой пары треугольников могут быть одинаковыми, а координаты текстуры вторая пара может иметь смещение, скажем, (+5, +5) (предположим, что текстура сейчас составляет, например, 100x100, поскольку это легче читать).

Итак, что теперь происходит с вершинами 2 и 3 ? Координаты текстуры не могут быть одновременно 0.5 и 5. Они представляют собой просто отдельные вершины двух треугольников, которые случаются с l ie рядом друг с другом по положению, но являются совершенно отдельными по текстуре. Треугольная полоса, повторно использующая все атрибуты из предыдущих вершин, теперь является препятствием.


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

-- triangle 0
0:  (0,0) (0  ,   0)
1:  (0,1) (0  , 0.5)
2:  (1,0) (0.5,   0)

-- triangle 1
3:  (0,1) (0  , 0.5)
4:  (1,0) (0.5,   0)
5:  (1,1) (0.5, 0.5)

-- triangle 2
6:  (1,0) (5  ,   5)
7:  (1,1) (5  , 5.5)
8:  (2,0) (5.5,   5)

-- triangle 3
9:  (1,1) (5  , 5.5)
10: (2,0) (5.5,   5)
11: (2,1) (5.5, 5.5) 

Это необработанные данные, но я понимаю, что за ними трудно следить, поэтому давайте использовать индексы и посмотри еще раз. Предположим позиции go, как они делали раньше (0-5), координаты текстуры от t0 до t3 для первого треугольника и от u0 до u3 для второго. Сейчас:

0:  0 t0
1:  1 t1
2:  2 t2

3:  1 t1
4:  2 t2
5:  3 t3

6:  2 u0
7:  3 u1
8:  4 u2

9:  3 u1
10: 4 u2
11: 5 u3

Фу! Теперь немного легче определить принципиальную разницу: позиция 2 появляется в сочетании с texcoord t2 в первом треугольнике, но с позицией u0 во втором. Аналогично, позиция 3 взаимодействует с t3 и u1 соответственно. Это потому, что вершина 2 - это третья вершина первого треугольника, но первая вершина второго и т. Д.

И это все! Теперь вам просто нужно написать код для генерации такого макета, настройте ваши VBO так, как вам удобно (помните, что атрибут вершины для позиций может быть в совершенно другом VBO, чем атрибут для плиток, чтобы упростить обновления только контента листов без переписывания сами плитки) и все готово.

Помните, что, как я уже упоминал ранее, это все еще нарисовано в одном розыгрыше . Весь VBO линейно обрабатывается графическим процессором так быстро, как только может, и это должно привести к очень хорошей производительности, немного более высокое использование памяти довольно незначительно, учитывая, с какими данными мы здесь работаем, и размером памяти Типичные графические процессоры сегодня дублируются только индексы, а не фактические данные вершин. Это хорошая идея, чтобы использовать его здесь

Помните о порядке намотки при генерации буфера. Так как вы делаете это программно, это не сложно изменить, но может привести к нескольким забавным сбоям, если вы не получите правильный порядок. Хранение индексов текстуры в отдельном буфере, или то же самое Буфер, но не чередование, кажется заманчивым, потому что вы можете обновить их, не касаясь позиций, которые в конечном итоге никогда не изменятся. Это звучит заманчиво, но не без недостатков; чередующийся формат хорош именно потому, что он обеспечивает тесное использование данных, что означает, что выборка из буфера может быть очень, очень эффективной. Если вы разделите их, вы заставите gpu выполнять потоковую передачу из двух разных областей памяти, что может привести к снижению производительности рендеринга. Опять же, это, вероятно, не имеет значения в случае простой двумерной сетки, но об этом следует помнить.
...