Qt3D绘制光照效果

发布时间 2023-09-22 10:55:09作者: 兜尼完

直接给出一个例子供参考。此例子参考了以下博文中公开的代码并做了整理:

这里把立方体换成了四面体,贴图也换成了顶点的颜色。并对代码结构做了简化,以使读者更容易看懂。有兴趣的读者可以调节软件中相机位置、EYE位置和视角方向(即调整lookAt(...)函数的参数)等观察这些参数对渲染结果的影响。本文开发环境是VS2017和Qt5.9。软件效果如下图:

头文件如下:

class QOpenGLTexture;
class QOpenGLBuffer;

class MwOpenGLWidget : public QOpenGLWidget, private QOpenGLFunctions
{
    Q_OBJECT

private:
    struct VertexData
    {
        QVector3D pos;
        QVector3D normal;
        QVector3D color;
    };

public:
    MwOpenGLWidget(QWidget* parent = 0);

private:
    void initializeGL() override;
    void paintGL() override;
    void resizeGL(int w, int h) override;

private:
    QOpenGLShaderProgram* program;
    QOpenGLBuffer* vertexBuff;
    QOpenGLBuffer* indexBuff;
    int iter;
};

CPP文件:

MwOpenGLWidget::MwOpenGLWidget(QWidget* parent) :
    QOpenGLWidget(parent), iter(0)
{
    QSurfaceFormat surface;
    surface.setSamples(4);
    setFormat(surface);

    QTimer* timer = new QTimer(this);
    connect(timer, &QTimer::timeout, this, QOverload<>::of(&MyOpenGLWidget::update));
    timer->setInterval(100);
    timer->start();
}

void MwOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);

    program = new QOpenGLShaderProgram(this);
    const char* vsrc = u8R"(
        #version 330 core
        layout (location = 0) in vec3 aPos; // 空间坐标
        layout (location = 1) in vec3 aNormal; // 法线向量
        layout (location = 2) in vec3 aColor; // 顶点颜色
        uniform mat4 modelMatrix;
        uniform mat4 mvpMatrix;
        out vec3 oNormal;
        out vec3 oFragPos;
        out vec3 oColor;

        void main()
        {
            oFragPos = vec3(modelMatrix * vec4(aPos, 1.0));
            oNormal =  mat3(transpose(inverse(modelMatrix))) * aNormal;
            oColor = aColor;
            gl_Position = mvpMatrix * vec4(aPos, 1.0);
        })";
    program->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);
    const char* fsrc = u8R"(
        #version 330 core
        uniform vec3 lightPos; 
        uniform vec3 viewPos; 
        uniform vec3 lightColor;
        in vec3 oNormal;
        in vec3 oFragPos;
        in vec3 oColor;

        void main()
        {
            // 环境光照
            float ambientStrength = 0.1;
            vec3 ambient = ambientStrength * lightColor;
            // 漫反射光照 
            vec3 norm = normalize(oNormal);
            vec3 lightDir = normalize(lightPos - oFragPos);
            float diff = max(dot(norm, lightDir), 0.0);
            vec3 diffuse = diff * lightColor;
            // 镜面光照
            float specularStrength = 1.0;
            vec3 viewDir = normalize(viewPos - oFragPos);
            vec3 reflectDir = reflect(-lightDir, norm);
            float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
            vec3 specular = specularStrength * spec * lightColor;
            // 把纹理中的颜色和光照参数相乘
            gl_FragColor = vec4((ambient + diffuse + specular) * oColor, 1.0);
        })";
    program->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);

    program->link();
    program->bind();

    VertexData vertices[] =
    {
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 0) },
        { QVector3D(0, 0, 0.5f), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 0) },
        { QVector3D(0, 0.707f, 0), QVector3D(-0.816f, 0.333f, 0.471f), QVector3D(0, 0, 1.0f) },

        { QVector3D(0, 0, 0.5f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(0.433f, 0, -0.25f), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(1.0f, 0, 1.0f) },
        { QVector3D(0, 0.707f, 0), QVector3D(0.816f, 0.333f, 0.471f), QVector3D(0, 1.0f, 1.0f) },

        { QVector3D(0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 0, 1.0f) },
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, 0.333f, -0.943f), QVector3D(0, 1.0f, 0) },
        { QVector3D(0, 0.707f, 0), QVector3D(0, 0.333f, -0.943f), QVector3D(1.0f, 0, 0) },

        { QVector3D(0, 0, 0.5f), QVector3D(0, -1.0f, 0), QVector3D(0, 1.0f, 1.0f) },
        { QVector3D(-0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 1.0f, 0) },
        { QVector3D(0.433f, 0, -0.25f), QVector3D(0, -1.0f, 0), QVector3D(1.0f, 0, 1.0f) },
    };
    GLushort indices[] =
    {
        0, 1, 2, 3, 4, 5,
        6, 7, 8, 9, 10, 11,
    };
    vertexBuff = new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
    vertexBuff->create();
    vertexBuff->bind();
    vertexBuff->allocate(vertices, 12 * sizeof(VertexData));
    indexBuff = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
    indexBuff->create();
    indexBuff->bind();
    indexBuff->allocate(indices, 12 * sizeof(float));

    int offset = 0;
    int vertexLocation = program->attributeLocation("aPos");
    program->enableAttributeArray(vertexLocation);
    program->setAttributeBuffer(vertexLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
    offset += sizeof(QVector3D);
    int normalLocation = program->attributeLocation("aNormal");
    program->enableAttributeArray(normalLocation);
    program->setAttributeBuffer(normalLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
    offset += sizeof(QVector3D);
    int colorLocation = program->attributeLocation("aColor");
    program->enableAttributeArray(colorLocation);
    program->setAttributeBuffer(colorLocation, GL_FLOAT, offset, 3, sizeof(VertexData));
}

#define LIGHT_COLOR QVector3D(0.9f, 1.0f, 2.0f) /* 这里的颜色值可以大于1不知道为什么 */
#define LIGHT_POS   QVector3D(2.5f, 2.8f, 2.0f)
#define EYE_CENTER  QVector3D(0.0, -1.4, 8.0)

void MwOpenGLWidget::paintGL()
{
    QMatrix4x4 projection;
    projection.perspective(45.0f, 1, 0.1f, 100.0f);
    QMatrix4x4 viewMatrix;
    viewMatrix.lookAt(EYE_CENTER, QVector3D(0, 2, -5), QVector3D(0, 1, 0));
    QMatrix4x4 modelMatrix;
    modelMatrix.translate(0, -0.8, 0); // 平移
    modelMatrix.rotate(iter, 0, 1); // 旋转
    modelMatrix.scale(4.0); // 缩放
    QMatrix4x4 mvpMatrix = projection * viewMatrix * modelMatrix;

    program->setUniformValue("lightColor", LIGHT_COLOR);
    program->setUniformValue("lightPos", LIGHT_POS);
    program->setUniformValue("viewPos", EYE_CENTER);
    program->setUniformValue("mvpMatrix", mvpMatrix);
    program->setUniformValue("modelMatrix", modelMatrix);
    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_SHORT, 0);
    iter++;
}

#undef LIGHT_COLOR
#undef LIGHT_POS
#undef EYE_CENTER

void MwOpenGLWidget::resizeGL(int w, int h)
{
}