目录
结果显示
材质介绍
函数解析
具体代码
结果显示
材质介绍
当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(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_NAMESPACEclass openGLWidget : public QOpenGLWidget
{Q_OBJECTpublic: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 likingxoffset *= 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 corelayout (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 coreout 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(){// ambientvec3 ambient = uLight.ambient * uMaterial.ambient;// diffusevec3 norm = normalize(vNormal);vec3 lightDir = normalize(uLight.position - vFragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 diffuse = uLight.diffuse * (diff * uMaterial.diffuse);// specularvec3 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 corelayout (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 coreout 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};// 创建VBOvbo.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参考前一篇