You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将S3TC/DXTn压缩纹理直接转换为QImage的技术咨询

解决方案:直接将无DDS头的DXT压缩纹理转为QImage(基于Qt OpenGL)

Hey, I’ve tackled similar legacy texture conversion work before, so let’s walk through how to pull this off using Qt’s OpenGL tools—no temporary files or manual decompression required, exactly what you’re looking for.

Step 1: Ensure a Valid OpenGL Context

First, you need an active QOpenGLContext to work with. If you’re working within a QOpenGLWidget, you can handle this directly in its initializeGL or paintGL methods. For offline/batch processing (no UI), create an offscreen surface and bind the context:

QOpenGLContext context;
context.setFormat(QSurfaceFormat::defaultFormat());
if (!context.create()) {
    // Handle context creation failure
}

QOffscreenSurface surface;
surface.setFormat(context.format());
surface.create();

context.makeCurrent(&surface);

Step 2: Load Compressed Data into QOpenGLTexture

This is where setCompressedData shines—you just need to map your DXT format to the corresponding Qt OpenGL texture format, and pass in your raw pixel data with the correct dimensions:

// Assume you have these metadata values pre-defined
int width = 512;
int height = 512;
const unsigned char* rawData = /* Your compressed pixel data pointer */;
size_t dataSize = /* Size of the compressed data in bytes */;
QOpenGLTexture::TextureFormat glFormat;

// Map DXT format to OpenGL format
switch (yourTextureFormat) {
    case DXT1:
        glFormat = QOpenGLTexture::RGB_S3TC_DXT1_EXT;
        break;
    case DXT3:
        glFormat = QOpenGLTexture::RGBA_S3TC_DXT3_EXT;
        break;
    case DXT5:
        glFormat = QOpenGLTexture::RGBA_S3TC_DXT5_EXT;
        break;
    default:
        // Handle invalid format
}

// Create and configure the texture
QOpenGLTexture texture(QOpenGLTexture::Target2D);
texture.setSize(width, height);
texture.setFormat(glFormat);
texture.allocateStorage();
texture.setCompressedData(dataSize, rawData);
texture.setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear); // Optional: adjust filtering

Step 3: Render Texture to Framebuffer Object (FBO)

Next, we’ll draw the texture to an FBO so we can extract it as a QImage. For this, you’ll need to render a full-screen quad. If you’re using a core OpenGL profile, you’ll need a simple shader program and vertex buffer. Here’s a minimal example:

First, define basic shaders (you can embed these as strings):

const char* vertexShaderSource = R"(
    #version 330 core
    layout (location = 0) in vec2 aPos;
    layout (location = 1) in vec2 aTexCoord;
    out vec2 TexCoord;
    void main() {
        gl_Position = vec4(aPos, 0.0, 1.0);
        TexCoord = aTexCoord;
    }
)";

const char* fragmentShaderSource = R"(
    #version 330 core
    out vec4 FragColor;
    in vec2 TexCoord;
    uniform sampler2D texture1;
    void main() {
        FragColor = texture(texture1, TexCoord);
    }
)";

Then set up the shader program, vertex data, and render:

// Create shader program
QOpenGLShaderProgram shaderProgram;
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
shaderProgram.link();

// Full-screen quad vertex data (positions + texture coords)
float vertices[] = {
    // Positions       // TexCoords
     1.0f,  1.0f,      1.0f, 1.0f,
     1.0f, -1.0f,      1.0f, 0.0f,
    -1.0f, -1.0f,      0.0f, 0.0f,
    -1.0f,  1.0f,      0.0f, 1.0f
};
unsigned int indices[] = { 0, 1, 3, 1, 2, 3 };

// Create vertex buffer/array objects
QOpenGLBuffer vbo(QOpenGLBuffer::VertexBuffer);
QOpenGLBuffer ebo(QOpenGLBuffer::IndexBuffer);
QOpenGLVertexArrayObject vao;
vao.create();
vao.bind();

vbo.create();
vbo.bind();
vbo.allocate(vertices, sizeof(vertices));

ebo.create();
ebo.bind();
ebo.allocate(indices, sizeof(indices));

// Set vertex attribute pointers
shaderProgram.bind();
shaderProgram.enableAttributeArray(0);
shaderProgram.setAttributeBuffer(0, GL_FLOAT, 0, 2, sizeof(float)*4);
shaderProgram.enableAttributeArray(1);
shaderProgram.setAttributeBuffer(1, GL_FLOAT, sizeof(float)*2, 2, sizeof(float)*4);

// Create FBO
QOpenGLFramebufferObject fbo(width, height);
if (!fbo.isValid()) {
    // Handle FBO creation failure
}

// Render to FBO
fbo.bind();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);

shaderProgram.bind();
texture.bind(0);
shaderProgram.setUniformValue("texture1", 0);

vao.bind();
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

// Clean up bindings
vao.release();
texture.release();
shaderProgram.release();
fbo.release();

Step 4: Extract QImage from FBO

Once rendering is done, you can grab the image directly from the FBO. Make sure to wait for OpenGL to finish rendering first:

glFinish(); // Ensure all rendering commands are completed
QImage resultImage = fbo.toImage(QImage::Format_RGBA8888);

Key Notes & Optimizations

  • Extension Check: Verify that the OpenGL context supports S3TC compression with context.hasExtension("GL_EXT_texture_compression_s3tc")—most modern GPUs do, but older ones might not.
  • Batch Processing: If you’re handling hundreds/thousands of textures, reuse the FBO, shader program, and VAO/VBO/EB objects. Just resize the FBO and update the texture data instead of creating new objects each time.
  • Thread Safety: If processing in a background thread, make sure the OpenGL context is properly bound to that thread and the offscreen surface.
  • Alternative for Compatibility Profile: If you’re stuck with an older OpenGL compatibility profile, you can skip the shader setup and use fixed-function pipeline calls (e.g., glBegin(GL_QUADS) with texture coordinates), but modern core profile is more reliable long-term.

内容的提问来源于stack exchange,提问作者user9088793

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:37:33