glDrawElemets медленный
Пишу игру на подобии minecraft (воксельный движок) на основе OpenGL и SDL2.
Столкнулся с такой проблемой: Рендеринг чанков очень медленный (пробуя разные методы отрисовки, получал от 9% до 50% в диспечере задач за ОДИН чанк). Вот некоторые куски кода:
Макросы, указывающие параметры чанков (длину по осям x,y,z):
#define CMD_CHUNK_COUNT_X 16
#define CMD_CHUNK_COUNT_Y 256
#define CMD_CHUNK_COUNT_Z 16
#define CMD_CHUNK_COUNT_ALL CMD_CHUNK_COUNT_X*CMD_CHUNK_COUNT_Y*CMD_CHUNK_COUNT_Z
Заполнение буфера вершин в структуре данных CMD_Mesh:
void CMD_RegenerateChunkMesh(CMD_Mesh* dst, CMD_Chunk* chunk)
{
CMD_ChunckVertex* vertecies = (CMD_ChunckVertex*)malloc(
CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex)); // max size
memset(vertecies, 0, CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex));
for(uint32_t x = 0; x < CMD_CHUNK_COUNT_X; x++)
for(uint32_t y = 0; y < CMD_CHUNK_COUNT_Y; y++)
for(uint32_t z = 0; z < CMD_CHUNK_COUNT_Z; z++)
{
uint32_t offset = (x*CMD_CHUNK_COUNT_Y*CMD_CHUNK_COUNT_Z + y*CMD_CHUNK_COUNT_Z +z)*24;
vec3 currentPos = {x,y,z};
vec3 opx = {1,0,0};
vec3 onx = {-1,0,0};
vec3 opy = {0,1,0};
vec3 ony = {0,-1,0};
vec3 opz = {0,0,1};
vec3 onz = {0,0,-1};
vec3* direcions[6] = {opx, onx, opy, ony, opz, onz};
for(uint32_t i = 0; i < 6; i++){
if(CMD_DrawSide(chunk, currentPos, direcions[i]))
{
for(uint32_t j = 0; j < 4; j++){
vertecies[offset+j+i*4] = CMD_MapChunkVertexData(
CMD_VOXEL_VERTECIES[j*4+0+i*4*4] +x,
CMD_VOXEL_VERTECIES[j*4+1+i*4*4] +y,
CMD_VOXEL_VERTECIES[j*4+2+i*4*4] +z,
CMD_VOXEL_VERTECIES[j*4+3+i*4*4],
2
);
}
}
}
}
dst->ibo = CMD_ChunkIBO;
GPI_BindBuffer(&dst->vbo);
glBufferSubData(dst->vbo.TYPE, 0, CMD_CHUNK_COUNT_ALL * 24 * sizeof(CMD_ChunckVertex), vertecies);
free(vertecies);
}
CMD_Mesh хранит в себе VBO, IBO (ссылку на него ибо IBO глобальное для всех чанков), VAO. Функция CMD_MapChunkVertexData сжимает данные, дабы одна вершина весила всего 8 байт (2 uint32_t) Сама процедура добавляет в псевдо 3D массив вершины, с offset-ом.
И собственно сам виновник торжества - отрисовка:
void CMD_RenderChunkMesh(CMD_Mesh* mesh, vec3 pos)
{
GPI_BindVertexArray(&mesh->vao);
GPI_BindVertexArrayAttribs(&mesh->vao);
GPI_BindBuffer(&mesh->vbo);
GPI_BindBuffer(&mesh->ibo);
GPI_BindShader(&CMD_ChunkShader);
int32_t loc = GPI_GetUniformLocation(&CMD_ChunkShader, "u_ChunkPosition");
if(loc != -1)
glUniform3f(loc, pos[0], pos[1], pos[2]);
glDrawElements(
GL_TRIANGLES,
CMD_CHUNK_COUNT_ALL * 36,
GL_UNSIGNED_INT,
NULL);
}
Рендерю я с интегрированной видеокарты Intel HD Graphics 630, но пробовал и со своей основной GTX 1050 Ti, но на моё огромное удивление, особой разницы не нашел.
Есть ли более эффективный метод отрисовки?
Ответы (1 шт):
Решению - быть! Всё дело в том, что вершины из VBO раскиданы под неким смещением а не в куче.. Было это сделано намеренно ибо так проще убирать блоки. Но проблема в том, что glDrawElements рисует ВЕСЬ vbo, что невероятно бьет по оптимизации (т.е количество рисуемых индексов = CMD_CHUNK_COUNT_ALL * 36).
Что же нужно делать? Использовать массив как массив и держать каждую вершину подряд.
НО есть у такого подхода проблема: игры на подобии minecraft дают игроку возможность редактировать мир, поэтому необходимо хранить массив вершин чанка в оперативной памяти и в нужный момент пушить данные с оперативной памяти в видеопамять (да, это дополнительный расход памяти). Но все не так плохо как кажется, ибо одна вершина занимает всего 8 байт.