qt + opengl 给立方体增加阴影
在前几篇文章里面学会了通过opengl实现一个立方体,那么这篇我们来学习光照。
风氏光照模型的主要结构由3个分量组成:环境(Ambient)、漫反射(Diffuse)和镜面(Specular)光照。下面这张图展示了这些光照分量看起来的样子:
1 环境光照(Ambient Lighting):即使在黑暗的情况下,世界上通常也仍然有一些光亮(月亮、远处的光),所以物体几乎永远不会是完全黑暗的。为了模拟这个,我们会使用一个环境光照常量,它永远会给物体一些颜色。
2 漫反射光照(Diffuse Lighting):模拟光源对物体的方向性影响(Directional Impact)。它是风氏光照模型中视觉上最显著的分量。物体的某一部分越是正对着光源,它就会越亮。
3 镜面光照(Specular Lighting):模拟有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。
我们运行下结果如下:
1 我们设置顶点着色器
#version 330 core
//顶点着色器
uniform mat4 mvp_matrix;
uniform mat4 model_matrix;
layout (location = 0) in vec3 a_position; //空间坐标
layout (location = 1) in vec3 aColor; //颜色
layout (location = 2) in vec2 a_texcoord; //纹理
out vec3 FragPos;
out vec2 outtexcoord;
out vec3 outclolor;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 Normal;
void main()
{
gl_Position = projection*view* model * vec4(a_position, 1.0);
outtexcoord = a_texcoord;
outclolor = aColor;
}
在设置片段着色器
#version 330 core
//像素着色器
in vec2 outtexcoord;
in vec3 outclolor;
out vec4 FragColor;
in vec3 FragPos;
in vec3 Normal;
uniform vec3 lightPos;
uniform vec3 viewPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform sampler2D texture;
void main()
{
//ambient 环境光照
float ambientStrength = 0.1; //环境因子
vec3 ambient = ambientStrength * lightColor;//环境因子*光照颜色
vec3 result = ambient * objectColor;//环境因子*光照位置*物体颜色
FragColor = vec4(result, 1.0);
}
上立方体代码
#include "widget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>
#include <QDateTime>
#include <QtMath>
#include "ui_widget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>
//GLfloat light_ambient[4]={0.5, 0.5, 0.5, 1.0};
//GLfloat light_diffuse[4]={1.0, 1.0, 1.0, 1.0};
//GLfloat light_position[4]={0.0, 0.0, 2.0, 0.0};
GLfloat LightAmbient[4] = {0.5f, 0.5f, 0.5f, 1.0f}; //环境光参数
GLfloat LightDiffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; //漫散光参数
GLfloat LightPosition[4] = {0.0f, 0.0f, 2.0f, 1.0f}; //光源位置
//模型主动刷新帧率
#define ACTION_FPS 60
#define LIGHT_COLOR QVector3D(1.2f, 1.0f, 2.0f)
#define EYE_CENTER QVector3D(0.0, 0.0, 3.0)
#define LIGHT_POS QVector3D(2.5f, 2.0f, -1.0f)
static const char *vertexShaderSource =
"#version 330\n"
"layout (location = 0) in vec4 vertex;\n"
"layout (location = 1) in vec4 texCoord;\n"
"out vec4 texc;\n"
"out vec3 ourColor;\n"
"uniform mat4 model;\n"
"void main(void)\n"
"{\n"
" gl_Position = model * vertex;\n"
" texc = texCoord;\n"
"}\n";
static const char *fragmentShaderSource =
"#version 330\n"
"uniform sampler2D texture;\n"
"in vec3 ourColor;\n"
"in vec4 texc;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = texture2D(texture, texc.st)* vec4(1.0f, 0.5f, 0.5f, 1.0);\n"
"}\n";
static const char *vertex1ShaderSource =
"#version 330\n"
"layout (location = 0) in vec4 aPos;\n"
"layout (location = 1) in vec4 aColor;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"out vec4 FragPos;\n"
"out vec4 Normal;\n"
"void main(void)\n"
"{\n"
//" FragPos = vec3(model * vec4(aPos, 1.0));\n"
// " Normal = mat3(transpose(inverse(model))) * aNormal;\n"
" gl_Position = projection * view * model * aPos;\n"
"}\n";
static const char *vertex2ShaderSource =
"#version 330\n"
"layout (location = 0) in vec4 aPos;\n"
"out vec3 result;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"uniform vec3 objectColor;\n"
"uniform vec3 lightColor;\n"
"uniform float ambientStrength;\n"
"void main(void)\n"
"{\n"
" gl_Position = projection * view * model * aPos;\n"
" vec3 ambient = ambientStrength * lightColor;\n"
" result = ( ambient ) * objectColor;\n"
"}\n";
static const char *fragment1ShaderSource =
"#version 330\n"
"in vec3 FragPos;\n"
"in vec3 Normal;\n"
"uniform vec3 lightPos;\n"
"uniform vec3 viewPos;\n"
"uniform vec3 objectColor;\n"
"uniform vec3 lightColor;\n"
"void main(void)\n"
"{\n"
// ambient
" float ambientStrength = 0.1;\n"
" vec3 ambient = ambientStrength * lightColor;\n"
// diffuse
" vec3 norm = normalize(Normal);\n"
" vec3 lightDir = normalize(lightPos - FragPos);\n"
" float diff = max(dot(norm, lightDir), 0.0);\n"
" vec3 diffuse = diff * lightColor; \n"
// specular
" float specularStrength = 0.5;\n"
" vec3 viewDir = normalize(viewPos - FragPos);\n"
" vec3 reflectDir = reflect(-lightDir, norm);\n"
" float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);\n"
" vec3 specular = specularStrength * spec * lightColor;\n"
" vec3 result = (ambient + diffuse + specular) * objectColor;\n"
" gl_FragColor = vec4(result, 1.0);\n"
"}\n";
static const char *vertexLight1Source =
"#version 330\n"
"layout (location = 0) in vec4 aPos;\n"
"layout (location = 1) in vec4 aColor;\n"
"uniform mat4 matrix;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"void main(void)\n"
"{\n"
" gl_Position = projection * view * matrix * aPos;\n"
"}\n";
// " gl_FragColor = texture2D(texture, texc.st) *vec4(1.0f, 0.5f, 0.5f, 1.0);\n"
static const char *fragmentLight1Source =
"#version 330\n"
"out vec4 FragColor;\n"
"void main(void)\n"
"{\n"
"FragColor = vec4(1.0);\n"
"}\n";
//static const char *vertexLightSource =
// "#version 330\n"
// "layout (location = 0) in vec4 aPos;\n"
// "layout (location = 1) in vec4 aTexCord;\n"
// "uniform mat4 matrix;\n"
// "uniform mat4 view;\n"
// "uniform mat4 projection;\n"
// "out vec4 texc;\n"
// "void main(void)\n"
// "{\n"
// " gl_Position = projection * view * matrix * aPos;\n"
// " texc = aTexCord;\n"
// "}\n";
" gl_FragColor = texture2D(texture, texc.st) *vec4(1.0f, 0.5f, 0.5f, 1.0);\n"
//static const char *fragmentLightSource =
// "#version 330\n"
// "out vec4 FragColor;\n"
// "uniform sampler2D texture;\n"
// "in vec4 texc;\n"
// "uniform vec3 objectColor;\n"
// "uniform vec3 lightColor;\n"
// "void main(void)\n"
// "{\n"
// "gl_FragColor = vec4(lightColor * objectColor, 1.0);\n"
// "}\n";
Widget::Widget():QOpenGLWidget()
,m_xRos(0)
,m_yRos(0)
{
cam = QVector3D(m_xRos, m_yRos, m_zRos);
cameraPos = QVector3D(0, 0, 3);
timer = new QTimer;
timer->setInterval(20);
connect(timer,&QTimer::timeout,this,[=]{
qDebug()<<"timeout";
// m_xRos+=30;
// m_yRos+=30;
//rotateBy(2 * 16, +2 * 16, -1 * 16);
rotateBy(10 * 16, +10 * 16, -1 * 16);
});
timer->start();
}
Widget::~Widget()
{
makeCurrent();
vbo.destroy();
for (int i = 0; i < 6; ++i)
delete textures[i];
delete program;
doneCurrent();
}
QSize Widget::minimumSizeHint() const
{
return QSize(400, 400);
}
QSize Widget::sizeHint() const
{
return QSize(400, 400);
}
void Widget::rotateBy(int xAngle, int yAngle, int zAngle)
{
xRot += xAngle;
yRot += yAngle;
zRot += zAngle;
update();
timer->stop();
}
void Widget::setClearColor(const QColor &color)
{
clearColor = color;
update();
}
void Widget::initializeGL()
{
/*vertices = {
// ---- 位置---- - 纹理坐标 - ---- 颜色 ----
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,
};*/
vertices = {
// ---- 位置---- - 纹理坐标 - ---- 颜色 ----
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//1
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//2
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//3
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//4
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//5
-0.5f, -0.5f, 0.5f,1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f,0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f,
};
GLushort indices[36] =
{
/**
* 每两个三角形渲染一个面
* 注意节点顺序,因为开启了遮挡剔除(glEnable(GL_CULL_FACE)),opengl是根据顶点顺序决定三角形法线方向的,顺时针顺序算出来
* 三角形是朝里的就不画了,所以 0 3 1 会导致该三角形不显示,后面的三角形同样的道理注意顶点顺序
*/
//Face 0
0, 1 , 3, //triangle1
2, 0, 3, //triangle2
//Face 1
4, 5, 7, //triangle3
6, 4, 7, //triangle4
//Face 2
8, 9, 11, //triangle5
10, 8, 11, //triangle6
//Face 3
12, 13, 15, //triangle7
14, 12, 15, //triangle8
//Face 4
16, 17, 19, //triangle9
18, 16, 19, //triangle10
//Face 5
20, 21, 23, //triangle11
22, 20, 23, //triangle12
};
initializeOpenGLFunctions();
// makeObject();
//#define PROGRAM_VERTEX_ATTRIBUTE 0
//#define PROGRAM_TEXCOORD_ATTRIBUTE 1
// lightinhProgram = new QOpenGLShaderProgram;
// lightinhProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Diffuse.vs");
// lightinhProgram->addShaderFromSourceFile(QOpenGLShader::Fragment,":/Diffuse.fs");
// lightinhProgram->link();
// lightinhProgram->bind();//激活Program对象
program = new QOpenGLShaderProgram;
program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vs");
program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/shader.fs");
program->link();
program->bind();//激活Program对象
vbo.create();
vbo.bind(); //绑定到当前的OpenGL上下文,
vbo.allocate(vertices.constData(), vertices.count() * sizeof(GLfloat));
//初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
vao.create();
vao.bind();
// indexBuf.create();
// indexBuf.bind();
// indexBuf.allocate(indices, 36 * sizeof(GLushort));
for (int j = 0; j < 6; ++j)
{
textures[j] = new QOpenGLTexture(QImage(QString(":/cube%1.png").arg(j+1)).mirrored());
textures[j]->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
textures[j]->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);
textures[j]->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
}
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8 * sizeof(float)); //设置aPos顶点属性
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 8 * sizeof(float)); //设置aColor顶点颜色
program->setAttributeBuffer(2, GL_FLOAT, 6 * sizeof(float), 2, 8 * sizeof(float)); //设置aColor顶点颜色
program->enableAttributeArray(0); //使能aPos顶点属性
program->enableAttributeArray(1); //使能aColor顶点颜色
program->enableAttributeArray(2); //使能aColor顶点颜色
program->setUniformValue("texture", 0);
// lightinhProgram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 8 * sizeof(float)); //设置aPos顶点属性
// lightinhProgram->enableAttributeArray(0); //使能aPos顶点属性
projection.setToIdentity();
projection.perspective(45,(float)width()/height(),2.0,45.0);//构建透视矩阵,这个可以是固定写法
//projection.perspective(45,(float)width()/height(),0.1,100);//构建透视矩阵,这个可以是固定写法
program->setUniformValue("projection", projection);
//program1->setUniformValue("projection", projection);
//program->setUniformValue("texture", 0);
// vao.release();
// vbo.release();
}
void Widget::paintGL()
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); //设置清屏颜色
// glClearColor(0.1f,0.5f,0.7f,1.0f); //设置清屏颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//开启深度测试
glEnable(GL_DEPTH_TEST);
//开启遮挡剔除
glEnable(GL_CULL_FACE);
QMatrix4x4 m;
m.translate(0.0f, 0.0f, 0.0f);
m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
m.scale(0.5);
QMatrix4x4 modelmatrix;
//平移至左下角
modelmatrix.translate(0.0f, 0.0f, 0.0f);
//鼠标滚动旋转角度
modelmatrix.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
modelmatrix.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
modelmatrix.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
//滚轮缩放
modelmatrix.scale(0.5);
program->setUniformValue("model", modelmatrix);
QMatrix4x4 view;
view.lookAt(EYE_CENTER, QVector3D(0, 0, -20), QVector3D(0, 1, 0));
program->setUniformValue("view", view);
program->setUniformValue("lightColor", QVector3D(1.0f, 1.0f, 1.0f));//灯光颜色
program->setUniformValue("objectColor", QVector3D(1.0f, 0.5f, 0.3f));//物体颜色
for (int i = 0; i < 6; ++i) {
textures[i]->bind();
glDrawArrays(GL_TRIANGLE_FAN, i * 4, 4);
}
}
void Widget::resizeGL(int width, int height)
{
this->glViewport(0,0,width,height); //定义视口区域
}
void Widget::mousePressEvent(QMouseEvent *event)
{
lastPos = event->pos();
}
void Widget::mouseMoveEvent(QMouseEvent *event)
{
int dx = event->x() - lastPos.x();
int dy = event->y() - lastPos.y();
if (event->buttons() & Qt::LeftButton) {
rotateBy(8 * dy, 8 * dx, 0);
} else if (event->buttons() & Qt::RightButton) {
rotateBy(8 * dy, 0, 8 * dx);
}
lastPos = event->pos();
}
void Widget::mouseReleaseEvent(QMouseEvent * /* event */)
{
// emit clicked();
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QOpenGLWidget>
#include <QOpenGLExtraFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include <QOpenGLTexture>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
namespace Ui {
class Widget;
}
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
class Widget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
//using QOpenGLWidget::QOpenGLWidget;
Widget();
~Widget();
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
void rotateBy(int xAngle, int yAngle, int zAngle);
void setClearColor(const QColor &color);
void drawLight();
signals:
// void clicked();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
private:
Ui::Widget *ui;
QColor clearColor = Qt::black;
QPoint lastPos;
int xRot = 0;
int yRot = 0;
int zRot = 0;
QOpenGLTexture *textures[6] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
QOpenGLShaderProgram *program = nullptr;
QOpenGLShaderProgram *lightinhProgram = nullptr;
QOpenGLBuffer vbo;
QVector<float> vertices;
QOpenGLVertexArrayObject vao;
QOpenGLVertexArrayObject vao1;
QTimer* timer;
float m_xRos = 0.0f;
float m_yRos = 0.0f;
float m_zRos = 3.0f;
QVector3D cam;
int nCount=0;
QVector3D cameraPos;
QVector3D cameraTarget;
QVector3D cameraDirection;
float m_viewAngle=0.0f;
float radius = 10.0f;
float camX= 10.0f;
float camZ= 10.0f;
QVector3D _cameraPos = {0.0f, 0.0f, 3.0f};
QVector3D _cameraFront = {0.0f, 0.0f, -1.0f};
QVector3D _cameraUp = {0.0f, 1.0f, 0.0f};
float ambientStrength = 0.0;//关照强度
QMatrix4x4 projection;
QOpenGLTexture *texture;
QOpenGLBuffer indexBuf;
};
#endif // WIDGET_H
运行下结果如下:
我们可以看到物体并全黑,还是可以看到物体的轮廓,为什么变黑呢?变黑的原因:1.无光源2无
法线。
二 漫反射
环境光照本身不能提供最有趣的结果,但是漫反射光照就能开始对物体产生显著的视觉影响了。漫反射光照使物体上与光线方向越接近的片段能从光源处获得更多的亮度。为了能够更好的理解漫反射光照,请看下图:
图左上方有一个光源,它所发出的光线落在物体的一个片段上。我们需要测量这个光线是以什么角度接触到这个片段的。如果光线垂直于物体表面,这束光对物体的影响会最大化(更亮)。为了测量光线和片段的角度,我们使用一个叫做法向量(Normal Vector)的东西,它是垂直于片段表面的一个向量(这里以黄色箭头表示),我们在后面再讲这个东西。这两个向量之间的角度很容易就能够通过点乘计算出来。
法向量是一个垂直于顶点表面的(单位)向量。由于顶点本身并没有表面(它只是空间中一个独立的点),我们利用它周围的顶点来计算出这个顶点的表面。我们能够使用一个小技巧,使用叉乘对立方体所有的顶点计算法向量,但是由于3D立方体不是一个复杂的形状,所以我们可以简单地把法线数据手工添加到顶点数据中。更新后的顶点数据数组可以在这里找到。试着去想象一下,这些法向量真的是垂直于立方体各个平面的表面的(一个立方体由6个平面组成)。
(修改顶点着色器)
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
out vec3 Normal;
out vec3 FragPos;
out vec4 OutTextcord;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
FragPos = vec3(model*vec4(aPos,1.0));
Normal = aNormal;
}
修改片段着色器
#version 330 core
out vec4 FragColor;
in vec3 Normal;
in vec3 FragPos;
in vec4 OutTextcord;
uniform vec3 viewPos;
uniform vec3 lightPos;
uniform vec3 lightColor;
uniform vec3 objectColor;
uniform sampler2D texture;
void main()
{
float ambientStrength = 0.1;
vec3 ambient = ambientStrength * lightColor;
// diffuse
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(lightPos - FragPos);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * lightColor;
// specular
float specularStrength = 0.5;
vec3 viewDir = normalize(viewPos - FragPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 specular = specularStrength * spec * lightColor;
vec3 result = (ambient + diffuse + specular) * objectColor;
FragColor = vec4(result, 1.0);
}
#include "testshadewidget.h"
#define EYE_CENTER QVector3D(0.0, 0.0, 3.0)
testshadeWidget::testshadeWidget():QOpenGLWidget()
,m_xRos(0)
,m_yRos(0)
,m_zRos(0)
,lightPos(1.0, 1.0, 0.0)
,m_verticalAngle(45)
{
cam = QVector3D(m_xRos, m_yRos, m_zRos);
cameraPos = QVector3D(0, 0, 3);
timer = new QTimer;
timer->setInterval(100);
connect(timer,&QTimer::timeout,this,[=]{
qDebug()<<"timeout";
// m_xRos+=30;
// m_yRos+=30;
//rotateBy(2 * 16, +2 * 16, -1 * 16);
/*if(nDerection==0) //RIGHT
{
m_xRos += 0.05f;
m_yRos = 0;
m_zRos = 0;
if(m_xRos>=1.0f)
{
m_xRos = 0.0f;
timer->stop();
}
}
else if(nDerection==1)//LEFT
{
m_xRos -= 0.05f;
m_yRos = 0;
m_zRos = 0;
if(m_xRos<=-1.0f)
{
m_xRos = 0.0f;
timer->stop();
}
}
else if(nDerection==2)//DOWN
{
m_xRos = 0;
m_yRos -= 0.05f;
m_zRos = 0;
if(m_yRos<=-1.0f)
{
m_yRos = 0.0f;
timer->stop();
}
}
else if(nDerection==3)//UP
{
m_xRos = 0;
m_yRos += 0.05f;
m_zRos = 0;
if(m_yRos>=1.0f)
{
m_yRos = 0.0f;
timer->stop();
}
}
update();
// offsetBy(10 * 16, +10 * 16, -1 * 16);*/
rotateBy(10 * 16, +10 * 16, -1 * 16);
});
timer->start();
}
testshadeWidget::~testshadeWidget()
{
}
void testshadeWidget::initializeGL()
{
float vertices22[] = {
// ---- 顶点 ---- - 法向量 -
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,//1 正面 -z垂直
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,//2上面 垂直
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,//3右边 x
-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,//4 左边 -x
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,//5 后面 z
-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,//6下面-y
};
float vertices11[] = {
// ---- 顶点 ---- - 法向量 -
// ---- 顶点 ---- - 法向量 - - 纹理 -
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f,0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f,0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f,0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f,1.0f, //1 正面 -z垂直
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f,0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f,1.0f, //2上面 垂直
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f,0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f,0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f,0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f,1.0f, //3右边 x
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f,0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f,0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f,0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f,1.0f, //4 左边 -x
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f,0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f,0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,0.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f,1.0f, //5 后面 z
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f,0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f,0.0f,
0.5f, 0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f,0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f,1.0f, //6下面-y
};
vertices = {
// ---- 位置---- - 纹理坐标 - ---- 颜色 ----
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//1
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//2
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//3
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//4
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//5
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f,//6
};
//绑定OpenGL函数指针?类似GLAD库的作用?
initializeOpenGLFunctions();
//开启深度测试
glEnable(GL_DEPTH_TEST);
lightprogram = new QOpenGLShaderProgram;
lightprogram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/base_lighting.vs");
lightprogram->addShaderFromSourceFile(QOpenGLShader::Fragment,":/base_lighting.fs");
lightprogram->link();
lightprogram->bind();//激活Program对象
program = new QOpenGLShaderProgram;
// program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/shader.vs");
// program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/shader.fs");
program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/Diffuse.vs");
program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/Diffuse.fs");
program->link();
program->bind();//激活Program对象
unsigned int indices[] = {
0, 1, 3, // first triangle
1, 2, 3 // second triangle
};
vbo.create();
vbo.bind(); //绑定到当前的OpenGL上下文,
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo.allocate(vertices22, sizeof(vertices22));
//vbo.allocate(vertices.constData(), vertices.count() * sizeof(GLfloat));
vao.create();
vao.bind();
// _ebo = new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer);
// _ebo->create();
// if(!_ebo->bind()){
// qDebug() << "ebo绑定失败!";
// }
// _ebo->allocate(indices, sizeof(indices));
for (int j = 0; j < 6; ++j)
{
textures[j] = new QOpenGLTexture(QImage(QString(":/cube%1.png").arg(j+1)).mirrored());
textures[j]->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
textures[j]->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);
textures[j]->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
}
program->setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float)); //设置aPos顶点属性
program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float), 3, 6 * sizeof(float)); //设置aColor顶点颜色
// program->setAttributeBuffer(2, GL_FLOAT, 6 * sizeof(float), 2, 8 * sizeof(float)); //设置aColor顶点颜色
program->enableAttributeArray(0); //使能aPos顶点属性
program->enableAttributeArray(1); //使能aColor顶点颜色
// program->enableAttributeArray(2); //使能aColor顶点颜色
//program->setUniformValue("texture", 0);
lightVao.create();
lightVao.bind();
lightprogram->setAttributeBuffer(0, GL_FLOAT, 0, 3, 6 * sizeof(float));
lightprogram->enableAttributeArray(0); // 使能 location = 0的顶点属性aPos
}
void testshadeWidget::rotateBy(int xAngle, int yAngle, int zAngle)
{
xRot += xAngle;
yRot += yAngle;
zRot += zAngle;
update();
//timer->stop();
}
void testshadeWidget::offsetBy(int xPos, int yPos, int zPos)
{
m_xRos += xPos;
m_yRos += yPos;
m_zRos += zPos;
update();
}
void testshadeWidget::mouseMoveEvent(QMouseEvent *event)
{
// if(event->x() == m_cursorPos.x() && m_cursorPos.y() == event->y())
// return;
// QPoint MovePos = QCursor::pos();
// QPoint currentPos = mapFromGlobal(MovePos);
// int xoffset = 0;
// int yoffset = 0;
// xoffset = currentPos.x() - m_cursorPos.x();
// yoffset = currentPos.y() - m_cursorPos.y();
// qDebug()<<"1111111111"<<m_xRos;
// if(xoffset>0 )
// {
// m_xRos += 0.1;
// m_yRos = 0;
// if(m_xRos>=1.0f)
// {
// m_xRos = 0.0f;
// m_yRos = 0.0f;
// }
// }
// else if(xoffset<0 )
// {
// m_xRos -= 0.1;
// m_yRos = 0;
// if(m_xRos<=-1.0f)
// {
// m_xRos = 0.0f;
// m_yRos = 0.0f;
// }
// }
// else if(yoffset>0)
// {
// m_xRos = 0;
// m_yRos -= 0.1;
// if(m_yRos<=-1.0f)
// {
// m_xRos = 0.0f;
// m_yRos = 0.0f;
// }
// }
// else if(yoffset<0)
// {
// m_xRos = 0;
// m_yRos += 0.1;
// if(m_yRos>=1.0f)
// {
// m_xRos = 0.0f;
// m_yRos = 0.0f;
// }
// }
// update();
// m_cursorPos = currentPos;
//timer->start();
//m_yRos += yPos;
//update();
//m_yRos += yoffset;
//qDebug()<<"mouseMoveEvent"<<xoffset<<yoffset<<m_xRos<<m_yRos<<xPos<<yPos;
//qDebug()<<"m_cursorPos"<<(m_xRos==1.0);
//m_cursorPos = QPoint(event->x(),event->y());
}
void testshadeWidget::mouseReleaseEvent(QMouseEvent *event)
{
if(event->x() == m_cursorPos.x() && m_cursorPos.y() == event->y())
return;
QPoint MovePos = QCursor::pos();
QPoint currentPos = mapFromGlobal(MovePos);
int xoffset = 0;
int yoffset = 0;
xoffset = currentPos.x() - m_cursorPos.x();
yoffset = currentPos.y() - m_cursorPos.y();
if( xoffset>0 && (yoffset>=-15 && yoffset<=15))
{
nDerection = 0;
}
else if(xoffset<0 && (yoffset>=-15 && yoffset<=15))
{
nDerection = 1;
}
else if(yoffset>0 && (xoffset>=-15 && xoffset<=15))//down
{
nDerection = 2;
}
else if(yoffset<0 && (xoffset>=-15 && xoffset<=15))//up
{
nDerection = 3;
}
qDebug()<<"1111111111"<<m_xRos<<nDerection<<xoffset<<yoffset;
timer->start();
}
void testshadeWidget::mousePressEvent(QMouseEvent *event)
{
if(event->button() == Qt::LeftButton)
{
m_cursorGlobalPos = QCursor::pos();
m_cursorPos = mapFromGlobal(m_cursorGlobalPos);
}
}
void testshadeWidget::paintGL()
{
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); //设置清屏颜色
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// //开启深度测试
//glEnable(GL_DEPTH_TEST);
// //开启遮挡剔除
// glEnable(GL_CULL_FACE);
QMatrix4x4 m;
m.translate(m_xRos, m_yRos, m_zRos);
m.rotate(xRot / 16.0f, 1.0f, 0.0f, 0.0f);
m.rotate(yRot / 16.0f, 0.0f, 1.0f, 0.0f);
m.rotate(zRot / 16.0f, 0.0f, 0.0f, 1.0f);
m.scale(0.5);
program->bind();
QMatrix4x4 view;
view.lookAt(QVector3D(0,0,3), QVector3D(0,0,3)+QVector3D(0,0,-1), QVector3D(0,1,0));
QMatrix4x4 projection;
projection.perspective(45.0f,
float(this->width())/float(this->height()),
0.1f,
100.0f);
program->setUniformValue("model", m);
program->setUniformValue("view", view);
program->setUniformValue("projection", projection);
program->setUniformValue("objectColor", QVector3D(1.0f, 0.5f, 0.3f));//物体颜色
program->setUniformValue("lightColor", QVector3D( 1.0f, 1.0f, 1.0f));
program->setUniformValue("lightPos", QVector3D(1.2f, 1.0f, 2.0f));//光位置
for(int i=0;i<6;i++)
{
glDrawArrays(GL_TRIANGLE_FAN, i*4, 4);
}
vao.bind();
lightprogram->bind();
QMatrix4x4 model;
model.translate(0.5f, 0.5f, 0.5f);
model.scale(0.2);
lightprogram->setUniformValue("view", view);
lightprogram->setUniformValue("model", model);
lightprogram->setUniformValue("projection", projection);
lightVao.bind();
//glDrawArrays(GL_TRIANGLES, 0, 36);
for(int i=0;i<6;i++)
{
glDrawArrays(GL_TRIANGLE_FAN, i*4, 4);
}
}
void testshadeWidget::resizeGL(int width, int height)
{
// QMatrix4x4 projection;
// projection.perspective(m_verticalAngle, width/height, 0.01, 100);
// // m_verticalAngle: 设置垂直角度(值越大,那么物体越小)
// // width()/height(): 设置宽高比
// // 0.1 100: 设置近远平面距离
// glUseProgram(program->programId());
// program->setUniformValue("projection", projection);
this->glViewport(0,0,width,height); //定义视口区域
this->update();
}
#ifndef TESTSHADEWIDGET_H
#define TESTSHADEWIDGET_H
#include <QWidget>
#include <QOpenGLWidget>
#include <QOpenGLExtraFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include <QOpenGLTexture>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QOpenGLVertexArrayObject>
#include <QTimer>
#include <QMouseEvent>
QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram);
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)
class testshadeWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
testshadeWidget();
~testshadeWidget();
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int width, int height) override;
void rotateBy(int xAngle, int yAngle, int zAngle);
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void mousePressEvent(QMouseEvent *event) override;
void offsetBy(int xPos, int yPos, int zPos);
private:
QOpenGLTexture *textures[6] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
QOpenGLShaderProgram *program = nullptr;
QOpenGLBuffer vbo;
QVector<float> vertices;
QOpenGLVertexArrayObject vao;
QTimer* timer;
float m_xRos = 0.0f;
float m_yRos = 0.0f;
float m_zRos = 0.0f;
QVector3D cameraPos;
QVector3D cameraTarget;
QVector3D cameraDirection;
QOpenGLTexture *texture;
QMatrix4x4 projection;
QVector3D cam;
int xRot = 0;
int yRot = 0;
int zRot = 0;
QPoint m_cursorPos;
QPoint m_cursorGlobalPos;
int nDerection=0;
QVector3D lightPos;
float m_verticalAngle; // 视角缩放
QOpenGLShaderProgram *lightprogram = nullptr;
QOpenGLVertexArrayObject lightVao;
QOpenGLBuffer *_ebo;
};
#endif // TESTSHADEWIDGET_H
我们运行下
这里是不是有阴影和光照了。
我们来解释下方向量的定义。
我们看下正面这个面,垂直于这个面的是不是Z轴,那就是z轴是1,其他是0,在看垂直于这个面的是不是Z轴反方向,所以是-1,所以法向量是0,0,-1。