目录
结果显示
材质介绍
当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(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参考前一篇