OpenGL RGB DXT1压缩纹理mipmap上传

Posted

技术标签:

【中文标题】OpenGL RGB DXT1压缩纹理mipmap上传【英文标题】:OpenGL RGB DXT1 compressed texture mipmap upload 【发布时间】:2020-01-15 13:11:54 【问题描述】:

我正在尝试使用 PBO 上传 GL_COMPRESSED_RGB_S3TC_DXT1_EXT 格式的 mipmaped 纹理级别。 该程序正在使用stb_resize.hstb_dxt.h 库调整和压缩图像。

显然图像压缩工作正常,但上传任何 mipmap 级别 会产生以下结果:

但是,如果我没有为它正确渲染的纹理定义任何 mipmap 级别。 此外,我没有收到来自 OpenGL 的 任何 错误,因为我启用了 GL_KHR_debug

这里是上传mipmap的opengl代码,中间是我自己的一些库代码:

// Load image:
auto img = loadImage2D("parrot.png");

// Resize the image to power-of-two
int max_mipmaps = 1;
unsigned int max_size = nextPow2(std::max(img.width, img.height));
for(int i = max_size; i > 4; i >>= 1)
    max_mipmaps++;

auto rimg = img.scaleCubic(max_size,max_size);

// Allocate PBO to source compressed mipmap levels from
handle_t pbo;
size_t pbosize = rimg.compressed_size();
glCreateBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, pbosize, 0,
    GL_DYNAMIC_STORAGE_BIT|GL_MAP_WRITE_BIT|GL_MAP_READ_BIT|GL_MAP_PERSISTENT_BIT);
// Map the buffer
void * pbodata = (char*)glMapBufferRange(
    GL_PIXEL_UNPACK_BUFFER, 0, pbosize,
    GL_MAP_WRITE_BIT|GL_MAP_READ_BIT
    |GL_MAP_PERSISTENT_BIT
    |GL_MAP_FLUSH_EXPLICIT_BIT);

// Compress the image and write the data directly into PBO.
// compress_to() fmt returns GL_COMPRESSED_RGB_S3TC_DXT1_EXT
option_t fmt;
rimg.compress_to(pbodata, fmt);
glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, pbosize);

// Allocate the texture
glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTextureStorage2D(texture, max_mipmaps, fmt, rimg.width, rimg.width);
// Upload base image level.
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glCompressedTextureSubImage2D(texture, 0, 0,0, rimg.width, rimg.height, fmt, rimg.compressed_size(), 0);
// Process and Upload mipmap levels.
unsigned int mipmapsize = max_size >> 1;
for(int i = 1; i < max_mipmaps && mipmapsize >= 4; ++i) 
    // Resize next mipmap level.
    rimg = img.scaleLinear(mipmapsize,mipmapsize);

    // Compress the image and write result to *pbodata
    rimg.compress_to(pbodata, fmt);
    glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, pbosize);

    // Upload mipmap image level.
    glCompressedTexSubImage2D(GL_TEXTURE_2D, i, 0,0, rimg.width, rimg.height, fmt, rimg.compressed_size(), 0);

    mipmapsize >>= 1;

// Discard the PBO
glUnmapNamedBuffer(pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
glDeleteBuffers(1, &pbo);

// Set texture params.
glTextureParameteri(texture, GL_TEXTURE_BASE_LEVEL, 0);
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, max_mipmaps - 1);
glTextureParameteri(texture, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTextureParameteri(texture, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

【问题讨论】:

【参考方案1】:

执行glFlushMappedBufferRange足以确保与 GPU 正确同步。这似乎是这里发生的事情:整个 mipmap 金字塔是在 CPU 上创建的,GPU 处理第一个 glCompressedTexSubImage2D 调用之前。

作为对这个假设的快速测试,您可以在循环中添加glFinish。要正确同步它,您可以使用GL Sync Objects,但最好完全避免同步:只需使用足够大的 PBO 来保存整个 mipmap 金字塔数据。

【讨论】:

我怀疑 CPU 在 glCompressedTexSubImage2D() 获取数据之前过早地覆盖了数据。 谢谢 PS:我可以简单地为每个纹理级别上传使用新的新鲜 PBO,然后在最后一次 gl*TexSubImage2D 调用后丢弃 PBO? 是的,您当然也可以在这里使用多个 PBO。最好的选择是性能方面的,很可能只使用一个具有不同范围的 PBO,并且只创建和映射一次。 OTOH,无论如何,CPU 端 DXT 压缩和图像缩放操作可能是这里的限制因素......

以上是关于OpenGL RGB DXT1压缩纹理mipmap上传的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL之Mipmap、压缩纹理

在硬件中高效实现 DXT1 纹理解压

opengl 渲染纹理并在是不是使用 mipmap 之间切换

OpenGL中怎么使用压缩纹理?

OpenGL ES 压缩纹理

初识OpenGL (-)多级渐远纹理(Mipmap)