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++ host program:
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):
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
):
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 ".
According to the Qt documentation, it should not be possible to use a tuple size of 6 (only 1, 2, 3, 4 are allowed). On the other side, QGLShaderProgram allows to pass in a matrix with up to 4x4 entries. GLSL also allows structs for uniforms which have more than 4 floats in them ( according to this example ).
This leads to the question:
How can I pass structs of floats / vectors as a uniform array using 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.
Of course I tried the code above anyway. It results in all vectors in the struct being set to 0.
When I drop the second parameter in the struct and use a tuple size of 3, the code still does not work! However, when just using vec3
in the shader (still the struct with one QVector3D in the host program, tuple size = 3), it works.
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. I currently use two uniform arrays (one for the pos, one for the intensity) as a workaround.
Qt has nothing to do with this.
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());
...
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.