【问题标题】:Compressed texture batching in OpenGLOpenGL中的压缩纹理批处理
【发布时间】:2015-02-27 09:33:34
【问题描述】:

我正在尝试创建压缩纹理图集,但似乎无法正常工作。这是一个代码sn-p:

void Texture::addImageToAtlas(ImageProperties* imageProperties)
{   
    generateTexture();  // delete and regenerate an empty texture
    bindTexture();      // bind it

    atlasProperties.push_back(imageProperties);

    width = height = 0;
    for (int i=0; i < atlasProperties.size(); i++)
    {
        width += atlasProperties[i]->width;
        height = atlasProperties[i]->height;
    }

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // glCompressedTexImage2D MUST be called with valid data for the 'pixels'
    // parameter. Won't work if you use zero/null.
    glCompressedTexImage2D(GL_TEXTURE_2D, 0, 
        GL_COMPRESSED_RGBA8_ETC2_EAC,
        width,
        height,
        0,
        (GLsizei)(ceilf(width/4.f) * ceilf(height/4.f) * 16.f),
        atlasProperties[0]->pixels);

    // Recreate the whole atlas by adding all the textures we have appended 
    // to our vector so far
    int x, y = 0;
    for (int i=0; i < atlasProperties.size(); i++)
    {
        glCompressedTexSubImage2D(GL_TEXTURE_2D,
            0,
            x,
            y,
            atlasProperties[i]->width,
            atlasProperties[i]->height,
            GL_RGBA,
            (GLsizei)(ceilf(atlasProperties[i]->width/4.f) * ceilf(atlasProperties[i]->height/4.f) * 16.f),
        atlasProperties[i]->pixels);

        x += atlasProperties[i]->width;
    }

    unbindTexture();  // unbind the texture
}

我只用 2 个大小相同的小型 KTX 纹理对此进行测试,正如您从代码中看到的那样,我试图在 x 轴上将第二个附加到第一个旁边。

我的 KTX 解析工作正常,因为我可以渲染单个纹理,但是一旦我尝试批处理(即一旦我使用 glCompressedTexSubImage2d),屏幕上什么也没有。

如果我用 PNG 替换压缩纹理并将 glCompressedTexImage2d 和 glCompressedTexSubImage2d 与它们的非压缩版本交换,那么知道所有这些都可以正常工作可能会很有用...

我找不到任何信息的一件事是图集中纹理的 x 和 y 位置。我该如何抵消它们?因此,例如,如果第一个纹理的宽度为 60 像素,我是否只需将第二个纹理定位在 61 处?

我在网上看到一些代码,人们计算 x 和 y 位置如下:

x &= ~3;
y &= ~3;

这是我需要做的吗?为什么?我已经尝试过了,但它似乎不起作用。

另外,我正在使用 Vivante GPU 的 ARM i.mx6 Quad 上尝试上述代码,我从网上阅读的内容中怀疑 glCompressedTexSubImage2d 可能无法在此板上工作。

谁能帮帮我?

【问题讨论】:

  • 不应该是height = max(height, atlasProperties[i]-&gt;height)

标签: c++ opengl opengl-es


【解决方案1】:

您传递给glCompressedTexSubImage2D() 的格式必须与用于相应glCompressedTexImage2D() 的格式相同。来自 ES 2.0 规范:

此命令不提供图像格式转换,因此如果格式与正在修改的纹理图像的内部格式不匹配,则会导致 INVALID_OPERATION 错误。

因此,要匹配glCompressedTexImage2D() 调用,glCompressedTexSubImage2D() 调用需要是:

glCompressedTexSubImage2D(GL_TEXTURE_2D,
    0, x, y, atlasProperties[i]->width, atlasProperties[i]->height,
    GL_COMPRESSED_RGBA8_ETC2_EAC,
    (GLsizei)(ceilf(atlasProperties[i]->width/4.f) *
              ceilf(atlasProperties[i]->height/4.f) * 16.f),
    atlasProperties[i]->pixels);

关于尺寸和偏移量:

  • 仅当所有子图像的高度相同时,您确定整体大小的逻辑才有效。或者更准确地说,由于高度设置为最后一个子图像的高度,如果没有其他高度大于最后一个。为了使其更加健壮,您可能希望使用所有子图像的最大高度。
  • 我很惊讶您不能将 null 作为glCompressedTexImage2D() 的最后一个参数传递,但这似乎是真的。至少我在规范中找不到任何允许它的东西。但在这种情况下,我认为简单地将指针传递给第一个子图像的数据是不行的。那将是不够的数据,它会读取超出内存的末尾。您可能必须分配和传递足够大以覆盖整个图集纹理的“数据”。您可以将其设置为任何值(例如将其归零),因为无论如何您都要替换它。
  • 按照我阅读 ETC2 定义(包含在 ES 3.0 规范中)的方式,纹理的宽度/高度不一定是 4 的倍数。但是,glCompressedTexSubImage2D() 的位置 do 必须是 4 的倍数,以及宽度/高度,除非它们延伸到纹理的边缘。这意味着您必须将除最后一个之外的每个子图像的宽度设为 4 的倍数。此时,您不妨对所有内容都使用 4 的倍数。

基于此,我认为大小确定应该是这样的:

width = height = 0;
for (int i = 0; i < atlasProperties.size(); i++)
{
    width += (atlasProperties[i]->width + 3) & ~3;
    if (atlasProperties[i]->height > height)
    {
        height = atlasProperties[i]->height;
    }
}
height = (height + 3) & ~3;

uint8_t* dummyData = new uint8_t[width * height];
memset(dummyData, 0, width * height);
glCompressedTexImage2D(GL_TEXTURE_2D, 0, 
    GL_COMPRESSED_RGBA8_ETC2_EAC,
    width, height, 0,
    width * height,
    dummyData);
delete[] dummyData;

然后设置子图:

int xPos = 0;
for (int i = 0; i < atlasProperties.size(); i++)
{
    int w = (atlasProperties[i]->width + 3) & ~3;
    int h = (atlasProperties[i]->height + 3) & ~3;
    glCompressedTexSubImage2D(GL_TEXTURE_2D,
        0, xPos, 0, w, h,
        GL_COMPRESSED_RGBA8_ETC2_EAC,
        w * h,
        atlasProperties[i]->pixels);
    xPos += w;
}

如果您可以确保原始纹理图像的大小已经是 4 的倍数,那么整个事情会变得稍微简单一些。然后您可以跳过将大小/位置四舍五入为 4 的倍数。

【讨论】:

    【解决方案2】:

    毕竟,这是让您想一头撞到墙上的错误之一。 GL_COMPRESSED_RGBA8_ETC2_EAC 板上实际上不支持。

    我从标题中复制了它,但它没有向设备查询支持的格式。我可以在这段代码中使用DXT5 格式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-27
      • 1970-01-01
      • 2019-02-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多