gpt4 book ai didi

java - 在 OpenGL 中重用纹理和顶点

转载 作者:行者123 更新时间:2023-11-29 04:07:40 25 4
gpt4 key购买 nike

我正在尝试制作一个简单的 2D 游戏,我将世界存储在 Block 的 2D 数组中(一个枚举,每个值都有其纹理)。

因为这些都是简单的不透明图 block ,所以在渲染时我按纹理对它们进行排序,然后通过转换为它们的坐标来渲染它们。但是,我还需要为我绘制的每个图 block 指定纹理坐标和顶点,即使这些也是相同的。

这是我目前拥有的:

public static void render() {
// Sorting...
for(SolidBlock block : xValues.keySet()) {
block.getTexture().bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

for(int coordinateIndex = 0; coordinateIndex < xValues.get(block).size(); coordinateIndex++) {
int x = xValues.get(block).get(coordinateIndex);
int y = yValues.get(block).get(coordinateIndex);
glTranslatef(x, y, Integer.MIN_VALUE);
// Here I use MIN_VALUE because I'll later have to do z sorting with other tiles
glBegin(GL_QUADS);
loadModel();
glEnd();
glLoadIdentity();
}

xValues.get(block).clear();
yValues.get(block).clear();
}
}

private static void loadModel() {
glTexCoord2f(0, 0);
glVertex2f(0, 0);
glTexCoord2f(1, 0);
glVertex2f(1, 0);
glTexCoord2f(1, 1);
glVertex2f(1, 1);
glTexCoord2f(0, 1);
glVertex2f(0, 1);
}

我想知道是否可以将 loadModel() 放在主循环之前,以避免使用相同的数据加载模型数千次,以及还有什么可以移动以使其尽可能快尽可能!

最佳答案

一些快速优化:

  • glTexParameteri每个纹理的每个参数只需要调用一次。您应该将其放在加载纹理的代码部分。
  • 你可以一次绘制多个四边形glBegin/glEnd简单地通过添加更多的顶点来配对。但是,您不能在 glBegin 之间进行任何坐标更改。和 glEnd (例如 glTranslatefglLoadIdentityglPushMatrix )所以你必须通过 xy给你的loadModel函数(为了准确起见,实际上应该称为 addQuad)。也不允许在 glBegin 之间重新绑定(bind)纹理。/glEnd ,所以你必须使用一组 glBegin/glEnd每个纹理。
  • 次要,但不是调用 xValues.get(block)很多次,就说List<Integer> blockXValues = xValues.get(block)在你的外部循环的开始,然后使用 blockXValues从那时起。

一些更复杂的优化:

  • 旧版 OpenGL 有绘图列表,它们基本上是 OpenGL 的宏。您可以让 OpenGL 记录您在 glNewList 之间进行的所有 OpenGL 调用。和 glEndList (有一些异常(exception)),并以某种方式存储它们。下次你想运行那些精确的 OpenGL 调用时,你可以使用 glCallList让 OpenGL 为您做到这一点。将对抽奖列表进行一些优化,以加快后续抽奖。
  • 纹理切换相对昂贵,自从您按纹理对四边形进行排序后您可能已经意识到这一点,但是有比排序纹理更好的解决方案:将所有纹理放入一个 texture atlas 中。 .您需要将每个 block 的子纹理坐标存储在 SolidBlock 中。 s,然后传递 blockaddQuad同样,您可以将适当的子纹理坐标传递给 glTexCoord2f .完成后,您不再需要按纹理排序,只需遍历 x 和 y 坐标即可。

良好做法:

  • 只使用glLoadIdentity每帧一次,在绘制过程开始时。然后使用 glPushMatrixglPopMatrix 配对保存和恢复矩阵的状态。这样,代码的内部部分就不需要知道外部部分事先可能完成或未完成的矩阵变换。
  • 不要使用 Integer.MIN_VALUE作为顶点坐标。使用您自己选择的常数,最好是不会使您的深度范围变大的常数(我假设您正在使用 glOrtho 的最后两个参数)。深度缓冲区精度有限,如果在将 Z 范围设置为 Integer.MIN_VALUE 后尝试使用 1 或 2 左右的 Z 坐标,则会遇到 Z 冲突问题。至 Integer.MAX_VALUE .此外,您正在使用 float坐标,所以 int无论如何,常量在这里没有意义。

这是快速通过后的代码(没有纹理图集更改):

private static final float BLOCK_Z_DEPTH = -1; // change to whatever works for you
private int blockCallList;
private boolean regenerateBlockCallList; // set to true whenever you need to update some blocks

public static void init() {
blockCallList = glGenLists(1);
regenerateBlockCallList = true;
}

public static void render() {
if (regenerateBlockCallList) {
glNewList(blockCallList, GL_COMPILE_AND_EXECUTE);
drawBlocks();
glEndList();

regenerateBlockCallList = false;
} else {
glCallList(blockCallList);
}
}

private static void drawBlocks() {
// Sorting...

glPushMatrix();
glTranslatef(0, 0, BLOCK_Z_DEPTH);

for (SolidBlock block : xValues.keySet()) {
List<Integer> blockXValues = xValues.get(block);
List<Integer> blockYValues = yValues.get(block);

block.getTexture().bind();
glBegin(GL_QUADS);
for(int coordinateIndex = 0; coordinateIndex < blockXValues.size(); coordinateIndex++) {
int x = blockXValues.get(coordinateIndex);
int y = blockYValues.get(coordinateIndex);
addQuad(x,y);
}
glEnd();

blockXValues.clear();
blockYValues.clear();
}

glPopMatrix();

}

private static void addQuad(float x, float y) {
glTexCoord2f(0, 0);
glVertex2f(x, y);
glTexCoord2f(1, 0);
glVertex2f(x+1, y);
glTexCoord2f(1, 1);
glVertex2f(x+1, y+1);
glTexCoord2f(0, 1);
glVertex2f(x, y+1);
}

使用现代 OpenGL(顶点缓冲区、着色器和实例化,而不是显示列表、矩阵变换和逐个传递顶点),您会以非常不同的方式解决这个问题,但我会把它放在我的回答范围之外。

关于java - 在 OpenGL 中重用纹理和顶点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57373361/

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