【问题标题】:Tiled backgrounds on iPhone using OpenGL ES使用 OpenGL ES 在 iPhone 上平铺背景
【发布时间】:2009-08-12 21:13:09
【问题描述】:

我想在 iPhone 上制作一个 2D 平铺背景系统。获取瓦片地图和瓦片集图像并将其转换为屏幕上的完整地图的东西。

只是做一些乱七八糟的事情,我的第一个方法是为每个图块创建一个多边形。这工作得很好,直到我开始测试 400 个左右的多边形,然后它开始运行非常缓慢。我只是想知道 - 这种几个多边形的方法不是要走的路吗?还是我做错了什么?如果需要,我稍后会发布代码,但我的主要问题是“400 个小多边形会在 iPhone 上运行缓慢还是我只是做错了什么?”

我还考虑了另一种方法,即在初始化期间,通过 tilemap/tilesets 中的代码创建地图纹理,然后将其粘贴在一个大多边形上。所以是的......关于我应该如何处理这样的事情的任何反馈?

我知道有人会提到这一点 - 我考虑过尝试 cocos2d,但我有理由不走那条路。

【问题讨论】:

  • 需要更多信息来查明问题。每个不同的瓦片图像是否仅作为纹理加载并传递给 Open GL 一次,这些相似的瓦片共享相同的纹理资源?这些平铺纹理图像有多大?你有多少种不同的瓷砖纹理?您是预加载纹理还是动态加载它们?
  • 抱歉,不清楚。我将具体说明我测试的速度很慢:我在初始化时将一个大 (2048x2048) 瓦片集图像作为 PVRTexture 类(由 Apple 提供)加载,而不是在运行中加载。同样在初始化期间,我在一个数组中创建所有平铺顶点。然后在循环期间,我绑定纹理并在大顶点数组上调用 glDrawArrays。

标签: iphone opengl-es


【解决方案1】:

您的问题几乎可以肯定是您绑定了 400 次纹理,而不是其他任何东西。您应该将所有图块放在一个大的纹理图集/精灵表中,而不是重新绑定您的纹理,您应该只绑定一次图集,然后绘制它的一小部分。如果这样做,您应该能够绘制数千个图块而不会真正减速。

你可以像这样画你的精灵:

//Push the matrix so we can keep it as it was previously.
glPushMatrix();

//Store the coordinates/dimensions from a rectangle.
float x = CGRectGetMinX(rect);
float y = CGRectGetMinY(rect);
float w = CGRectGetWidth(rect);
float h = CGRectGetHeight(rect);

float xOffset = x;
float yOffset = y;

if (rotation != 0.0f)
{
    //Translate the OpenGL context to the center of the sprite for rotation.
    glTranslatef(x+w/2, y+h/2, 0.0f);

    //Apply the rotation over the Z axis.
    glRotatef(rotation, 0.0f, 0.0f, 1.0f);

    //Have an offset for the top left corner.
    xOffset = -w/2;
    yOffset = -h/2;
}

// Set up an array of values to use as the sprite vertices.
GLfloat vertices[] =
{
    xOffset, yOffset,
    xOffset, yOffset+h,
    xOffset+w, yOffset+h,
    xOffset+w, yOffset,
};

// Set up an array of values for the texture coordinates.
GLfloat texcoords[] =
{
    CGRectGetMinX(clippingRect),    CGRectGetMinY(clippingRect),
    CGRectGetMinX(clippingRect),    CGRectGetHeight(clippingRect),
    CGRectGetWidth(clippingRect),   CGRectGetHeight(clippingRect),
    CGRectGetWidth(clippingRect),   CGRectGetMinY(clippingRect),
};

//If the image is flipped, flip the texture coordinates.
if (flipped)
{
    texcoords[0] = CGRectGetWidth(clippingRect);
    texcoords[2] = CGRectGetWidth(clippingRect);
    texcoords[4] = CGRectGetMinX(clippingRect);
    texcoords[6] = CGRectGetMinX(clippingRect);
}

//Render the vertices by pointing to the arrays.
glVertexPointer(2, GL_FLOAT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, texcoords);

// Set the texture parameters to use a linear filter when minifying.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

//Allow transparency and blending.
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

//Enable 2D textures.
glEnable(GL_TEXTURE_2D);

//Bind this texture.
if ([Globals getLastTextureBound] != texture)
{
    glBindTexture(GL_TEXTURE_2D, texture);
}

//Finally draw the arrays.
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

//Restore the model view matrix to prevent contamination.
glPopMatrix();

我使用的两个 CGRect 只是为了方便。您可以指定绘制图像的 X、Y、宽度和高度,并且可以使用 clippingRect 指定要在图像中绘制的位置。使用裁剪矩形, (0, 0, 1, 1) 是整个图像,而 (0, 0, 0.25, 0.25) 只会绘制左上角。通过更改裁剪矩形,您可以将各种不同的瓦片放在同一个纹理中,然后您只需绑定一次。便宜很多。

【讨论】:

    【解决方案2】:

    Scott,每个纹理只需要设置一次 TexParameter 设置。但是,这不是您减速的原因。

    建立索引列表并为整个图块集调用一次 glDrawArrays 会好得多。顶点数组的目标是让你一步画出尽可能多的东西。

    应该避免使用glDrawTex,因为它会迫使您进入非常低效的一次一次的心态。

    【讨论】:

    • 谢谢,我知道必须有一种方法可以在一次 glDrawArrays 调用中完成所有工作。经过一番研究,我想我只需要使用 GL_QUADS 它就会知道创建单独的多边形。我不在我的 Mac 上,但我稍后会对此进行测试。
    • GL_QUADS 在 ES 中不受支持。你应该使用一对三角形。对于这个用例,我不会费心尝试 FAN 或 STRIP。
    • 你说得对,我最后写了几个三角形来模拟“四边形”。非常感谢。我现在开始了,看起来还不错,减速已经基本消失了。
    【解决方案3】:

    也可以使用glDrawTex 扩展名。

    【讨论】:

      【解决方案4】:

      斯坦福 iTune 大学有一个关于为 iPhone 优化 OpenGL 的播客。

      但基本的想法是:

      1. 批量几何,将各种顶点数组组合成一个大顶点数组。这应该将 x 次 glPointer 调用减少为单个 glPointer 调用。

      2. 纹理图集,对所有不同的瓦片使用单一纹理,不同之处在于每个瓦片使用的区域。只需为所有平铺绘图绑定一次纹理即可。

      3. 交错数组,将点的各个部分(例如顶点、纹理坐标、颜色)组合成一个数组。这应该将 gl*Pointer 调用减少到单个调用。

      4. 索引三角形,允许您重复使用几何信息

      5. 尽可能使用 Short 而不是 Float 来获取几何信息,因为它更小。

      这只是一般的 opengl 优化指南。至于瓦片引擎,嗯..

      1. 在将数据发送到 opengl 之前进行自己的剔除。你不画的东西,你保存。

      我想这就是我目前能想到的。

      【讨论】:

        【解决方案5】:

        我做了什么来加速我的应用程序。在我加载我的关卡之后。我用地图上的图块创建了一个地图集。然后我检查每一帧,看看相机是否移动了。如果是这样,那么我只需传递一个 glTranslatef 并立即移动整个地图。如果只有动态对象在地图上移动,那么我只需在顶点数组图集中更新该对象。这个系统非常高效,因为我能够绘制大量瓷砖而不会出现帧率下降。

        【讨论】:

          【解决方案6】:

          客户端状态应该只在初始化时启用,glTexParameteri 函数也应该在创建纹理对象时调用。 所有 glEnable 函数都不会被缓存,这意味着即使它已经设置为该值,它也会设置状态。 所有这些小事都会累加起来,让你慢下来。

          BR

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-01-08
            • 1970-01-01
            • 1970-01-01
            • 2011-01-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多