【问题标题】:OpenGL Blending with textures converted from SDL_SurfaceOpenGL 与从 SDL_Surface 转换的纹理混合
【发布时间】:2018-03-09 23:28:55
【问题描述】:

我想尝试使用 OpenGL 和 GLUT 制作游戏,但事实证明,GLUT 并不适合制作游戏。所以我转而使用 SDL 1.2(这是为了某种竞争,所以我不能使用 SDL 2)。当我看到我可以在 SDL 中使用 OpenGL 时,我决定这样做,因为我已经用 OpenGL 编写了大部分代码。现在,我在尝试将图像加载到SDL_Surface 中,然后在启用 OpenGL 混合的情况下将其转换为 OpenGL 纹理时遇到问题。这是我正在使用的代码(loadImage 加载 SDL_SurfaceloadTexture 加载到 OpenGL 纹理中):

SDL_Surface * Graphics::loadImage(const char * filename) {
    SDL_Surface *loaded = nullptr;
    SDL_Surface *optimized = nullptr;

    loaded = IMG_Load(filename);

    if (loaded) {
        optimized = SDL_DisplayFormat(loaded);
        SDL_FreeSurface(loaded);
    }

    return optimized;
}

GLuint Graphics::loadTexture(const char * filename, GLuint oldTexId) {
    //return SOIL_load_OGL_texture(filename, SOIL_LOAD_AUTO, oldTexId, SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_MULTIPLY_ALPHA);
    GLuint texId = 0;
    SDL_Surface *s = loadImage(filename);
    if (!s) return 0;

    if (oldTexId) glDeleteTextures(1, &oldTexId);

    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);

    int format;
    if (s->format->BytesPerPixel == 4) {
        if (s->format->Rmask == 0x000000ff)
            format = GL_RGBA;
        else
            format = GL_BGRA;
    } else if (s->format->BytesPerPixel == 3) {
        if (s->format->Rmask == 0x000000ff)
            format = GL_RGB;
        else
            format = GL_BGR;
    }

    glTexImage2D(GL_TEXTURE_2D, 0, s->format->BytesPerPixel, s->w, s->h, 0, format, GL_UNSIGNED_BYTE, s->pixels);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    SDL_FreeSurface(s);

    return texId;
}

我一直在网上搜索这个问题的解决方案,但我找到的解决方案都没有奏效。当我不glEnable(GL_BLEND) 时,此代码实际上有效,但是当我启用它时,它不再在屏幕上显示任何内容。我对 OpenGL 还很陌生,我不确定我是否正确使用了 glTexImage2D

我在转换为 SDL 之前加载图像的方式是使用 SOIL 库,当我用注释掉的第一行替换 loadTexture 函数的主体时,它实际上工作正常,但我宁愿少用外部库,并使用 SDL 和 OpenGL 完成所有图形方面的工作。

【问题讨论】:

  • 如果您只是将位加载到 OpenGL 纹理中,为什么还要使用 SDL_DisplayFormat()?您只需要优化表面以加速 SDL 的 blitting 系统,它与 OpenGL 无关。
  • @genpfault 当我不SDL_DisplayFormat() 表面时,format->BytesPerPixel 是 1。我可能会修改我的函数以最终读取它,但现在不是,或者至少不应该't,成为一个问题

标签: c++ opengl sdl


【解决方案1】:

glTexImage2D的第三个参数错误:

glTexImage2D(GL_TEXTURE_2D, 0, s->format->BytesPerPixel, s->w, s->h, 0, format, GL_UNSIGNED_BYTE, s->pixels);

第三个参数是 internalFormat 并且必须是基本内部格式之一:

GL_DEPTH_COMPONENT
GL_DEPTH_STENCIL
GL_RED
GL_RG
GL_RGB
GL_RGBA

或其中一种大小的内部格式,它指定每个通道的位数。

所以换句话说,你的第三个论点应该是:

GL_RGB 
GL_RGB8 
GL_RGBA 
GL_RGBA8 

如果您使用的是每通道 8 位纹理。

第 7 个参数 format 可以是 RGB 或 BGR(包括 alpha 版本),而第三个参数 internalFormat 只能是 RGB,反之则不行。

因此,您检查红色蒙版并更改格式仍然适用于第 7 个参数,第三个参数(internalFormat)应该是 GL_RGB 或 GL_RGBA。或者可选的尺寸版本 GL_RGB8 或 GL_RGBA8。

glTexImage2D(GL_TEXTURE_2D, 0, /*GL_RGB or GL_RGBA*/, s->w, s->h, 0, format, GL_UNSIGNED_BYTE, s->pixels);

Docs

【讨论】:

  • 这并没有解决问题。它仍然没有出现,但只有在启用混合时才会出现
  • glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 这就是我启用混合的方式,但它不应该改变任何东西,因为它确实适用于加载了 SOIL 的图像
  • @Arthur Bouvier 我对你所说的感到困惑。片段着色器输出的最终分量 (w) 或 (a) 将决定混合。将其设为 1 并查看是否抽签。然后,如果您说的内容适用于 SOIL 但不适用于 SDL,请确保您没有得到错误的 alpha 蒙版。例如,在您的片段着色器中,采样器将返回一个 vec4。尝试将 r、g、b、a 分量移动到写出的 vec4 的最后一个 (w) 分量,看看通道是否混淆。
  • @Arthur Bouvier 或者制作一个完全黑色且 alpha 为 255 的 png 或 jpg 文件,在您的片段着色器中加载并采样,并使用 alpha 混合,如果它什么都不画,则将 w 组件设为 1 和如果它绘制红色或蓝色,那么您知道您需要整理出正确的频道顺序。或者制作一个红色图像,并在使用 SDL 循环加载它之后,将每个字节作为 unsigned char 转换为 int 以查看红色在哪里。有很多方法可以解决问题。
  • 当我使用调试器进行单步调试时,我检查了表面格式上的 Amask 和 Ashift,两者都为零(顺便说一句,Bshift 也为零)
猜你喜欢
  • 1970-01-01
  • 2014-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多