Как уменьшить количество вызовов отрисовки до большого количества текстур? - PullRequest
0 голосов
/ 10 января 2019

Я пытаюсь разработать карту для игры на основе 2D-плиток, я использую подход, чтобы сохранить изображения карты в большой текстуре (набор тайлов) и рисовать только нужные плитки на экране, обновляя позиции через вершинный шейдер, однако на карте 10x10 задействовано 100 вызовов glDrawArrays, просматривая диспетчер задач, это потребляет 5% использования ЦП и 4 ~ 5% ГП, представьте, что это была полная игра с десятками вызовов, есть способ оптимизировать это, например, подготовить всю сцену и просто сделать 1 вызов на рисование, нарисовать все сразу или какой-то другой подход?

void GameMap::draw() {
  m_shader - > use();
  m_texture - > bind();

  glBindVertexArray(m_quadVAO);

  for (size_t r = 0; r < 10; r++) {
    for (size_t c = 0; c < 10; c++) {
      m_tileCoord - > setX(c * m_tileHeight);
      m_tileCoord - > setY(r * m_tileHeight);
      m_tileCoord - > convert2DToIso();

      drawTile(0);
    }
  }

  glBindVertexArray(0);
}

void GameMap::drawTile(GLint index) {
  glm::mat4 position_coord = glm::mat4(1.0 f);
  glm::mat4 texture_coord = glm::mat4(1.0 f);

  m_srcX = index * m_tileWidth;

  GLfloat clipX = m_srcX / m_texture - > m_width;
  GLfloat clipY = m_srcY / m_texture - > m_height;

  texture_coord = glm::translate(texture_coord, glm::vec3(glm::vec2(clipX, clipY), 0.0 f));
  position_coord = glm::translate(position_coord, glm::vec3(glm::vec2(m_tileCoord - > getX(), m_tileCoord - > getY()), 0.0 f));
  position_coord = glm::scale(position_coord, glm::vec3(glm::vec2(m_tileWidth, m_tileHeight), 1.0 f));

  m_shader - > setMatrix4("texture_coord", texture_coord);
  m_shader - > setMatrix4("position_coord", position_coord);

  glDrawArrays(GL_TRIANGLES, 0, 6);
}

--Vertex Shader

#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>

out vec4 TexCoords;

uniform mat4 texture_coord;
uniform mat4 position_coord;
uniform mat4 projection;

void main()
{
    TexCoords =    texture_coord * vec4(vertex.z, vertex.w, 1.0, 1.0);
    gl_Position =   projection * position_coord * vec4(vertex.xy, 0.0, 1.0);
}

-- Fragment Shader
#version 330 core
out vec4 FragColor;

in vec4 TexCoords;

uniform sampler2D image;
uniform vec4 spriteColor;

void main()
{
    FragColor = vec4(spriteColor) * texture(image, vec2(TexCoords.x, TexCoords.y));
}

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Основная техника

Первое, что вы хотите сделать, это настроить буфер вершин сетки 10x10. Каждый квадрат в сетке на самом деле два треугольника. И все треугольники будут нуждаться в своих вершинах, потому что координаты UV для соседних плиток не одинаковы, даже если координаты XY одинаковы. Таким образом, каждый треугольник может скопировать область из текстурного атласа, в которой он нуждается, и он не должен быть смежным в ультрафиолетовом пространстве.

Вот как будут установлены вершины двух соседних квадов в сетке:

enter image description here

1:  xy=(0,0) uv=(Left0 ,Top0)
2:  xy=(1,0) uv=(Right0,Top0)
3:  xy=(1,1) uv=(Right0,Bottom0)
4:  xy=(1,1) uv=(Right0,Bottom0)
5:  xy=(0,1) uv=(Left0 ,Bottom0)
6:  xy=(0,0) uv=(Left0 ,Top0)
7:  xy=(1,0) uv=(Left1 ,Top1)
8:  xy=(2,0) uv=(Right1,Top1)
9:  xy=(2,1) uv=(Right1,Bottom1)
10: xy=(2,1) uv=(Right1,Bottom1)
11: xy=(1,1) uv=(Left1 ,Bottom1)
12: xy=(1,0) uv=(Left1 ,Top1)

Эти 12 вершин определяют 4 треугольника. Координаты верхнего, левого, нижнего и правого УФ-лучей для первого квадрата могут полностью отличаться от координат второго квадрата, что позволяет текстурировать каждый квадрат с помощью отдельной области текстурного атласа. Например. см. ниже, чтобы увидеть, как координаты UV для каждого треугольника отображаются на плитку в текстурном атласе.

enter image description here

В вашем случае с сеткой 10x10 у вас будет 100 квадов или 200 треугольников. С 200 треугольниками по 3 вершины в каждом, это будет 600 вершин для определения. Но это одиночный вызов из 200 треугольников (600 вершин). Каждая вершина имеет свои координаты x, y, u, v. Чтобы изменить, какой тайл является квадом, вам нужно обновить координаты uv 6 вершин в буфере вершин.

Вероятно, вы обнаружите, что это наиболее удобный и эффективный подход.

Продвинутые подходы

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

Но в многопотоковом подходе вы можете указать все вершины xy отдельно от всех вершин uv, ​​чтобы избежать дублирования. Вы также можете указать второй набор координат текстуры, который был только верхним левым углом плитки в атласе, и позволить ультрафиолетовым координатам просто перейти от 0,0 (верхний левый) к 1,1 (нижний правый) для каждого квадратора. , затем позвольте вашему шейдеру масштабировать и трансформировать координаты ультрафиолета, чтобы получить окончательные координаты текстуры. Вы также можете указать одну ультрафиолетовую координату верхнего левого угла исходной области для каждого примитива и позволить геометрическому шейдеру заполнить квадраты. И, что еще умнее, вы можете указать только координаты x, y (полностью исключая координаты uv), а в своем вершинном шейдере вы можете выбрать текстуру, которая содержит «числа плиток» каждого квадратора. Вы могли бы сэмплировать эту текстуру в координатах на основе значений x, y в сетке, а затем на основе прочитанного значения вы можете преобразовать ее в координаты uv в атласе. Чтобы изменить плитку в этой системе, вы просто меняете один пиксель в текстуре карты плитки. И, наконец, вы можете пропустить генерацию примитивов полностью и получить их полностью из одного списка, отправленного в геометрический шейдер, и сгенерировать координаты x, y сетки, которая отправляется вниз по потоку в вершинный шейдер, чтобы завершить геометрию треугольника и координаты uv сетка, это наиболее эффективно использует память, но использует GPU для вычисления установки во время выполнения.

Со статической настройкой 6 вершин на треугольник вы освобождаете обработку графического процессора за счет небольшой дополнительной памяти. В зависимости от того, что вам нужно для производительности, вы можете обнаружить, что использование большего объема памяти для получения более высоких fps желательно. Буферы вершин крошечные по сравнению с текстурами.

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

0 голосов
/ 10 января 2019

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

Прошло 3 года с тех пор, как я использовал OpenGL, поэтому я могу только указать вам правильное направление. Начните читать некоторые материалы, например, например:

https://ferransole.wordpress.com/2014/07/09/multidrawindirect/

https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawArraysIndirect.xhtml

Кроме того, имейте в виду, что это GL 4.x, проверьте вашу целевую платформу (программное + аппаратное обеспечение), поддержку версии GL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...