GLSL 像素着色器仅操作 0 纹理单元

Posted

技术标签:

【中文标题】GLSL 像素着色器仅操作 0 纹理单元【英文标题】:GLSL Pixel shader operates only 0 texture unite 【发布时间】:2015-05-12 14:27:19 【问题描述】:

我正在尝试编写一个简单的片段着色器,它应该混合 2 个或更多纹理。我已经在 Qt 5.4 上编写了测试项目,但由于某种原因,它无法操作任何绑定到非零联合的纹理。 它忽略任何值 setUniformValue("tex*", *); (str. 83-90) 并且任何 sampler2d 总是只操作绑定到 0 的纹理。

怎么了?

Source of test project on Qt 5.4 available at bitbucket

#include <QApplication>
#include <QCoreApplication>
#include <QOffscreenSurface>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLFramebufferObject>
#include <QOpenGLShader>
#include <QOpenGLTexture>
#include <QLabel>

int main(int argc, char *argv[])

    QApplication a(argc, argv);

    QSurfaceFormat format;
    format.setMinorVersion( 2 );
    format.setMajorVersion( 4 );
    format.setProfile( QSurfaceFormat::CompatibilityProfile );
//    format.setProfile( QSurfaceFormat::CoreProfile );

    QOpenGLContext context;
    context.setFormat(format);
    if(!context.create())        
        qFatal("Cannot create the requested OpenGL context!");
    

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

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    const float c_01SquareVertices[8] =0.0f, 0.0f,
                                        1.0f, 0.0f,
                                        1.0f, 1.0f,
                                        0.0f, 1.0f;
    glVertexPointer(2, GL_FLOAT, 0, c_01SquareVertices);
    glTexCoordPointer(2, GL_FLOAT, 0, c_01SquareVertices);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    int maxTextureUnits;
    glGetIntegerv ( GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits );
    qDebug()<<"GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" << maxTextureUnits;

    QImage texImg = QImage(":/tex/tex");
    QOpenGLTexture tex(texImg.mirrored());
    QImage texImg1 = QImage(":/tex/tex1");
    QOpenGLTexture tex1(texImg1.mirrored());
    QImage texImg2 = QImage(":/tex/tex2");
    QOpenGLTexture tex2(texImg2.mirrored());

    QString fsc =
            "uniform sampler2D tex;"
            "uniform sampler2D tex1;"
            "uniform sampler2D tex2;"
            "varying vec4 gl_TexCoord[];"
            "void main(void)"
            ""
            "   gl_FragColor = texture2D(tex2, gl_TexCoord[0].yx * 2.0);"
//            "   gl_FragColor = texture2D(tex1, gl_TexCoord[0].xy) + texture2D(tex2, gl_TexCoord[0].xy);"
            "";

    QOpenGLShader fsh( QOpenGLShader::Fragment, &context );
    fsh.compileSourceCode( fsc );

    QOpenGLShaderProgram pr( &context );

    pr.addShader( &fsh );
    pr.link();

    QOpenGLFramebufferObjectFormat fboFormat;
//    fboFormat.setInternalTextureFormat(GL_ALPHA32F);
    QOpenGLFramebufferObject fbo( 1000, 1000, fboFormat );
    fbo.bind();
        glViewport(0,0,fbo.width(),fbo.height());
        glClear(GL_COLOR_BUFFER_BIT);

        tex.bind(0);
        pr.setUniformValue("tex", GLuint(1));

        tex1.bind(2);
        pr.setUniformValue("tex1", GLuint(2));

        tex2.bind(3);
        pr.setUniformValue("tex2", GLuint(3));

        pr.bind();

        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
    fbo.release();

    QLabel w;
    w.resize(fbo.size());
    w.setPixmap(QPixmap::fromImage(fbo.toImage()));
    w.show();

    return a.exec();

【问题讨论】:

尝试将pr.bind()移动到第一个pr.setUniformValue之前(glClear之后) 非常感谢!它有效) @ColonelThirtyTwo 您应该将您的评论移至答案 【参考方案1】:

在OpenGL的C API中,为了使用或修改一个对象,你必须先绑定它*。

显然,pr.setUniformValue 在调用 glUniform 更改统一值之前没有绑定 pr。虽然有点不方便和不直观,但这是可以理解的;一遍又一遍地冗余绑定相同的着色器会产生性能开销。

所以只需将pr.bind() 移到您调用pr.setUniformValue 的上方。


*EXT_direct_state_access 扩展允许您在不绑定对象的情况下修改对象,但需要不同的 API,并且不保证会出现在 4.5(最新)之前的 OpenGL 版本中。

【讨论】:

以上是关于GLSL 像素着色器仅操作 0 纹理单元的主要内容,如果未能解决你的问题,请参考以下文章

如何在着色器之后读取像素?

顶点着色器 glsl qt 中的纹理映射

GLSL将颜色数据从片段着色器发送到顶点着色器似乎总是等于0

为啥我的着色器中的 GLSL 纹理坐标不是线性的?

为啥片段着色器比渲染纹理更快?

GLSL 像素化着色器中的边框伪影