我们前面已经研究了各中不同光源,但是我们都是单独实现的。一个场景中往往会有多种光源。所以我们不得不考虑这种情况。为了能看出效果,我们需要先来一个地形,下载地形模型直接按前面讲的,绘制出来就好了。
效果:
这只是有定向光的情况,但是我们已经能看到各个部分,背向光线有了阴影。
混合光的思路:
我们可以对每种光源处理后最后返回的颜色封装成函数。然后对结果进行结合。
封装的好处:封装好了更便于我们以后重用,而且能使shader中main函数更简介更可读。
注意:这里为了简洁我们默认每一种光的环境光因子,漫反射因子和镜面反射因子都是相同的,我们就不在每种光结构体中定义这三种因子了。首先shader和c语言及其相似,我们可以用c语言的方法来写函数。
修复法向量:
经过前面的学习我们法线光照往往是需要法线的,而法向量会随着模型的旋转和非等比缩放,就会发生改变,如果我们不修复就会出现光照的严重视觉错误。当旋转后,我们可以直接使用:
Normal=vec3(model*vec4(normal,0.0));
把最后的法向量发送给片段着色器去处理光照,我们知道模型矩阵中包括了位移缩放和旋转,而向量没有位移属性因此我们必须排除掉,直接把w分量置0就能很好的解决,这样法向量就和顶点坐标一样被放置到世界坐标系中。当然这种情况不适用于非等比缩放。非等缩放需要用到逆转置矩阵,这个在以后来讨论。
struct Factor
{
float ambient;
float diffuse;
float specular;
};
uniform Factor factor;
封装定向光:
struct DirLight
{
vec3 direct;
vec3 color;
};
uniform DirLight dirDight;
函数,注意先声明再使用,也可写再main函数上面
vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
//获取漫反射贴图,镜面反射贴图纹理采样的颜色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir=normalize(dirDight.direct);
vec3 normal=normalize(Normal);
//计算漫反射强度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//计算镜面反射强度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三种反射的颜色
vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
//返回最终结合出的颜色
return vec3(ambient + diffuse+specular);
}
完整shader:这是个例子后面就省略了这些代码
#version 430
struct Material
{
sampler2D diffuse;
sampler2D specular;
float shininess;
};
struct DirLight
{
vec3 direct;
vec3 color;
};
struct Factor
{
float ambient;
float diffuse;
float specular;
};
uniform Material material;
uniform DirLight dirDight;
uniform Factor factor;
out vec4 color;
uniform vec3 cameraPos;
in vec2 texcoord;
in vec3 Normal;
in vec3 worldPos;
vec3 getDirLightPointColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material);
void main(void)
{
color=vec4(getDirLightColor(texcoord,Normal,factor,dirDight,material),1.0);
}
vec3 getDirLightColor(vec2 texcoord,vec3 Normal,Factor factor,DirLight dirDight,Material material){
//获取漫反射贴图,镜面反射贴图纹理采样的颜色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir=normalize(dirDight.direct);
vec3 normal=normalize(Normal);
//计算漫反射强度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//计算镜面反射强度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三种反射的颜色
vec3 ambient=factor.ambient*dirDight.color*diffuseColor;
vec3 diffuse =factor.diffuse* diffuseStringth *dirDight.color*diffuseColor;
vec3 specular = factor.specular * specularStringth*dirDight.color*specularColor;
//返回最终结合出的颜色
return vec3(ambient + diffuse+specular);
}
封装点光源:
struct PointLight
{
vec3 position;
float constant;//1.0
float linear;//0.045
float quadratic; //0.0075,这个衰减距离是100时全部衰减
vec3 color;
};
uniform PointLight pointLight;
vec3 getPointLightColor(vec2 texcoord,vec3 Normal,Factor factor,PointLight pointLight,Material material){
//获取漫反射贴图,镜面反射贴图纹理采样的颜色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir = normalize(pointLight.position-worldPos);
vec3 normal=normalize(Normal);
// 计算漫反射强度
float diffuseStringth = max(dot(normal, lightDir), 0.0);
// 计算镜面反射
vec3 reflectDir = reflect(-lightDir, normal);
vec3 viewDir = normalize(cameraPos - worldPos);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// 计算衰减
float distance = length(pointLight.position - worldPos);
float attenuation = 1.0f/(pointLight.constant + pointLight.linear * distance +
pointLight.quadratic * (distance * distance));
// 将各个分量合并
vec3 ambient = factor.ambient*attenuation*pointLight.color*diffuseColor;
vec3 diffuse = factor.diffuse*diffuseStringth*attenuation*pointLight.color*diffuseColor;
vec3 specular = factor.specular*specularStringth*attenuation*pointLight.color*specularColor;
return vec3(ambient + diffuse + specular);
}
单独开启点光源效果(光源放在龙模型上方五个像素处):
可以看见只有点光源周围被点亮了。
封装聚光灯:我们这里写一个手电筒和一个类似与路灯的光源:
struct Spotlight
{
vec3 position;
vec3 direction;
float cutOff;//Φ的余玄值
float outerCutOff;//r的余玄
vec3 color;
};
uniform Spotlight spotlight;
vec3 getSpotlightColor(vec2 texcoord,vec3 Normal,Factor factor,Spotlight spotlight,Material material){
//获取漫反射贴图,镜面反射贴图纹理采样的颜色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir = normalize(spotlight.position-worldPos);
vec3 normal=normalize(Normal);
//计算光照范围强度
float theta = dot(lightDir, normalize(spotlight.direction));
float epsilon = spotlight.cutOff - spotlight.outerCutOff;
float intensity = clamp((theta - spotlight.outerCutOff) / epsilon,0.0, 1.0);
//计算漫反射强度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//计算镜面反射强度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三种反射最终颜色
vec3 ambient = factor.ambient*spotlight.color*diffuseColor;
vec3 diffuse = factor.diffuse*diffuseStringth*spotlight.color*intensity*diffuseColor;
vec3 specular = factor.specular*specularStringth*spotlight.color*intensity*specularColor;
return vec3(ambient + diffuse + specular);
}
效果:
可以看到斜坡别聚光灯照亮了,我们这里认为聚光灯也不衰减。
当然一般的模仿路灯的效果这样就足够了,如果做手电筒还是应该做衰减的。我这里就不做衰减了,就是把点光源的衰减方法加在这里就好了。
手电筒的实现很简单就是把光源的位置换成相机的位置,光源的方向换成视线的方向:
struct Torch
{
float cutOff;//Φ的余玄值
float outerCutOff;//r的余玄
float constant;
float linear;
float quadratic;
vec3 color;
};
uniform Torch torch;
vec3 getElectricTorchColor(vec2 texcoord,vec3 Normal,Factor factor,Torch torch,Material material){
//获取漫反射贴图,镜面反射贴图纹理采样的颜色
vec3 diffuseColor=vec3(texture(material.diffuse,texcoord));
vec3 specularColor=vec3(texture(material.specular,texcoord));
vec3 lightDir = normalize(cameraPos-worldPos);
vec3 cameraViewDir = normalize(cameraPos - cameraView);
vec3 normal=normalize(Normal);
//计算光照范围强度
float theta = dot(lightDir, normalize(cameraViewDir));
float epsilon = torch.cutOff - torch.outerCutOff;
float intensity = clamp((theta - torch.outerCutOff) / epsilon,0.0, 1.0);
// 计算衰减
float distance = length(cameraPos - worldPos);
float attenuation = 1.0f/(torch.constant + torch.linear * distance +
torch.quadratic * (distance * distance));
//计算漫反射强度
float diffuseStringth = max(dot(normal,lightDir), 0.0);
//计算镜面反射强度
vec3 viewDir = normalize(cameraPos - worldPos);
vec3 reflectDir = reflect(-lightDir, normal);
float specularStringth = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
//三种反射最终颜色
vec3 ambient = factor.ambient*torch.color*diffuseColor*attenuation;
vec3 diffuse = factor.diffuse*diffuseStringth*torch.color*intensity*diffuseColor*attenuation;
vec3 specular = factor.specular*specularStringth*torch.color*intensity*specularColor*attenuation;
return vec3(ambient + diffuse + specular);
}
效果图:
我们能看见近处被照亮远处亮度就不够了,这就是我们要的效果。如果要开启多个光源,直接把这些返回值相加就好了。
统一变量发送
shaderProgram->setUniformValue("factor.ambient", ambientFactor);
shaderProgram->setUniformValue("factor.diffuse", diffuseFactor);
shaderProgram->setUniformValue("factor.specular", specularFactor);
shaderProgram->setUniformValue("material.shininess", shininess);
shaderProgram->setUniformValue("dirDight.direct", direct);
shaderProgram->setUniformValue("dirDight.color", dirLightColor);
shaderProgram->setUniformValue("pointLight.position", QVector3D(0.0,5.0,-30.0));
shaderProgram->setUniformValue("pointLight.constant", 1.0f);
shaderProgram->setUniformValue("pointLight.linear", linear);
shaderProgram->setUniformValue("pointLight.quadratic", quadratic);
shaderProgram->setUniformValue("pointLight.color", pointLightColor);
shaderProgram->setUniformValue("spotlight.position", QVector3D(0.0,20.0,-30.0));
shaderProgram->setUniformValue("spotlight.direction", QVector3D(0.0,1.0,0.0));
shaderProgram->setUniformValue("spotlight.cutOff", 0.95f);
shaderProgram->setUniformValue("spotlight.outerCutOff", 0.93f);
shaderProgram->setUniformValue("spotlight.color", QVector3D(1.0,1.0,1.0));
shaderProgram->setUniformValue("torch.constant", 1.0f);
shaderProgram->setUniformValue("torch.linear", 0.014f);
shaderProgram->setUniformValue("torch.quadratic", 0.0007f);
shaderProgram->setUniformValue("torch.cutOff", 0.91f);
shaderProgram->setUniformValue("torch.outerCutOff", 0.82f);
shaderProgram->setUniformValue("torch.color", QVector3D(1.0, 1.0, 1.0));
开启点光源和聚光灯:
vec3 spotlightColor=getSpotlightColor(texcoord,Normal,factor,spotlight,material);
vec3 pointLightColor=getPointLightColor(texcoord,Normal,factor,pointLight,material);
vec3 mixtureColor=spotlightColor+pointLightColor;
color=vec4(mixtureColor,1.0);
可以看到龙被点光源照亮,光圈被聚光灯照亮。
目录
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校正