QT+OPenGL十四之多光源混合的问题

我们前面已经研究了各中不同光源,但是我们都是单独实现的。一个场景中往往会有多种光源。所以我们不得不考虑这种情况。为了能看出效果,我们需要先来一个地形,下载地形模型直接按前面讲的,绘制出来就好了。
效果:


image.png

这只是有定向光的情况,但是我们已经能看到各个部分,背向光线有了阴影。

混合光的思路:

我们可以对每种光源处理后最后返回的颜色封装成函数。然后对结果进行结合。
封装的好处:封装好了更便于我们以后重用,而且能使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);
}

单独开启点光源效果(光源放在龙模型上方五个像素处):


image.png

可以看见只有点光源周围被点亮了。

封装聚光灯:我们这里写一个手电筒和一个类似与路灯的光源:

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);
    
}

效果:


image.png

可以看到斜坡别聚光灯照亮了,我们这里认为聚光灯也不衰减。
当然一般的模仿路灯的效果这样就足够了,如果做手电筒还是应该做衰减的。我这里就不做衰减了,就是把点光源的衰减方法加在这里就好了。

手电筒的实现很简单就是把光源的位置换成相机的位置,光源的方向换成视线的方向:
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);
}

效果图:


image.png

我们能看见近处被照亮远处亮度就不够了,这就是我们要的效果。如果要开启多个光源,直接把这些返回值相加就好了。
统一变量发送

 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); 
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校正

你可能感兴趣的:(QT+OPenGL十四之多光源混合的问题)