QT+OPenGL十之光照模型
在现实生活中光是粒子,也就是无数粒子构成的,但是在计算机中如果我们模拟这种大量的粒子的话会导致效率严重下降,程序难以运行。但是我们肉眼观看世界,实际上看的是光的物理特性,也就是反射出的颜色。如果我们从这种颜色出手,就能达到模拟现实光照的效果,而且效率很高。大家知道一张素描纸上也能画出逼真的3D效果,这完全是对颜色的把控。(只要骗过肉眼就是好的哈哈)。这时冯着色就诞生了(很牛皮的数学家哦)。
冯着色:
ambient:环境光
diffuse:漫反射光
specular:镜面反射光(高光)
三者结合(combined phong)后就是冯着色模型。
OpenGL中的像素vec4(r,g,b,a)且都在范围0.0~1.0的浮点数之间来表示,前三位是颜色,最后一位是透明度。
环境光(又称全局光):
这是什么喃?:
我们有在老家待过的经理就会发现,虽然老家没有路灯,但是我们并没有见到完全黑暗的镜像。我们还是能微微的看见大树和山的轮廓、模模糊糊的道路。在有月光时显得更清晰。实际上在自然环境中,几乎没有完全黑暗的地方(封闭的山洞除外哈,别杠),每个地方多多少少会从其他东西上反射而获得微弱的光。比如黑夜中能反射星星或者月光亦或者路灯,都能让我们感受到夜晚并不完全黑暗。这样的微弱光芒就是环境光。
实现原理:
因为现实中会反射的微弱的光太多太多甚至是反射很多次,我们很难用算法进行模拟,为了模仿这种微弱的光的效果,而且不影响效率。我们采用全局反射相同的微弱光。用微弱的光线强度因子乘以光的颜色,最后再乘以物体的颜色。最后就得到物体对环境光反射的颜色。
因为只研究颜色,所以修改都在片段着色器了。
shaderMode.fs
#version 430
out vec4 color;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform sampler2D textureID;
in vec2 texcoord;
void main(void)
{
float ambientStringth=0.2;
vec3 lightColor=vec3(1.0,1.0,1.0);
vec3 ambient=ambientStringth*lightColor;
vec4 objectColor=texture(textureID,texcoord);
color=vec4(ambient,1.0)*objectColor;
}
这里因子给了0.2,因为0.1太黑不太容易观察。光的颜色白色,物体颜色采用上一章的纹理颜色(全球地图)。
image.png
大家对于上一章,发现地球进入了黑夜喃,我们能够微弱的看到一些表面。
漫反射光:
漫反射是什么?:
漫反射很显然是体现在粗糙的物体上,面向光的地方会更亮,大家记得中学物理书上讲的自行车尾部的红色物体,清洁工人的衣服,当有光靠近,就会特别的明亮,来提示前方有人。其实就是运用漫反射原理。而对于地球来说很显然面向光源的一侧就是白天,太阳照射的方向。而光源就是太阳。物理书上为了描述这种现象是不是引入了法线这个概念喃。
实现:
上面提到了几个关键字,光源、法线
image.png
说到这里不得不说说向量的点乘:
image.png
两向量垂直时,其结果为0,夹角为锐角时,角度越小其结果越偏向与1,当然钝角时为负数(我们不关心负数的情况,背向光源我们认为没有漫反射,因此取0.0)。(这里向量都单位化了,长度为1的向量,这样可以排除cosθ前面的模长对结果造成影响)故:a*b=cosθ。这个大小不正是漫反射的特点吗。
注意:光线本是指向物体,但是为了计算我们会取光线的逆方向。且每个位置(空间中)光强度因子不衰减。
做到这不得不说前面做了不好的事情:
1.mv矩阵我是相乘后把结果发送给shader,但是漫反射需要每个顶点在世界坐标系下的位置也就是m*position。(模型自己本身就有坐标空间,他的顶点都是在这个空间下的位置,当我们乘以模型矩阵,这些点就是世界坐标下的位置)。因此我们把mv矩阵拆开发送给shader。
2.之前我没有注重效率,把统一变量(也叫一致变量)的设置在初始话的时候一起做。
修改myMesh.cpp
#include "stdafx.h"
#include "MyMesh.h"
MyMesh::MyMesh(vector<Vertex> vertices, vector<GLuint> indices, vector<Texture> textures): ebo(QOpenGLBuffer::IndexBuffer), texture(QImage("./Resources/model/earth.bmp"))
{
this->vertices = vertices;
this->indices = indices;
this->textures = textures;
}
void MyMesh::init(QOpenGLShaderProgram* shaderProgram)
{
initializeOpenGLFunctions();
this->shaderProgram = shaderProgram;
shaderProgram->bind();
vao.create();
vbo.create();
ebo.create();
vao.bind();
vbo.bind();
vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);
vbo.allocate(&vertices[0], this->vertices.size() * sizeof(Vertex));
// 设置顶点坐标指针
vPosition = shaderProgram->attributeLocation("vPosition");
shaderProgram->setAttributeBuffer(vPosition, GL_FLOAT, 0, 3, sizeof(Vertex));
glEnableVertexAttribArray(vPosition);
// 设置法线指针
normal= shaderProgram->attributeLocation("normal");
shaderProgram->setAttributeBuffer("normal", GL_FLOAT, offsetof(Vertex, Normal), 3, sizeof(Vertex));//shader变量索引,参数类型,偏移量,元素大小,步长
glEnableVertexAttribArray(normal);
// 设置顶点的纹理坐标
uv = shaderProgram->attributeLocation("uv");
shaderProgram->setAttributeBuffer(uv, GL_FLOAT, offsetof(Vertex, TexCoords), 2, sizeof(Vertex));
glEnableVertexAttribArray(uv);
ebo.bind();
ebo.setUsagePattern(QOpenGLBuffer::StaticDraw);
ebo.allocate(&this->indices[0], this->indices.size() * sizeof(GLuint));
vao.release();
ebo.release();
vbo.release();
model_loc = shaderProgram->uniformLocation("model");
view_loc = shaderProgram->uniformLocation("view");
shaderProgram->setUniformValue("textureID", texture.textureId());//一次先获取好统一变量的设置位置。
shaderProgram->release();
vertices.clear();
}
void MyMesh::draw(Camera camera) {
shaderProgram->bind();
texture.bind(texture.textureId());
//构建视图矩阵
QMatrix4x4 m;
m.translate(locationX, locationY, locationZ);
shaderProgram->setUniformValue(model_loc, m);//解开mv矩阵,分开发送
QMatrix4x4 v;
v.lookAt(QVector3D(camera.location.x, camera.location.y, camera.location.z),
QVector3D(camera.viewPoint.x, camera.viewPoint.y, camera.viewPoint.z),
QVector3D(camera.worldY.x, camera.worldY.y, camera.worldY.z));
shaderProgram->setUniformValue(view_loc, v);
vao.bind();
glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
vao.release();
texture.release();
shaderProgram->release();
}
void MyMesh::setLocation(float x, float y, float z) {
locationX = x;
locationY = y;
locationZ = z;
}
shaderMode.vs
#version 430
uniform mat4 model;
uniform mat4 view;
uniform mat4 proj_matrix;
in vec3 vPosition;
in vec3 normal;
in vec2 uv;
out vec3 Normal;
out vec2 texcoord;
out vec3 worldPos;
void main(void)
{
gl_Position=proj_matrix*view*model*vec4(vPosition,1.0);
texcoord=uv;
Normal=normal;//输出法线
worldPos=vec3(model*vec4(vPosition,1.0));//输出每个点在世界坐标系的位置
}
shaderMode.fs
#version 430
out vec4 color;
uniform mat4 proj_matrix;
uniform sampler2D textureID;
in vec2 texcoord;
in vec3 Normal;//接受法线向量
in vec3 worldPos;//接受世界坐标系下每个点的位置
void main(void)
{
float ambientFactor=0.2;
float diffuseFactor = 1.0f;
vec3 lightPos=vec3(0.0,30.0,-7.0);//设置光源位置
vec3 lightColor=vec3(1.0,1.0,1.0);
vec3 ambient=ambientFactor*lightColor;
vec4 objectColor=texture(textureID,texcoord);
vec3 normol=normalize(Normal);//向量单位化
vec3 lightDir = normalize(lightPos - worldPos);//光线方向的反方向单位化
float diffuseStringth = max(dot(normol, lightDir), 0.0);//dot运算出cosθ,max如果小于0.0,取0.0。获取漫反射强度
vec3 diffuse = diffuseFactor *diffuseStringth * lightColor;//面反射光
color= vec4(ambient + diffuse,1.0) * objectColor;//和全局光相结合
}
效果图:
image.png
看到了地球有了"白天和黑夜",迎接光的地方更亮了。
注意这里我们只考虑模型的等比缩放,不等比缩放会影响法向量,在着色器中会使用逆转置矩阵,对法线进行修复,但是着色器对于运算逆矩阵开销很大,影响效率,因此我们应该在CPU运算出这个矩阵然后发送给着色器,我们还没有讨论到逆装置矩阵,所以现在只考虑模型的等比缩放。
镜面反射:
什么是镜面反射:
我们可以考虑金属的表面会反射出光斑,我们侧着看镜子也会捕获到这样的在一部分面积产生白色光亮的部位。这就是镜面反射。
实现:
这里引用learnOpenGL的图片:
image.png
反射光线与视线的向量夹角越小,余玄值越大,镜面反射越集中。(镜子也就越刺眼哈哈)
因此我们还要获取相机位置
myMesh.cpp中添加
cameraPos_loc = shaderProgram->uniformLocation("cameraPos");
shaderProgram->setUniformValue(cameraPos_loc, QVector3D(camera.location.x, camera.location.y, camera.location.z));
.fs
#version 430
out vec4 color;
uniform mat4 proj_matrix;
uniform sampler2D textureID;
uniform vec3 cameraPos;
in vec2 texcoord;
in vec3 Normal;
in vec3 worldPos;
void main(void)
{
float ambientFactor=0.2;
float diffuseFactor = 1.0f;
float specularFactor = 0.5f;
vec3 lightPos=vec3(0.0,30.0,-7.0);
vec3 lightColor=vec3(1.0,1.0,1.0);
vec3 ambient=ambientFactor*lightColor;
vec4 objectColor=texture(textureID,texcoord);
vec3 normol=normalize(Normal);
vec3 lightDir = normalize(lightPos - worldPos);
float diffuseStringth = max(dot(normol, lightDir), 0.0);
vec3 diffuse =diffuseFactor* diffuseStringth * lightColor;
vec3 viewDir = normalize(cameraPos - worldPos);//视线方向
vec3 reflectDir = reflect(-lightDir, normol);//反射光线方向
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), 32);//pow作用是让光线散射变集中
vec3 specular = specularFactor * specularStringth * lightColor;
color= vec4(ambient + diffuse+specular,1.0) * objectColor;
}
image.png
当max函数中返回值在0~1时
我们观察到数字越大变化越小,散射越集中。次方越多这种变化会更明显
image.png
和上面比较这里明显出现了亮斑。
目录
VSC++2019+QT+OpenGL
QT+OpenGL一之绘制立方体(三角形图元)
QT+OpenGL二之纹理贴图
QT+OpenGL三之矩阵简解
QT+OpenGL四之相机的移动和旋转
QT+OpenGL五之绘制不同的模型(vao,vbo机制)
QT+OpenGL六之天空盒
QT+OpenGL七之使用EBO
QT+OPenGL八之模型准备
QT+OPenGL九之模型解码
QT+OPenGL十之光照模型
QT+OPenGL十一之漫反射和镜面反射贴图
QT+OPenGL十二之定向光
QT+OPenGL十三之真正的点光源和聚光灯
QT+OPenGL十四之多光源混合的问题
QT+OPenGL十五之深度缓冲区
QT+OPenGL十六之模板缓冲区
QT+OPenGL十七帧缓冲区(离屏渲染)
QT+OPenGL十八抗锯齿
QT+OPenGL十九镜面反射效率调整
QT+OPenGL二十Gamma校正