目录

结果显示

材质介绍

函数解析

具体代码


结果显示

材质介绍

当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。现在,我们再添加一个反光度(Shininess)分量,结合上述的三个颜色,我们就有了全部所需的材质属性了。

为风氏光照模型的每个分量都定义一个颜色向量。ambient材质向量定义了在环境光照下这个表面反射的是什么颜色,通常与表面的颜色相同。diffuse材质向量定义了在漫反射光照下表面的颜色。漫反射颜色(和环境光照一样)也被设置为我们期望的物体颜色。specular材质向量设置的是表面上镜面高光的颜色(或者甚至可能反映一个特定表面的颜色)。最后,shininess影响镜面高光的散射/半径。有这4个元素定义一个物体的材质,我们能够模拟很多现实世界中的材质。devernay.free.fr中的一个表格展示了一系列材质属性,它们模拟了现实世界中的真实材质。下图展示了几组现实世界的材质参数值对我们的立方体的影响:

可以看到,通过正确地指定一个物体的材质属性,我们对这个物体的感知也就不同了。效果非常明显,但是要想获得更真实的效果,我们需要以更复杂的形状替换这个立方体。

函数解析

timerEvent(QTimerEvent *event)函数

initializeGL()函数绘制光源

顶点着色器

片段着色器 

环境光通常被认为是均匀地照亮场景的,所以直接用光源的环境光和材质的环境光相乘来简单表示。

漫反射光的强度取决于光线与表面法向量的夹角。通过归一化法向量和光线方向向量,然后计算它们的点积,就能得到光线与表面的夹角余弦值。夹角越小,漫反射光越强,所以用这个点积值来控制漫反射光的强度。

镜面反射光主要是模拟物体表面的高光效果。通过计算反射方向向量和观察方向向量的点积,并对结果进行幂运算,来模拟高光的集中和锐利程度。材质的 shininess 值越大,高光就越集中和锐利。

光源的绘制和前一次(基础光照)没有区别。 

paintGL()函数:传参进行绘制

具体代码

.h

#ifndef OPENGLWIDGET_H
#define OPENGLWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLTexture>
#include <QElapsedTimer>
#include "Camera.h"

QT_BEGIN_NAMESPACE
namespace Ui { class openGLWidget; }
QT_END_NAMESPACE

class openGLWidget : public QOpenGLWidget
{
    Q_OBJECT

public:
    openGLWidget(QWidget *parent = nullptr);
    ~openGLWidget();
protected:
    virtual void timerEvent(QTimerEvent *event) override;
    //鼠标事件
    virtual void enterEvent(QEnterEvent *event) override;
    virtual void leaveEvent(QEvent *event) override;
    virtual void mouseMoveEvent(QMouseEvent *event) override;
    virtual void wheelEvent(QWheelEvent *event) override;
    virtual void keyPressEvent(QKeyEvent *event) override;
    virtual void keyReleaseEvent(QKeyEvent *event) override;
    //初始化
    virtual void initializeGL() override;
    virtual void resizeGL(int w, int h) override;
    virtual void paintGL() override;

private:
    QOpenGLShaderProgram lightingShader;
    QOpenGLShaderProgram lightCubeShader;
    QOpenGLBuffer vbo;
    QOpenGLVertexArrayObject cubeVao;
    QOpenGLVertexArrayObject lightVao;
    QMatrix4x4 projection;
    QMatrix4x4 view;
    Camera camera {Camera(QVector3D(0.0f, 0.0f, 3.0f))};
    QVector3D lightPos {QVector3D(1.2f, 1.0f, 2.0f)};
    // 用 0 - 1 的值 直接表示颜色
    QVector3D diffuseColor;
    QVector3D ambientColor;
    QElapsedTimer time;
    float lastFrameTime {0.f};
    struct {
        bool W {false};
        bool S {false};
        bool A {false};
        bool D {false};
    } keys;

private:
    Ui::openGLWidget *ui;
};
#endif // OPENGLWIDGET_H

.cpp

#include "openGLWidget.h"
#include "./ui_openGLWidget.h"

#include <QOpenGLFunctions>
#include <QKeyEvent>
#include <QPainter>
#include <QtMath>

openGLWidget::openGLWidget(QWidget *parent)
    : QOpenGLWidget(parent)
    , ui(new Ui::openGLWidget)
{
    ui->setupUi(this);
    setMouseTracking(true);
}

openGLWidget::~openGLWidget()
{
    makeCurrent();
    lightVao.destroy();
    cubeVao.destroy();
    vbo.destroy();
    doneCurrent();
    delete ui;
}

void openGLWidget::timerEvent(QTimerEvent *event)
{
    float s = time.elapsed() / 1000.0;
    float delta = s - lastFrameTime;
    lastFrameTime = s;
    if (keys.W)
        camera.ProcessKeyboard(FORWARD, delta);
    if (keys.S)
        camera.ProcessKeyboard(BACKWARD, delta);
    if (keys.A)
        camera.ProcessKeyboard(LEFT, delta);
    if (keys.D)
        camera.ProcessKeyboard(RIGHT, delta);
    view = camera.GetViewMatrix();
    QVector3D lightColor(qSin(lastFrameTime * 2.0), qSin(lastFrameTime * 0.7), qSin(lastFrameTime * 1.3));
    diffuseColor = lightColor * 0.5;
    ambientColor = lightColor * 0.2;
    update();
}

void openGLWidget::enterEvent(QEnterEvent *event)
{
    // 隐藏鼠标指针,将指针置于窗口中心
    setCursor(Qt::BlankCursor);
    QCursor::setPos(mapToGlobal(rect().center()));
}

void openGLWidget::leaveEvent(QEvent *event)
{

}

void openGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    float xoffset = rect().center().x() - event->x();
    float yoffset = rect().center().y() - event->y();

    float sensitivity = 0.1f; // change this value to your liking
    xoffset *= sensitivity;
    yoffset *= sensitivity;

    camera.ProcessMouseMovement(xoffset, yoffset);

    // 将指针置于窗口中心
    QCursor::setPos(mapToGlobal(rect().center()));
}

void openGLWidget::wheelEvent(QWheelEvent *event)
{
    float f = event->angleDelta().y() > 0 ? 1.0f : -1.0f;
    camera.ProcessMouseScroll(f);
    projection.setToIdentity();
    projection.perspective(camera.Zoom, float(width()) / float(height()), 0.1f, 100.f);
}

void openGLWidget::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_W:
        keys.W = true;
        break;
    case Qt::Key_S:
        keys.S = true;
        break;
    case Qt::Key_A:
        keys.A = true;
        break;
    case Qt::Key_D:
        keys.D = true;
        break;
    default:
        return;
    }
}

void openGLWidget::keyReleaseEvent(QKeyEvent *event)
{
    switch(event->key()) {
    case Qt::Key_W:
        keys.W = false;
        break;
    case Qt::Key_S:
        keys.S = false;
        break;
    case Qt::Key_A:
        keys.A = false;
        break;
    case Qt::Key_D:
        keys.D = false;
        break;
    default:
        return;
    }
}

void openGLWidget::initializeGL()
{
    // 设置用来清空屏幕的颜色 这里设置为黑色
    QOpenGLFunctions *f = context()->functions();
    f->glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

    lightingShader.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, R"(
        #version 330 core
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec3 aNormal;
        out vec3 vFragPos;
        out vec3 vNormal;
        uniform mat4 uProjection;
        uniform mat4 uView;
        uniform mat4 uModel;

        void main()
        {
            vFragPos = vec3(uModel * vec4(aPos, 1.0));
            vNormal = mat3(transpose(inverse(uModel))) * aNormal;

            gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
        }
    )");
    // 片段着色器
    lightingShader.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, R"(
        #version 330 core
        out vec4 FragColor;

        struct Material {
            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
            float shininess;
        };

        struct Light {
            vec3 position;
            vec3 ambient;
            vec3 diffuse;
            vec3 specular;
        };

        in vec3 vNormal;
        in vec3 vFragPos;

        uniform vec3 uViewPos;
        uniform Material uMaterial;
        uniform Light uLight;

        void main()
        {
            // ambient
            vec3 ambient = uLight.ambient * uMaterial.ambient;

            // diffuse
            vec3 norm = normalize(vNormal);
            vec3 lightDir = normalize(uLight.position - vFragPos);
            float diff = max(dot(norm, lightDir), 0.0);
            vec3 diffuse = uLight.diffuse * (diff * uMaterial.diffuse);

            // specular
            vec3 viewDir = normalize(uViewPos - vFragPos);
            vec3 reflectDir = reflect(-lightDir, norm);
            float spec = pow(max(dot(viewDir, reflectDir), 0.0), uMaterial.shininess);
            vec3 specular = uLight.specular * (spec * uMaterial.specular);

            vec3 result = ambient + diffuse + specular;
            FragColor = vec4(result, 1.0);
        }
    )");
    // 编译链接
    if(!lightingShader.link()) {
        qDebug() << lightingShader.log();
    };

    //光源
    lightCubeShader.addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, R"(
        #version 330 core
        layout (location = 0) in vec3 aPos;

        uniform mat4 uModel;
        uniform mat4 uView;
        uniform mat4 uProjection;

        void main()
        {
            gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
        }
    )");

    lightCubeShader.addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, R"(
        #version 330 core
        out vec4 FragColor;

        void main()
        {
            FragColor = vec4(1.0); // set all 4 vector values to 1.0
        }
    )");
    // 编译链接
    if(!lightCubeShader.link()) {
        qDebug() << lightCubeShader.log();
    };

    // 顶点数据
    float vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f,  1.0f,

        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,

        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,

        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f
    };
    // 创建VBO
    vbo.create();
    vbo.bind();
    vbo.allocate(vertices, sizeof(vertices));

    cubeVao.create();
    cubeVao.bind();
    lightingShader.enableAttributeArray(0);
    lightingShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
    lightingShader.enableAttributeArray(1);
    lightingShader.setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float));

    lightVao.create();
    lightVao.bind();
    lightCubeShader.enableAttributeArray(0);
    lightCubeShader.setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
    startTimer(1);
    time.start();
}

void openGLWidget::resizeGL(int w, int h)
{
    QOpenGLFunctions *f = context()->functions();
    f->glViewport(0, 0, w, h);
    projection.setToIdentity();
    projection.perspective(camera.Zoom, float(w) / float(h), 0.1f, 100.f);
}

void openGLWidget::paintGL()
{
    QOpenGLFunctions* f = context()->functions();
    f->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // 启用深度测试
    f->glEnable(GL_DEPTH_TEST);

    lightingShader.bind();
    lightingShader.setUniformValue("uViewPos", camera.Position);
    lightingShader.setUniformValue("uLight.position", lightPos);
    lightingShader.setUniformValue("uLight.ambient", ambientColor);
    lightingShader.setUniformValue("uLight.diffuse", diffuseColor);
    lightingShader.setUniformValue("uLight.specular", 1.0f, 1.0f, 1.0f);

    lightingShader.setUniformValue("uMaterial.ambient", 1.0f, 0.5f, 0.31f);
    lightingShader.setUniformValue("uMaterial.diffuse", 1.0f, 0.5f, 0.31f);
    lightingShader.setUniformValue("uMaterial.specular", 0.5f, 0.5f, 0.5f);
    lightingShader.setUniformValue("uMaterial.shininess", 32.0f);

    lightingShader.setUniformValue("uProjection", projection);
    lightingShader.setUniformValue("uView", view);
    lightingShader.setUniformValue("uModel", QMatrix4x4());
    cubeVao.bind();
    f->glDrawArrays(GL_TRIANGLES, 0, 36);

    lightCubeShader.bind();
    lightCubeShader.setUniformValue("uProjection", projection);
    lightCubeShader.setUniformValue("uView", view);
    QMatrix4x4 model;
    model.translate(lightPos);
    model.scale(0.2f);
    lightCubeShader.setUniformValue("uModel", model);
    lightVao.bind();
    f->glDrawArrays(GL_TRIANGLES, 0, 36);
}

Camera.h参考前一篇

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部