目录
结果显示
材质介绍
当描述一个表面时,我们可以分别为三个光照分量定义一个材质颜色(Material Color):环境光照(Ambient Lighting)、漫反射光照(Diffuse Lighting)和镜面光照(Specular Lighting)。通过为每个分量指定一个颜色,我们就能够对表面的颜色输出有细粒度的控制了。现在,我们再添加一个反光度(Shininess)分量,结合上述的三个颜色,我们就有了全部所需的材质属性了。
为风氏光照模型的每个分量都定义一个颜色向量。ambient材质向量定义了在环境光照下这个表面反射的是什么颜色,通常与表面的颜色相同。diffuse材质向量定义了在漫反射光照下表面的颜色。漫反射颜色(和环境光照一样)也被设置为我们期望的物体颜色。specular材质向量设置的是表面上镜面高光的颜色(或者甚至可能反映一个特定表面的颜色)。最后,shininess影响镜面高光的散射/半径。有这4个元素定义一个物体的材质,我们能够模拟很多现实世界中的材质。devernay.free.fr中的一个表格展示了一系列材质属性,它们模拟了现实世界中的真实材质。下图展示了几组现实世界的材质参数值对我们的立方体的影响:
可以看到,通过正确地指定一个物体的材质属性,我们对这个物体的感知也就不同了。效果非常明显,但是要想获得更真实的效果,我们需要以更复杂的形状替换这个立方体。
函数解析
timerEvent(QTimerEvent *event)函数:
**initializeGL()函数:**绘制光源
顶点着色器
片段着色器
环境光通常被认为是均匀地照亮场景的,所以直接用光源的环境光和材质的环境光相乘来简单表示。
漫反射光的强度取决于光线与表面法向量的夹角。通过归一化法向量和光线方向向量,然后计算它们的点积,就能得到光线与表面的夹角余弦值。夹角越小,漫反射光越强,所以用这个点积值来控制漫反射光的强度。
镜面反射光主要是模拟物体表面的高光效果。通过计算反射方向向量和观察方向向量的点积,并对结果进行幂运算,来模拟高光的集中和锐利程度。材质的 shininess 值越大,高光就越集中和锐利。
光源的绘制和前一次(基础光照)没有区别。
paintGL()函数:传参进行绘制
具体代码
.h
cpp
#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
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参考前一篇