无法使用QGLShaderProgram在Qt 4.7应用程序中使用组数组编译GLSL 3.30着色器

时间:2011-11-23 11:11:19

标签: qt opengl glsl shader opengl-3

我正在开发一个使用Qt 4.7的应用程序,目的是创建一个简单的3D OpenGL查看器。我想使用GLSL 3.30着色器。我使用的是Linux Ubutu 11.10,我的CG是带有NVS 3100M GPU的NVIDIA,NVIDIA驱动版本是280.13,OpenGL版本是3.3.0

我有一个工作的基本着色器,它不计算任何光照。那是想做下一步,但我遇到了问题。

这是我未经修改的顶点着色器。

#version 330

in vec3 inPosition;
in vec4 inColor;
in vec3 inNormal;
uniform mat4 inModelMatrix;
uniform mat4 inViewMatrix;
uniform mat4 inProjectionMatrix;
out vec4 exColor;

uniform Light {
    vec4 position;
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
} lights[4];

void main(void)
{
    gl_Position = inProjectionMatrix * inViewMatrix * inModelMatrix * vec4(inPosition, 1.0);
    exColor = lights[0].ambient;
}

GLSL 3.30的OpenGL着色语言参考指出:

uniform Transform { // API uses “Transform[2]” to refer to instance 2
    mat4 ModelViewMatrix;
    mat4 ModelViewProjectionMatrix;
    float Deformation;
} transforms[4];
...
... = transforms[2].ModelViewMatrix; // shader access of instance 2
// API uses “Transform.ModelViewMatrix” to query an offset or other query

所以如果我没弄错的话,代码是正确的。但是,它甚至没有编译。抛出以下错误:

QGLShader::link: "Vertex info
-----------
Internal error: assembly compile error for vertex shader at offset 1805:
-- error message --
line 38, column 15:  error: expected '='
line 82, column 36:  error: expected ';'
-- internal assembly text --
!!NVvp4.1
OPTION NV_parameter_buffer_object2;
# cgc version 3.1.0001, build date Jul 27 2011
# command line args: 
#vendor NVIDIA Corporation
#version 3.1.0.1
#profile gp4_1vp
#program main
#semantic Light.lights
#semantic inModelMatrix
#semantic inViewMatrix
#semantic inProjectionMatrix
#var float4 gl_Position : $vout.POSITION : HPOS : -1 : 1
#var float4 lights[0].position : BUFFER[0] : buffer[0][0] : -1 : 0
#var float4 lights[0].ambient : BUFFER[0] : buffer[0][16] : -1 : 1
#var float4 lights[0].diffuse : BUFFER[0] : buffer[0][32] : -1 : 0
#var float4 lights[0].specular : BUFFER[0] : buffer[0][48] : -1 : 0
#var float4 lights[1].position : BUFFER[1] : buffer[1][0] : -1 : 0
#var float4 lights[1].ambient : BUFFER[1] : buffer[1][16] : -1 : 0
#var float4 lights[1].diffuse : BUFFER[1] : buffer[1][32] : -1 : 0
#var float4 lights[1].specular : BUFFER[1] : buffer[1][48] : -1 : 0
#var float4 lights[2].position : BUFFER[2] : buffer[2][0] : -1 : 0
#var float4 lights[2].ambient : BUFFER[2] : buffer[2][16] : -1 : 0
#var float4 lights[2].diffuse : BUFFER[2] : buffer[2][32] : -1 : 0
#var float4 lights[2].specular : BUFFER[2] : buffer[2][48] : -1 : 0
#var float4 lights[3].position : BUFFER[3] : buffer[3][0] : -1 : 0
#var float4 lights[3].ambient : BUFFER[3] : buffer[3][16] : -1 : 0
#var float4 lights[3].diffuse : BUFFER[3] : buffer[3][32] : -1 : 0
#var float4 lights[3].specular : BUFFER[3] : buffer[3][48] : -1 : 0
#var float3 inPosition : $vin.ATTR0 : ATTR0 : -1 : 1
#var float4 inColor :  :  : -1 : 0
#var float3 inNormal :  :  : -1 : 0
#var float4x4 inModelMatrix :  : c[0], 4 : -1 : 1
#var float4x4 inViewMatrix :  : c[4], 4 : -1 : 1
#var float4x4 inProjectionMatrix :  : c[8], 4 : -1 : 1
#var float4 exColor : $vout.ATTR0 : ATTR0 : -1 : 1
PARAM c[12] = { program.local[0..11] };
CBUFFER buf0[][] = { program.buffer[0..3] };
ATTRIB vertex_attrib[] = { vertex.attrib[0..0] };
OUTPUT result_attrib[] = { result.attrib[0..0] };
TEMP R0, R1, R2, R3, R4, R5, R6, R7, R8;
MOV.F R3, c[9];
MOV.F R2, c[8];
MUL.F R0, R3, c[5].y;
MOV.F R1, c[10];
MAD.F R0, R2, c[5].x, R0;
MAD.F R5, R1, c[5].z, R0;
MOV.F R0, c[11];
MAD.F R6, R0, c[5].w, R5;
MUL.F R4, R3, c[4].y;
MAD.F R5, R2, c[4].x, R4;
MAD.F R5, R1, c[4].z, R5;
MAD.F R5, R0, c[4].w, R5;
MUL.F R4, R6, c[1].y;
MAD.F R8, R5, c[1].x, R4;
MUL.F R4, R3, c[6].y;
M    UL.F R7, R6, c[0].y;
MAD.F R4, R2, c[6].x, R4;
MUL.F R3, R3, c[7].y;
MAD.F R2, R2, c[7].x, R3;
MAD.F R3, R1, c[6].z, R4;
MAD.F R1, R1, c[7].z, R2;
MAD.F R2, R0, c[6].w, R3;
MUL.F R4, R6, c[2].y;
MAD.F R0, R0, c[7].w, R1;
MAD.F R3, R2, c[1].z, R8;
MAD.F R1, R0, c[1].w, R3;
MAD.F R7, R5, c[0].x, R7;
MAD.F R3, R2, c[0].z, R7;
MAD.F R3, R0, c[0].w, R3;
MUL.F R1, vertex.attrib[0].y, R1;
MAD.F R1, vertex.attrib[0].x, R3, R1;
MUL.F R3, R6, c[3].y;
MAD.F R3, R5, c[3].x, R3;
MAD.F R3, R2, c[3].z, R3;
MAD.F R4, R5, c[2].x, R4;
MAD.F R3, R0, c[3].w, R3;
MAD.F R2, R2, c[2].z, R4;
MAD.F R0, R0, c[2].w, R2;
MAD.F R0, vertex.attrib[0].z, R0, R1;
ADD.F result.position, R0, R3;
LDC.F32X4 result.attrib[0], buf0[0][16];
END
# 41 instructions, 9 R-regs
" 

如果我删除对lights [0] .ambient的调用,则着色器会编译,但是当我尝试获取统一位置时,它不起作用:

int lightsLocation = shaderProgram.uniformLocation("Light[0]");

PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)(context()->getProcAddress("glGetUniformLocation"));

qDebug() << lightsLocation << glGetUniformLocation(shaderProgram.programId(), "Light[0]"); // -1 -1

我也尝试过“Light”,“lights”,“lights [0]”,“lights [0] .ambient”作为统一名称但没有成功。

经过一番搜索后,我发现了这篇文章:Setting the values of a struct array from JS to GLSL因此尝试使用结构,但这也不起作用。

后续问题

如果我能通过一些答案把它带到工作的事件,我不知道我应该使用哪种QGLShaderProgram方法来上传数据。

TL;博士

1)为什么下面的代码没有编译?     out vec4 exColor;

uniform Light {
     vec4 ambient;
} lights[4];

void main(void) {
    exColor = lights[0].ambient;
}

2)应该使用什么统一名称作为QGLShaderProgam :: uniformLocation()的参数来获取uniform Light { ... } lights[4];struct Light { ... }; uniform Light lights[4];的位置?

3)检索到位置后,应该使用QGLShaderProgram的哪些功能将数据上传到GPU?

非常感谢!

2 个答案:

答案 0 :(得分:0)

1)你在Light之前缺少“struct”才能成为一个结构。

或......

您的代码是合法的,但它创建了一个 Uniform Block ,用于在不同着色器之间轻松共享大块数据,并且不存储在着色器统一块中,而是存储在缓冲区中宾语。如果您打算这样做,那么您需要在着色器的开头包含以下行:

#extension GL_ARB_uniform_buffer_object : enable

请注意,这不适用于较旧的NVIDIA GPU(驱动程序错误?它不适用于较旧的ATis),但相同的代码适用于GeForce GTX 560。

另外,熟悉GL_ARB_uniform_buffer_object(有一个示例着色器和代码来设置它。)

2)如果您没有使用(引用)制服,编译器会对其进行优化,这就是您无法获得统一位置的原因。这可能会导致沮丧,因为即使使用正确的名称也无法获得统一的位置。

对于统一缓冲区,内存中数据的布局由specs给出,然后创建一个缓冲区对象(glGenBuffers()),用原始数据填充它(glBufferData())并使用它将它绑定到统一块glGetUniformBlockIndex()和glUniformBlockBinding()。

如果它是一个“结构”,你总是可以通过glGetActiveUniformName()找到活动制服的名称,你可以看到要使用的名称。对于你的结构,你需要使用“lights [0] .ambient”到“lights [3] .ambient”来获得统一的位置,然后调用glUniform4f()四次来指定数据(因为指向的变量是矢量)。

3)上面已经说过了。 glBufferData()用于统一块,或者适用于统一的glUniform *()。如果在结构中,则需要单独上传结构成员。如果在结构数组中,仍然需要为数组中的每个结构单独上载结构成员。如果一个数组是结构的成员,那么你只能使用glUniform * v()来上传一个浮点数/向量/矩阵数组。

答案 1 :(得分:0)

要以与Qt对顶点缓冲区对象和索引缓冲区对象类似的方式处理统一缓冲区对象,我创建了这个类:

#ifndef UNIFORMBUFFEROBJECT_H
#define UNIFORMBUFFEROBJECT_H

#include <qgl.h>

struct UBOInfoStruct {
    QString uniformName;
    GLuint progId;
    GLuint blockIdx;
    GLint blockSize;
    GLuint bindingIdx;
};

typedef UBOInfoStruct UBOInfo;

class UniformBufferObject
{
public:
    UniformBufferObject();

    bool create();
    bool isCreated() const;

    void destroy();

    bool bind();
    bool bind(GLuint progId, QString uniformName);
    void release();

    GLuint bufferId() const;

//    bool read(int offset, void *data, int count);
    void write(int offset, const void *data, int count);

    void allocate(const void *data, int count);
    inline void allocate(int count) { allocate(0, count); }


protected:
    static GLuint bindingIndex();
    static QVector<GLuint> m_bindingIndices;

    bool initFunctionPointers(const QGLContext* m_glContext);

    GLuint m_bufferId;

    const QGLContext* m_glContext;

    bool m_inited;

    QVector<UBOInfo> m_UBOInfos;

    PFNGLBINDBUFFERPROC glBindBuffer;
    PFNGLBINDBUFFERBASEPROC glBindBufferBase;
    PFNGLBINDBUFFERRANGEPROC glBindBufferRange;
    PFNGLBUFFERDATAPROC glBufferData;
    PFNGLBUFFERSUBDATAPROC glBufferSubData;
    PFNGLDELETEBUFFERSPROC glDeleteBuffers;
    PFNGLGENBUFFERSPROC glGenBuffers;
    PFNGLGETACTIVEUNIFORMBLOCKIVPROC glGetActiveUniformBlockiv;
    PFNGLGETACTIVEUNIFORMSIVPROC glGetActiveUniformsiv;
    PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
    PFNGLGETUNIFORMINDICESPROC glGetUniformIndices;
    PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
};

#endif // UNIFORMBUFFEROBJECT_H

CPP:

#include "uniformbufferobject.h"

#include <QGLContext>

#include <QDebug>

QVector<GLuint> UniformBufferObject::m_bindingIndices = QVector<GLuint>();

GLuint UniformBufferObject::bindingIndex() {
    for(GLuint i = 0, size = m_bindingIndices.size(); i < size; i++) {
        if(m_bindingIndices.at(i) != i) {
            m_bindingIndices.insert(i, i);
            return i;
        }
    }
    m_bindingIndices.append(m_bindingIndices.size());
    return m_bindingIndices.size();
}

UniformBufferObject::UniformBufferObject() :
    m_bufferId(0), m_glContext(NULL), m_inited(false),
    glBindBuffer(NULL), glBindBufferBase(NULL), glBindBufferRange(NULL), glBufferData(NULL), glDeleteBuffers(NULL), glGenBuffers(NULL),
    glGetActiveUniformBlockiv(NULL), glGetActiveUniformsiv(NULL), glGetUniformBlockIndex(NULL), glGetUniformIndices(NULL),
    glUniformBlockBinding(NULL)
{
}

bool UniformBufferObject::create() {
    const QGLContext* m_glContext = QGLContext::currentContext();

    if(m_glContext) {
        if(!m_inited && !initFunctionPointers(m_glContext)) {
            qDebug("Cannot find Uniform Buffer Objects related functions");
            return false;
        }

        GLuint tmpBufferId = 0;
        glGenBuffers(1, &tmpBufferId);

        if(tmpBufferId) {
                m_bufferId = tmpBufferId;
            this->m_glContext = m_glContext;

            return true;
        } else {
            qDebug("Invalid buffer Id");
        }
    }

    qDebug("Could not retrieve buffer");

    return false;
}

bool UniformBufferObject::isCreated() const {
    return (bool)(m_bufferId != 0);
}

void UniformBufferObject::destroy() {
    if(m_bufferId != 0) {
        glDeleteBuffers(1, &m_bufferId);
    }
    m_bufferId = 0;
    m_glContext = NULL;
}

bool UniformBufferObject::bind() {
    if(!isCreated()) {
        qDebug("Buffer not created");
        return false;
    }

    glBindBuffer(GL_UNIFORM_BUFFER, m_bufferId);

    return true;
}

bool UniformBufferObject::bind(GLuint progId, QString uniformName) {
    GLuint tmpBlockIdx = glGetUniformBlockIndex(progId, uniformName.toUtf8());

    if(tmpBlockIdx == GL_INVALID_INDEX) {
        qDebug() << QString("Could not find block index of block named: %1").arg(uniformName);

        return false;
    }

    GLint tmpBlockSize;
    glGetActiveUniformBlockiv(progId, tmpBlockIdx, GL_UNIFORM_BLOCK_DATA_SIZE, &tmpBlockSize);

    GLuint tmpBindingIdx = bindingIndex();
    glUniformBlockBinding(progId, tmpBlockIdx, tmpBindingIdx);

    glBindBufferBase(GL_UNIFORM_BUFFER, tmpBindingIdx, m_bufferId);

    if(glGetError() == GL_INVALID_VALUE || glGetError() == GL_INVALID_ENUM) {
        qDebug() << "ERROR";
    }

    UBOInfo info;
    info.progId = progId;
    info.uniformName = uniformName;
    info.blockIdx = tmpBlockIdx;
    info.blockSize = tmpBlockSize;
    info.bindingIdx = tmpBindingIdx;

    m_UBOInfos.append(info);

    return true;
}

void UniformBufferObject::release() {
    glBindBuffer(GL_UNIFORM_BUFFER, 0);
}

GLuint UniformBufferObject::bufferId() const {
    return m_bufferId;
}

void UniformBufferObject::write(int offset, const void *data, int count) {
    if(!isCreated())
        return;

    bind();

    glBufferSubData(GL_UNIFORM_BUFFER, offset, count, data);
}

void UniformBufferObject::allocate(const void *data, int count) {
    if(!isCreated())
        return;

    bind();

    glBufferData(GL_UNIFORM_BUFFER, count, data, GL_DYNAMIC_DRAW);
}

bool UniformBufferObject::initFunctionPointers(const QGLContext* m_glContext) {
    glBindBuffer = (PFNGLBINDBUFFERPROC)m_glContext->getProcAddress("glBindBuffer");
    glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)m_glContext->getProcAddress("glBindBufferBase");
    glBindBufferRange = (PFNGLBINDBUFFERRANGEPROC)m_glContext->getProcAddress("glBindBufferRange");
    glBufferData = (PFNGLBUFFERDATAPROC)m_glContext->getProcAddress("glBufferData");
    glBufferSubData = (PFNGLBUFFERSUBDATAPROC)m_glContext->getProcAddress("glBufferSubData");
    glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)m_glContext->getProcAddress("glDeleteBuffers");
    glGenBuffers = (PFNGLGENBUFFERSPROC)m_glContext->getProcAddress("glGenBuffers");
    glGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC)m_glContext->getProcAddress("glGetActiveUniformBlockiv");
    glGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC)m_glContext->getProcAddress("glGetActiveUniformsiv");
    glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)m_glContext->getProcAddress("glGetUniformBlockIndex");
    glGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC)m_glContext->getProcAddress("glGetUniformIndices");
    glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)m_glContext->getProcAddress("glUniformBlockBinding");

    if(!glBindBuffer ||
            !glBindBufferBase ||
            !glBindBufferRange ||
            !glBufferData ||
            !glBufferSubData ||
            !glDeleteBuffers ||
            !glGenBuffers ||
            !glGetActiveUniformBlockiv ||
            !glGetActiveUniformsiv ||
            !glGetUniformBlockIndex ||
            !glGetUniformIndices ||
            !glUniformBlockBinding)
    {
        qDebug("Could not init function pointers");
        return false;
    }

    return true;
}

正如我接受的答案中所强调的,统一缓冲区对象具有关联的布局(http://www.opengl.org/wiki/Uniform_Buffer_Object),并且统一块必须“两次”链接(在此处阅读更多:http://arcsynthesis.org/gltut/Positioning/Tut07%20Shared%20Uniforms.html)。 / p>

希望这有帮助!