简体   繁体   English

使用QGLShaderProgram将自定义类型(结构)从Qt传递到GLSL

[英]Passing custom type (struct) uniform from Qt to GLSL using QGLShaderProgram

I defined a struct for light parameters which contains two vectors. 我为包含两个向量的光照参数定义了一个结构。 The struct is defined in both C++ and GLSL in an analogous way (note: QVector3D encapsulates 3 float s, not double s): 在C ++和GLSL中都以类似的方式定义了该结构(注意: QVector3D封装了3个float而不是double ):

C++ host program: C ++主机程序:

struct LightParameters {
    QVector3D pos;
    QVector3D intensity;
};

Fragment Shader: 片段着色器:

struct LightParameters {
    vec3 pos;
    vec3 intensity;
};

In the fragment shader, I also define the following uniforms. 在片段着色器中,我还定义了以下制服。 The numbers of lights is limited to 8, so the uniform array has a constant size (but only numLights are actually used): 灯光的数量限制为8,因此统一数组的大小是恒定的(但实际上仅使用numLights ):

const int maxLights = 8;
uniform int numLights;
uniform LightParameters lights[maxLights];

In the host program, I define the lights and pass them to the shader ( QGLShaderProgram ): 在主机程序中,我定义了灯光并将它们传递给着色器( QGLShaderProgram ):

static const int maxLights = 8;
LightParameters lights[maxLights];

int numLights = 1;
lights[0].pos = {0, 1, 0};
lights[0].intensity = {1, 1, 1};

shaderProgram->bind();
shaderProgram->setUniformValue("numLights", numLights);
shaderProgram->setUniformValueArray("lights", reinterpret_cast<GLfloat*>(lights), maxLights, 6);
shaderProgram->release();

The 6 in the last parameter should indicate that each array element uses 6 GLfloat s from the raw float array, aka " tuple size ". 最后一个参数中的6应该指示每个数组元素使用原始float数组中的6个GLfloat ,也就是“ 元组大小 ”。

According to the Qt documentation, it should not be possible to use a tuple size of 6 (only 1, 2, 3, 4 are allowed). 根据Qt文档,应该不可能使用6的元组大小(仅允许使用1、2、3、4)。 On the other side, QGLShaderProgram allows to pass in a matrix with up to 4x4 entries. 另一方面,QGLShaderProgram允许传入具有最多4x4条目的矩阵。 GLSL also allows structs for uniforms which have more than 4 floats in them ( according to this example ). GLSL还允许其中包含超过4个浮点的制服结构( 根据此示例 )。

This leads to the question: 这导致了一个问题:

How can I pass structs of floats / vectors as a uniform array using Qt? 如何使用Qt将浮点数/向量的结构作为统一数组传递? If it's not possible using Qt, I will call the GL function to do this, but I'm interested if Qt can do this for me conveniently. 如果无法使用Qt,我将调用GL函数来执行此操作,但是我对Qt能否为我方便地执行此操作感兴趣。

Of course I tried the code above anyway. 当然我还是尝试了上面的代码。 It results in all vectors in the struct being set to 0. 结果将结构中的所有向量都设置为0。

When I drop the second parameter in the struct and use a tuple size of 3, the code still does not work! 当我将第二个参数放在结构中并使用元组大小3时,代码仍然无法正常工作! However, when just using vec3 in the shader (still the struct with one QVector3D in the host program, tuple size = 3), it works. 但是,仅在着色器中使用vec3时(仍在主机程序中使用一个QVector3D的结构,元组大小= 3),它就可以工作。

So the problem seems to be that custom types are handled differently in the OpenGL host program API, but Qt doesn't seem to support it. 因此,问题似乎在于自定义类型在OpenGL主机程序API中的处理方式有所不同,但Qt似乎不支持它。 I currently use two uniform arrays (one for the pos, one for the intensity) as a workaround. 我目前使用两个统一的数组(一个用于pos,一个用于强度)作为解决方法。

Qt has nothing to do with this. Qt与此无关。

You cannot pass arrays of structs as though it were an array of values . 就像它是的数组,你不能传递结构的阵列。 If you have a uniform array whose type is not a basic type , then every array index and every member of that struct has a separate uniform location with a separate uniform name . 如果您有一个类型不是基本类型的统一数组,则每个数组索引和该结构的每个成员都具有一个单独的统一位置,并具有一个单独的统一名称 Therefore, you must pass each member separately . 因此,你必须分别通过每一个成员。

You must do this: 您必须这样做:

shaderProgram->setUniformValue("lights[0].pos", lights[0].pos);
shaderProgram->setUniformValue("lights[0].intensity", lights[0].intensity);
shaderProgram->setUniformValue("lights[1].pos", lights[1].pos);
shaderProgram->setUniformValue("lights[1].intensity", lights[1].intensity);
shaderProgram->setUniformValue("lights[2].pos", lights[2].pos);
shaderProgram->setUniformValue("lights[2].intensity", lights[2].intensity);

And so forth. 依此类推。

Or you could just use a proper uniform block and dispense with all of that. 或者,您可以使用适当的统一块并省去所有这些。

I use a template function: 我使用模板功能:

template<class T> void setUniformArrayValue(QOpenGLShaderProgram *program,
                                       const QString& arrayName,
                                       const QString& varName,
                                       int index,
                                       const T& value)
{
    const char* name = QString("%1[%2].%3")
            .arg(arrayName)
            .arg(index)
            .arg(varName)
            .toStdString().c_str();
    program->setUniformValue(name, value);
}

and in the program : 并在程序中:

for (int i = 0; i < m_lights.size(); i++) {
    setUniformArrayValue<QColor>(program, "lights", "ambient", i, m_lights[i].ambient());
    ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM