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

在Qt/C++的OpenGL着色器对象中添加或切换多纹理的实现方案

问题

需要在OpenGL单个Shader中为同一物体的不同部分(如房屋的窗户、屋顶、墙面)切换不同纹理,而非混合多纹理。当前实现仅支持单张纹理,扩展性不足,基于Qt开发,现有代码及尝试过的无效方法如下:

当前Qt实现代码

void widgetClass::initializeGL()
{
 float r, g, b, a = 1.0f;
 initializeOpenGLFunctions();

 shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertexShader.vsh");
 shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragmentShader.fsh");
 shaderProgram.link();
 
 vertices << QVector3D(-0.5, -0.5,  0.5) << QVector3D( 0.5, -0.5,  0.5) << QVector3D( 0.5,  0.5,  0.5) // Front
             << QVector3D( 0.5,  0.5,  0.5) << QVector3D(-0.5,  0.5,  0.5) << QVector3D(-0.5, -0.5,  0.5)
             << QVector3D( 0.5, -0.5, -0.5) << QVector3D(-0.5, -0.5, -0.5) << QVector3D(-0.5,  0.5, -0.5) // Back
             << QVector3D(-0.5,  0.5, -0.5) << QVector3D( 0.5,  0.5, -0.5) << QVector3D( 0.5, -0.5, -0.5)
             << QVector3D(-0.5, -0.5, -0.5) << QVector3D(-0.5, -0.5,  0.5) << QVector3D(-0.5,  0.5,  0.5) // Left
             << QVector3D(-0.5,  0.5,  0.5) << QVector3D(-0.5,  0.5, -0.5) << QVector3D(-0.5, -0.5, -0.5)
             << QVector3D( 0.5, -0.5,  0.5) << QVector3D( 0.5, -0.5, -0.5) << QVector3D( 0.5,  0.5, -0.5) // Right
             << QVector3D( 0.5,  0.5, -0.5) << QVector3D( 0.5,  0.5,  0.5) << QVector3D( 0.5, -0.5,  0.5)
             << QVector3D(-0.5,  0.5,  0.5) << QVector3D( 0.5,  0.5,  0.5) << QVector3D( 0.5,  0.5, -0.5) // Top
             << QVector3D( 0.5,  0.5, -0.5) << QVector3D(-0.5,  0.5, -0.5) << QVector3D(-0.5,  0.5,  0.5)
             << QVector3D(-0.5, -0.5, -0.5) << QVector3D( 0.5, -0.5, -0.5) << QVector3D( 0.5, -0.5,  0.5) // Bottom
             << QVector3D( 0.5, -0.5,  0.5) << QVector3D(-0.5, -0.5,  0.5) << QVector3D(-0.5, -0.5, -0.5);

  colors << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) // Front
           << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) << QVector3D(1, 0, 0)
           << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) // Back
           << QVector3D(1, 0, 0) << QVector3D(1, 0, 0) << QVector3D(1, 0, 0)
           << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) // Left
           << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) << QVector3D(0, 1, 0)
           << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) // Right
           << QVector3D(0, 1, 0) << QVector3D(0, 1, 0) << QVector3D(0, 1, 0)
           << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) // Top
           << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) << QVector3D(0, 0, 1)
           << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) // Bottom
           << QVector3D(0, 0, 1) << QVector3D(0, 0, 1) << QVector3D(0, 0, 1);

  textureCoordinates << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Front
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0)
                       << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Back
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0)
                       << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Left
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0)
                       << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Right
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0)
                       << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Top
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0)
                       << QVector2D(0, 0) << QVector2D(1, 0) << QVector2D(1, 1) // Bottom
                       << QVector2D(1, 1) << QVector2D(0, 1) << QVector2D(0, 0);


 texturesBank[0] = new QOpenGLTexture(QImage(QString(":/texture.bmp")));
 texturesBank[0]->create();
 uint test = texturesBank[0]->textureId();
 texturesBank[0]->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
 texturesBank[0]->setMagnificationFilter(QOpenGLTexture::Linear);
 texturesBank[0]->setWrapMode(QOpenGLTexture::Repeat);

}


void widgetClass::paintGL()
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 QMatrix4x4 mMatrix;
 QMatrix4x4 vMatrix;

 QMatrix4x4 cameraTransformation;
 cameraTransformation.rotate(alpha, 0, 1, 0); 
 cameraTransformation.rotate(beta, 1, 0, 0); 

 QVector3D cameraPosition = cameraTransformation * QVector3D(0, 0, distance);
 QVector3D cameraUpDirection = cameraTransformation * QVector3D(0, 1, 0);

 vMatrix.lookAt(cameraPosition, QVector3D(0, 0, 0), cameraUpDirection); 

 texturesBank[0]->bind();
 shaderProgram.bind();
 shaderProgram.setUniformValue("mvpMatrix", pMatrix * vMatrix * mMatrix);
 
 shaderProgram.setAttributeArray("vertex", vertices.constData());
 shaderProgram.enableAttributeArray("vertex");
 
 shaderProgram.setAttributeArray("textureCoordinate", textureCoordinates.constData());
 shaderProgram.enableAttributeArray("textureCoordinate");

 shaderProgram.setAttributeArray("color", colors.constData());
 shaderProgram.enableAttributeArray("color");

 glDrawArrays(GL_TRIANGLES, 0, vertices.size());
 
 shaderProgram.disableAttributeArray("vertex");
 shaderProgram.disableAttributeArray("textureCoordinate");
 shaderProgram.disableAttributeArray("color");
 shaderProgram.release();

}


void widgetClass::resizeGL(int w, int h)
{
    pMatrix.setToIdentity();
    pMatrix.perspective(60.0, (float) w / (float) h, 0.001, 1000);

    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

当前着色器代码

顶点着色器(vertexShader.vsh):

uniform mat4 mvpMatrix;
in vec4 vertex;
in vec2 textureCoordinate;
in vec4 color;

out vec2 varyingTextureCoordinate;
out vec4 varyingColor;
void main(void)
{
    varyingColor = color;
    varyingTextureCoordinate = textureCoordinate;
    gl_Position = mvpMatrix * vertex;
}

片元着色器(fragmentShader.fsh):

uniform sampler2D texture;
in vec2 varyingTextureCoordinate;
in vec4 varyingColor;
out vec4 fragColor;
void main(void)
{
   fragColor = texture2D(texture, varyingTextureCoordinate);
}

尝试过的无效方法

  • 创建多个sampler2D并与0/1数组相乘:效率低且实现不专业
  • 调整纹理坐标:项目扩展时会导致资源文件过大
  • 创建纹理数组并修改绑定:未生效

解决方案

方法一:顶点级纹理ID属性(灵活可控,推荐)

核心思路:为每个顶点添加纹理ID属性,顶点着色器将ID传递到片元着色器,片元着色器根据ID选择对应纹理采样。

1. Qt代码修改

  • 在initializeGL中添加纹理ID数组,为不同面分配对应ID:
QVector<float> textureIds;
// Front/Back用纹理0,Left/Right用纹理1,Top/Bottom用纹理2
textureIds << 0 << 0 << 0 << 0 << 0 << 0; // Front
textureIds << 0 << 0 << 0 << 0 << 0 << 0; // Back
textureIds << 1 << 1 << 1 << 1 << 1 << 1; // Left
textureIds << 1 << 1 << 1 << 1 << 1 << 1; // Right
textureIds << 2 << 2 << 2 << 2 << 2 << 2; // Top
textureIds << 2 << 2 << 2 << 2 << 2 << 2; // Bottom

// 加载多张纹理
texturesBank[0] = new QOpenGLTexture(QImage(QString(":/wall.bmp")));
texturesBank[0]->create();
texturesBank[0]->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
texturesBank[0]->setMagnificationFilter(QOpenGLTexture::Linear);
texturesBank[0]->setWrapMode(QOpenGLTexture::Repeat);

texturesBank[1] = new QOpenGLTexture(QImage(QString(":/window.bmp")));
texturesBank[1]->create();
texturesBank[1]->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
texturesBank[1]->setMagnificationFilter(QOpenGLTexture::Linear);
texturesBank[1]->setWrapMode(QOpenGLTexture::Repeat);

texturesBank[2] = new QOpenGLTexture(QImage(QString(":/roof.bmp")));
texturesBank[2]->create();
texturesBank[2]->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
texturesBank[2]->setMagnificationFilter(QOpenGLTexture::Linear);
texturesBank[2]->setWrapMode(QOpenGLTexture::Repeat);
  • 在paintGL中绑定多个纹理到不同纹理单元,并启用纹理ID属性:
// 绑定纹理到不同单元
texturesBank[0]->bind(0);
texturesBank[1]->bind(1);
texturesBank[2]->bind(2);

shaderProgram.bind();
// 设置sampler对应的纹理单元
shaderProgram.setUniformValue("texture0", 0);
shaderProgram.setUniformValue("texture1", 1);
shaderProgram.setUniformValue("texture2", 2);

// 启用纹理ID属性
shaderProgram.setAttributeArray("textureId", textureIds.constData());
shaderProgram.enableAttributeArray("textureId");

// 原有绘制代码不变...

2. 着色器修改

顶点着色器:

uniform mat4 mvpMatrix;
in vec4 vertex;
in vec2 textureCoordinate;
in vec4 color;
in float textureId; // 新增纹理ID输入

out vec2 varyingTextureCoordinate;
out vec4 varyingColor;
out float varyingTextureId; // 传递到片元着色器

void main(void)
{
    varyingColor = color;
    varyingTextureCoordinate = textureCoordinate;
    varyingTextureId = textureId;
    gl_Position = mvpMatrix * vertex;
}

片元着色器:

uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
in vec2 varyingTextureCoordinate;
in vec4 varyingColor;
in float varyingTextureId;
out vec4 fragColor;

void main(void)
{
    if (varyingTextureId == 0.0) {
        fragColor = texture2D(texture0, varyingTextureCoordinate);
    } else if (varyingTextureId == 1.0) {
        fragColor = texture2D(texture1, varyingTextureCoordinate);
    } else if (varyingTextureId == 2.0) {
        fragColor = texture2D(texture2, varyingTextureCoordinate);
    } else {
        fragColor = varyingColor; //  fallback颜色
    }
}

方法二:使用纹理数组(Texture Array)

核心思路:将多张纹理打包成一个纹理数组,通过额外的纹理坐标分量选择数组中的纹理层,适合纹理尺寸一致的场景。

1. Qt代码修改

// initializeGL中创建纹理数组
QVector<QImage> textureImages;
textureImages << QImage(":/wall.bmp") << QImage(":/window.bmp") << QImage(":/roof.bmp");

QOpenGLTexture* textureArray = new QOpenGLTexture(QOpenGLTexture::Target2DArray);
textureArray->setSize(textureImages[0].width(), textureImages[0].height(), textureImages.size());
textureArray->setFormat(QOpenGLTexture::RGBA8_UNorm);
textureArray->allocateStorage();

// 逐个上传纹理到数组层
for (int i = 0; i < textureImages.size(); ++i) {
    textureArray->setData(i, 0, QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, textureImages[i].bits());
}

textureArray->setMinificationFilter(QOpenGLTexture::LinearMipMapNearest);
textureArray->setMagnificationFilter(QOpenGLTexture::Linear);
textureArray->setWrapMode(QOpenGLTexture::Repeat);

2. 着色器修改

顶点着色器:

uniform mat4 mvpMatrix;
in vec4 vertex;
in vec2 textureCoordinate;
in vec4 color;
in float textureLayer; // 纹理层ID

out vec3 varyingTextureCoord; // 合并坐标和层,变成vec3
out vec4 varyingColor;

void main(void)
{
    varyingColor = color;
    varyingTextureCoord = vec3(textureCoordinate, textureLayer);
    gl_Position = mvp
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 09:40:22