gpt4 book ai didi

c++ - 如何减少对大量纹理的绘图调用次数?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:22:50 26 4
gpt4 key购买 nike

我正在尝试为基于 2D 图 block 的游戏开发 map ,我使用的方法是将 map 图像保存在大纹理(图 block 集)中,并通过更新位置仅在屏幕上绘制所需的图 block 通过顶点着色器,但是在10x10的 map 上涉及100次glDrawArrays调用,通过任务管理器查看,这消耗了5%的CPU使用率和4 ~ 5%的GPU,想象一下如果它是一个完整的游戏有几十次调用,有一个优化此方法的方法,例如准备整个场景并仅进行 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));
}

最佳答案

基本技术

您要做的第一件事是设置 10x10 网格顶点缓冲区。网格中的每个正方形实际上是两个三角形。所有三角形都需要自己的顶点,因为相邻图 block 的 UV 坐标不相同,即使 XY 坐标相同。这样每个三角形都可以从它需要的纹理图集中复制区域,并且它不需要在 UV 空间中连续。

下面是网格中两个相邻四边形顶点的设置方式:

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 坐标可以与第二个正方形的坐标完全不同,从而允许每个正方形由纹理图集的不同区域进行纹理化。例如。请参阅下文,了解每个三角形的 UV 坐标如何映射到纹理图集中的图 block 。

enter image description here

在您使用 10x10 网格的情况下,您将有 100 个四边形或 200 个三角形。如果有 200 个三角形,每个三角形有 3 个顶点,则需要定义 600 个顶点。但它是 200 个三角形(600 个顶点)的单个绘制调用。每个顶点都有自己的 x, y, u, v, 坐标。要更改四边形的瓦片,您必须更新顶点缓冲区中 6 个顶点的 uv 坐标。

您可能会发现这是最方便、最有效的方法。

高级方法

如果您愿意牺牲计算时间来换取内存或便利,则有更多内存高效或方便的方法可以使用多个流来设置它以减少顶点重复并利用着色器来完成设置工作。找到适合您的平衡点。 但在尝试优化之前,您应该先掌握基本技术。

但在多流方法中,您可以将所有 xy 顶点与所有 uv 顶点分开指定,以避免重复。您还可以指定第二组纹理坐标,它只是图集中图 block 的左上角,让每个四边形的 uv 坐标从 0,0(左上)到 1,1(右下) ,然后让您的着色器缩放和转换 uv 坐标以达到最终纹理坐标。您还可以为每个基元指定源区域左上角的单个 uv 坐标,并让几何着色器完成正方形。更聪明的是,您可以仅指定 x、y 坐标(完全忽略 uv 坐标),并且在您的顶点着色器中,您可以对包含每个四边形的“图 block 编号”的纹理进行采样。您将根据网格中的 x、y 值在坐标处对该纹理进行采样,然后根据您读取的值,您可以将其转换为图集中的 uv 坐标。要更改此系统中的图 block ,只需更改图 block 贴图纹理中的一个像素即可。最后,您可以完全跳过生成图元,完全从发送到几何着色器的单个列表派生它们,并生成网格的 x、y 坐标,该坐标被发送到下游的顶点着色器以完成三角形几何和 uv 坐标网格,这是内存效率最高的,但依赖于 GPU 在运行时计算设置。

使用静态的每个三角形 6 个顶点的设置,您可以释放 GPU 处理能力,但会占用一些额外的内存。根据您对性能的需求,您可能会发现使用更多内存来获得更高的 fps 是可取的。无论如何,与纹理相比,顶点缓冲区很小。

所以正如我所说,您应该首先从基本技术开始,因为它也可能是性能的最佳解决方案,尤其是当您的 map 不经常更改时。

关于c++ - 如何减少对大量纹理的绘图调用次数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54128181/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com