运行时创建纹理图集的openGL实现?

7

我已经建立了一个简洁的框架,将SDL、openGL和box2D整合在一起,用于2D游戏。

现在它的工作原理是,我创建一个“GameObject”类的对象,指定一个“源PNG”,然后它会自动创建一个openGL纹理和一个相同尺寸的box2d物体。

现在我担心的是,如果我需要在屏幕上渲染许多不同的纹理。

是否有可能在运行时加载所有我的精灵表,并将它们全部组合成一个纹理?如果可以,如何实现?并且有什么好的方法来实现它(这样我就不必手动指定任何参数或其他内容)。

我想在运行时而不是预处理是因为我可以轻松地将某个级别的所有瓷砖、敌人等都加载到这一个纹理中,因为每个级别都不会有相同的敌人。这也会使整个艺术创作过程更容易。


多少是“很多”?我更担心重复使用现有的纹理 - 引擎是否处理这个? - Stefan Hanke
我认为最多一次会有100个不同的纹理,因此那将是100次绑定操作。另外,当您说重新使用现有纹理时,是否意味着如果我有一个被使用12次的特定图块,引擎应该在绑定一次的情况下将其绘制12次而不是重新绑定?那将是一个好主意...有没有办法检查当前绑定的纹理,以便我不会重新绑定它?或者我必须制作一个系统来将相同的纹理一起绘制,以便减少绑定...但是如果我想按深度排序以特定顺序绘制它们,该怎么办? - Omar Shehata
是的,没错——那样可以保存绑定。第二条评论更针对具有相同数据的纹理,忘了它吧。 - Stefan Hanke
1个回答

12

很可能已经有一些用于创建纹理图集(最优包装是一个非常棘手的问题)并将旧纹理坐标转换为新纹理坐标的库存在。

但是,如果您想自己完成,您可能会像这样做:

  • 从磁盘加载所有纹理(您的“源PNG”),并检索原始像素数据缓冲区,
  • 如果需要,将所有源纹理转换为相同的像素格式,
  • 创建一个足够大的新纹理来容纳所有现有纹理,以及相应的缓冲区来容纳像素数据
  • 在给定偏移量处将源图像的像素数据“复制”到新缓冲区中
  • 使用新缓冲区的数据像往常一样创建纹理。
  • 在执行此操作时,确定“旧”纹理坐标到“新”纹理坐标的映射(应该是记录纹理图集的每个元素的偏移量并进行快速变换的简单问题)。 这也可能很容易在像素着色器内完成,但需要进行一些分析以查看传递额外参数的开销是否值得。

显然,您还希望检查以确保不会做出像将同一纹理加载到图集两次这样的愚蠢行为,但这不是本过程的关注点。


要将源图像复制到目标图像中,您可以执行以下操作(假设您正在将128x128纹理复制到512x512的纹理集纹理中,并从目标的(128, 0)位置开始):
unsigned char* source = new unsigned char[ 128 * 128 * 4 ]; // in reality, comes from your texture loader
unsigned char* target = new unsigned char[ 512 * 512 * 4 ];

int targetX = 128;
int targetY = 0;

for(int sourceY = 0; sourceY < 128; ++sourceY) {
    for(int sourceX = 0; sourceX < 128; ++sourceX) {
        int from = (sourceY * 128 * 4) + (sourceX * 4); // 4 bytes per pixel (assuming RGBA)
        int to = ((targetY + sourceY) * 512 * 4) + ((targetX + sourceX) * 4); // same format as source

        for(int channel = 0; channel < 4; ++channel) {
            target[to + channel] = source[from + channel];
        }
    }
}

这是一个非常简单的暴力实现:有更快,更简洁,更聪明的方法来复制数组,但基本思路是将源纹理的内容复制到给定 X 和 Y 偏移量的目标纹理中。最终,您将创建一个包含旧纹理的新纹理。

如果索引数学对您来说不合理,请考虑如何在一维空间(如计算机内存)中实际索引2D数组

请原谅任何错误。这不是生产代码,而是我编写的未经过编译或运行检查的东西。


由于您正在使用SDL,我应该提到它有一个很好的函数可能能够帮助您: SDL_BlitSurface。您可以在SDL中创建一个完整的SDL_Surface,然后仅使用SDL_BlitSurface将源表面复制到其中,然后将图集表面转换为GL纹理
它会处理所有的数学计算,并且也可以在运行时为您执行格式转换。

谢谢你的回答,ravuya!我认为我知道如何做你所说的一切,除了:“在给定偏移量处将源图像的像素数据“Blit”到新缓冲区中”如果您能提供相关链接或指导我如何进行编码,我将不胜感激。再次感谢! - Omar Shehata
我添加了一个“blitting”的简要说明 - 它实际上是将一个数组复制到另一个数组中。 - ravuya
等等,基本上,在openGL中的纹理包含它们的像素数据(称为缓冲区?),我们要做的就是将每个单独纹理的像素数据复制,并将它们全部放入一个大的像素缓冲区中,然后将其应用于我的大型纹理,有效地将所有小型纹理复制到大型纹理中?我认为我可以处理复制的数学问题,只是不确定在openGL中需要使用哪些函数来访问像素数据。再次感谢您迄今为止提供的帮助,我非常感激! - Omar Shehata
1
你可以完全跳过中间的 blitting 步骤,通过使用 glTexSubImage2D,直接将较小的图像上传到目标纹理中它们应该在的位置。 - datenwolf
顺便说一下:单词“blit”字面上只是指“块[内存]传输”,也就是“blt”。 - Ancurio
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接