Compressed texture batching in OpenGL

泪湿孤枕 提交于 2019-12-11 07:14:02

问题


I'm trying to create an atlas of compressed textures but I can't seem to get it working. Here is a code snippet:

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
}

I'm testing this with just 2 small KTX textures that have the same size and as you can see from the code I'm trying to append the second one next to the first one on the x axis.

My KTX parsing works fine as I can render individual textures but as soon as I try to batch (that is as soon as I use glCompressedTexSubImage2d) I get nothing on the screen.

It might be useful to know that all of this works fine if I replace compressed textures with PNGs and swap the glCompressedTexImage2d and glCompressedTexSubImage2d with their non-compressed versions...

One of the things that I cannot find any information on is the x and y position of the textures in the atlas. How do I offset them? So if the first texture has a width of 60 pixels for example, do I just position the second one at 61?

I've seen some code online where people calculate the x and y position as follows:

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

Is this what I need to do and why? I've tried it but it doesn't seem to work.

Also, I'm trying the above code on an ARM i.mx6 Quad with a Vivante GPU, and I get the suspicion from what I read online that glCompressedTexSubImage2d might not be working on this board.

Can anyone please help me out?


回答1:


The format you pass to glCompressedTexSubImage2D() must be the same as the one used for the corresponding glCompressedTexImage2D(). From the ES 2.0 spec:

This command does not provide for image format conversion, so an INVALID_OPERATION error results if format does not match the internal format of the texture image being modified.

Therefore, to match the glCompressedTexImage2D() call, the glCompressedTexSubImage2D() call needs to be:

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);

As for the sizes and offsets:

  • Your logic of determining the overall size would only work if the height of all sub-images is the same. Or more precisely, since the height is set to the height of the last sub-image, if no other height is larger than the last one. To make it more robust, you would probably want to use the maximum height of all sub-images.
  • I was surprised that you can't pass null as the last argument of glCompressedTexImage2D(), but it seems to be true. At least I couldn't find anything allowing it in the spec. But this being the case, I don't think it would be ok to simply pass the pointer to the data of the first sub-image. That would not be enough data, and it would read beyond the end of the memory. You may have to allocate and pass "data" that is large enough to cover the entire atlas texture. You could probably set it to anything (e.g. zero it out), since you're going to replace it anyway.
  • The way I read the ETC2 definition (as included in the ES 3.0 spec), the width/height of the texture do not strictly have to be multiples of 4. However, the positions for glCompressedTexSubImage2D() do have to be multiples of 4, as well as the width/height, unless they extend to the edge of the texture. This means that you have to make the width of each sub-image except the last a multiple of 4. At that point, you might as well use a multiple of 4 for everything.

Based on this, I think the size determination should look like this:

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;

Then to set the sub-images:

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;
}

The whole thing would get slightly simpler if you could ensure that the original texture images already had sizes that are multiples of 4. Then you can skip rounding up the sizes/positions to multiples of 4.




回答2:


After all, this was one of those mistakes that make you want to hit your head on a wall. GL_COMPRESSED_RGBA8_ETC2_EAC was actually not supported on the board.

I copied it from the headers but it did not query the device for supported formats. I can use a DXT5 format just fine with this code.



来源:https://stackoverflow.com/questions/28029700/compressed-texture-batching-in-opengl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!