【问题标题】:OpenGL Tiles/Blitting/ClippingOpenGL平铺/Blitting/Clipping
【发布时间】:2011-10-28 18:35:37
【问题描述】:

在 OpenGL 中,如何从使用 IMG_Load() 加载的图像文件中选择一个区域? (我正在为简单 2D 游戏制作瓷砖地图)

我使用以下原理将图像文件加载到纹理中:

GLuint loadTexture( const std::string &fileName ) {

    SDL_Surface *image = IMG_Load(fileName.c_str());

    unsigned object(0);                        
    glGenTextures(1, &object);                 
    glBindTexture(GL_TEXTURE_2D, object);   

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image->w, image->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);

    SDL_FreeSurface(image);
    return object;
}

然后我使用以下内容在我的渲染部分中实际绘制纹理:

glColor4ub(255,255,255,255);                
glBindTexture(GL_TEXTURE_2D, texture);      
glBegin(GL_QUADS);                          
glTexCoord2d(0,0);  glVertex2f(x,y);
glTexCoord2d(1,0);  glVertex2f(x+w,y);
glTexCoord2d(1,1);  glVertex2f(x+w,y+h);
glTexCoord2d(0,1);  glVertex2f(x,y+h);
glEnd(); 

现在我需要一个函数,它允许我从调用loadTexture( const std::string &fileName ) 得到的GLuint 中选择某些矩形部分,这样我就可以使用上面的代码将这些部分绑定到矩形然后将它们绘制到屏幕上。类似的东西:

GLuint getTileTexture( GLuint spritesheet, int x, int y, int w, int h )

【问题讨论】:

  • 谢谢。在我的案例中,我需要将什么作为最后一个论点? glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, const GLvoid * data); 我不再拥有 SDL_Surface,我只有通过调用 loadTexture() 获得的 GLuint。你能写一个简短的例子吗?

标签: c++ opengl sdl tiles clipping


【解决方案1】:

继续将整个拼贴加载到纹理中。然后在渲染几何图形时使用 glTexCoord 选择其中的一个子集。

glTexSubImage2D 不会有任何帮助。它允许您将多个文件添加到单个纹理,而不是从单个文件创建多个纹理。

示例代码:

void RenderSprite( GLuint spritesheet, unsigned spritex, unsigned spritey, unsigned texturew, unsigned textureh, int x, int y, int w, int h )
{
    glColor4ub(255,255,255,255);                
    glBindTexture(GL_TEXTURE_2D, spritesheet);
    glBegin(GL_QUADS);                          
    glTexCoord2d(spritex/(double)texturew,spritey/(double)textureh);
    glVertex2f(x,y);
    glTexCoord2d((spritex+w)/(double)texturew,spritey/(double)textureh);
    glVertex2f(x+w,y);
    glTexCoord2d((spritex+w)/(double)texturew,(spritey+h)/(double)textureh);
    glVertex2f(x+w,y+h);
    glTexCoord2d(spritex/(double)texturew,(spritey+h)/(double)textureh);
    glVertex2f(x,y+h);
    glEnd(); 
}

【讨论】:

  • 非常感谢!这行得通:) 不过只有一个问题,因为unsigned texturew, unsigned textureh 指的是精灵表的尺寸,是否有可能从GLuint spritesheet 本身获得这些?这样会舒服很多吗?
  • @Ben:您可以拨打glGetTexLevelParameter 并通过GL_TEXTURE_WIDTHGL_TEXTURE_HEIGHT。但仅在 CPU 上跟踪这些信息比每次都从 GPU 取回它们要高效得多。您可能想要创建一个结合纹理 ID (GLuint) 和宽度和高度的小型结构,以便将它们一起传递。
【解决方案2】:

虽然 Ben Voigt 的回答是常用的方法,但如果您真的想要瓷砖的额外纹理(这可能有助于在边缘进行过滤),您可以使用 glGetTexImage 并使用 glPixelStore 参数玩一点:

GLuint getTileTexture(GLuint spritesheet, int x, int y, int w, int h)
{
    glBindTexture(GL_TEXTURE_2D, spritesheet);

    // first we fetch the complete texture
    GLint width, height;
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
    GLubyte *data = new GLubyte[width*height*4];
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

    // now we take only a sub-rectangle from this data
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, /*filter+wrapping*/, /*whatever*/);
    glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
    glTexImage2D(GL_TEXTURE_2D, 0, RGBA, w, h, 0, 
        GL_RGBA, GL_UNSIGNED_BYTE, data+4*(y*width+x));

    // clean up
    glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
    delete[] data;
    return texture;
}

但请记住,此函数总是将整个纹理图集读取到 CPU 内存中,然后将子部分复制到新的较小纹理中。因此,一次创建所有需要的精灵纹理并且只读取一次数据是一个好主意。在这种情况下,您也可以完全删除 atlas 纹理,仅使用IMG_Load 将图像读入系统内存,以将其分配到各个精灵纹理中。或者,如果你真的需要大纹理,那么至少使用 PBO 将其数据复制到(使用 GL_DYNAMIC_COPY 或类似的东西),这样它就不需要离开 GPU 内存。

【讨论】:

  • 好吧,老实说,我不太确定我是否“真的想要瓷砖的额外纹理”。到目前为止,我已经将每个纹理都放在了一个单独的图像文件中。我现在所做的是将多个纹理组合到一个图像文件(spritesheet)中,例如每个字符或级别一张纸。我希望返回 GLuint 的唯一原因是因为我的逻辑是这样构造的(来自分离图像版本)。但我现在已经改变了。那么您会推荐我使用哪个版本? Ben 的版本与你的相比表现如何,有什么需要注意的吗?
  • @Ben 我认为 Ben 的版本更先进(你可能想要接受的版本,尽管你仍然可以投票给我的 :))。这样您就不必一直切换纹理。如果纹理变得太大,您可能会遇到问题(尽管最高 4096x4096 应该没问题,或者在较新的硬件上更多)或者您可能会在子纹理的边缘出现伪影,尤其是在使用线性过滤时(更不用说mip-mapping),因为它们不再被识别为纹理边缘,因为它们在图像内部。但这些问题是可以解决的(并且在 SO 上已经多次解决)。
  • 谢谢。我已经对你的版本投了赞成票;)我正在使用 Ben 的版本,到目前为止它对我来说效果很好。您是否知道我是否可以直接从GLuint spritesheet 获取图像尺寸以用于RenderSprite() 函数内部?你的函数中的前四行是这样做的吗?
  • @Ben 你可以,我已经在我的代码中做到了这一点(glGetTexLevelParameteriv 调用),但我不建议每次渲染精灵时都这样做,因为它会回调司机。查询一次(或者只是从加载中记住它)是更好的解决方案。如果你不想,你不需要把它放在函数参数中,你可以只创建一个类来存储所有信息,或者更好的是,为你的精灵使用顶点数组/缓冲区,这样你只需要计算一次 texCoords (以及许多其他优点)。但我现在不会详细说明 VBO。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 2012-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多