- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试为基于 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 空间中连续。
下面是网格中两个相邻四边形顶点的设置方式:
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 。
在您使用 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/
为了让我的代码几乎完全用 Jquery 编写,我想用 Jquery 重写 AJAX 调用。 这是从网页到 Tomcat servlet 的调用。 我目前情况的类似代码: var http = new
我想使用 JNI 从 Java 调用 C 函数。在 C 函数中,我想创建一个 JVM 并调用一些 Java 对象。当我尝试创建 JVM 时,JNI_CreateJavaVM 返回 -1。 所以,我想知
环顾四周,我发现从 HTML 调用 Javascript 函数的最佳方法是将函数本身放在 HTML 中,而不是外部 Javascript 文件。所以我一直在网上四处寻找,找到了一些简短的教程,我可以根
我有这个组件: import {Component} from 'angular2/core'; import {UserServices} from '../services/UserService
我正在尝试用 C 实现一个简单的 OpenSSL 客户端/服务器模型,并且对 BIO_* 调用的使用感到好奇,与原始 SSL_* 调用相比,它允许一些不错的功能。 我对此比较陌生,所以我可能会完全错误
我正在处理有关异步调用的难题: 一个 JQuery 函数在用户点击时执行,然后调用一个 php 文件来检查用户输入是否与数据库中已有的信息重叠。如果是这样,则应提示用户确认是否要继续或取消,如果他单击
我有以下类(class)。 public Task { public static Task getInstance(String taskName) { return new
嘿,我正在构建一个小游戏,我正在通过制作一个数字 vector 来创建关卡,该数字 vector 通过枚举与 1-4 种颜色相关联。问题是循环(在 Simon::loadChallenge 中)我将颜
我有一个java spring boot api(数据接收器),客户端调用它来保存一些数据。一旦我完成了数据的持久化,我想进行另一个 api 调用(应该处理持久化的数据 - 数据聚合器),它应该自行异
首先,这涉及桌面应用程序而不是 ASP .Net 应用程序。 我已经为我的项目添加了一个 Web 引用,并构建了各种数据对象,例如 PayerInfo、Address 和 CreditCard。但问题
我如何告诉 FAKE 编译 .fs文件使用 fsc ? 解释如何传递参数的奖励积分,如 -a和 -target:dll . 编辑:我应该澄清一下,我正在尝试在没有 MSBuild/xbuild/.sl
我使用下划线模板配置了一个简单的主干模型和 View 。两个单独的 API 使用完全相同的配置。 API 1 按预期工作。 要重现该问题,请注释掉 API 1 的 URL,并取消注释 API 2 的
我不确定什么是更好的做法或更现实的做法。我希望从头开始创建目录系统,但不确定最佳方法是什么。 我想我在需要显示信息时使用对象,例如 info.php?id=100。有这样的代码用于显示 Game.cl
from datetime import timedelta class A: def __abs__(self): return -self class B1(A):
我在操作此生命游戏示例代码中的数组时遇到问题。 情况: “生命游戏”是约翰·康威发明的一种细胞自动化技术。它由一个细胞网格组成,这些细胞可以根据数学规则生存/死亡/繁殖。该网格中的活细胞和死细胞通过
如果我像这样调用 read() 来读取文件: unsigned char buf[512]; memset(buf, 0, sizeof(unsigned char) * 512); int fd;
我用 C 编写了一个简单的服务器,并希望调用它的功能与调用其他 C 守护程序的功能相同(例如使用 ./ftpd start 调用它并使用 ./ftpd stop 关闭该实例)。显然我遇到的问题是我不知
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
在 dos 中,当我粘贴此命令时它会起作用: "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" https://google.
我希望能够从 cmd 在我的 Windows 10 计算机上调用 python3。 我已重新安装 Python3.7 以确保选择“添加到路径”选项,但仍无法调用 python3 并使 CMD 启动 P
我是一名优秀的程序员,十分优秀!