OpenGL学习笔记《八》光照

  在理解opengl中的光照以前,先理解一下颜色。在咱们现实生活中,咱们看到的物体颜色,是物体反射的颜色,即没有被物体吸取掉的部分。opengl利用这个,在系统中模拟的颜色,就是定义一个表示对象的颜色向量A,再顶一个一个表示光源的颜色向量B,而后在片断着色器中将两个向量相乘获得表示物体的颜色。所以大体能够理解为,opengl中最终要渲染出的颜色,实际上是表示对象的颜色乘以某个影响系数,得出来的。函数

  由于现实生活中的光照很是的复杂,早期计算设备条件不足的状况下不能作很复杂的计算,得出的光照效果比较粗糙。近年来随着计算设备能力的提高,开始出现光照追踪技术,尽量的模拟出现实生活中的光照效果。学习

  opengl中采用的光照模型,叫作冯氏光照模型(Phong lighting model),由三个份量组成:环境光(ambient),漫散射光(diffuse),镜面反射光(specular),三个份量在opengl中分别使用一个vec3向量表示,vec3三个份量x,y,z分别表示r,g,b。单独一个份量对最终渲染的影响,就是用表示份量颜色的向量去乘以对象颜色的向量,获得的结果就是opengl系统模拟的对象在受光照影响下的效果。而冯氏光照模型,就是将三个份量的效果组合起来,获得最终的影响效果。三种模式效果以下:网站

 

 

环境光(ambient)spa

  在现实生活中光照的来源有不少,如别的物体反射过来的,所以可能在咱们并无直接看到光源的状况下,咱们也能看到眼前的某些物体(即这些物体有反射光线过来)。冯氏光照模型中的环境光份量就是用来模拟这种效果,咱们在这里简单的模拟,即在光源颜色的基础上乘以一个常量系数,获得的就是环境光颜色,再将这个颜色乘以对象颜色,获得模拟出来的效果,在这里假定这个常量为0.1,调整片元着色器代码:3d

void main()
{
    float ambientStrength = 0.1;
    vec3 ambient = ambientStrength * lightColor;

    vec3 result = ambient * objectColor;
    FragColor = vec4(result, 1.0);
}  

获得的效果以下:code

 

 

 经过调整常量的值,咱们能够模拟出在不一样的光照强度下,对象的不一样表现。orm

漫散射光(diffuse)对象

  在现实生活中,以灯泡为光源,咱们拿一个物体放到离灯泡不一样的位置,物体表现出来的颜色效果会有不一样,若是离的近、与灯泡垂直了,那么物体表面就会更亮,若是离的远那么就会相对暗点。冯氏光照模型中的漫散射光份量模拟的就是这种效果,如下图为例:blog

 

 

 咱们提到的与灯泡垂直,离灯泡远,其实能够理解为上图中θ角度的变化,θ角度越小,即接近于垂直,那么此时物体表现的就会越亮,反之则暗,参考上文中提到的环境光系数的影响,在这里咱们也能够模拟一个系数,来达到效果。从数学角度出发,两个单位向量的点乘,结果等于两个单位向量的模乘以两个向量夹角的余弦值,0-90度范围内,夹角越小,值越大。所以在这里咱们能够根据这个特性,来模拟漫散射效果。数学

  根据光源位置,顶点坐标,咱们能够获得表示光源方向的环境向量,而后咱们再引入法线向量这个概念(垂直于表面的向量),用两个向量相乘获得的值,乘以光源的颜色就能够获得模拟漫反射的光源颜色,再将这个颜色乘以对象的颜色,就能够模拟出漫反射影响下的对象表现效果。

  两个向量相减,能够获得一个表示方向的向量,在这里咱们是用顶点的位置减去光源的位置,获得一个从顶点位置指向光源的单位向量,而法线向量咱们能够根据对象的形状直接获得,调整片元着色器代码:

void main(){

    vec3 norm = normalize(oNormal);
    vec3 lightDir = normalize(u_lightPos - oFragPos);
    float diff = max(dot(norm, lightDir), 0.0f);
    vec3 diffuse = diff * u_lightColor;

    float ambientStrength = 0.1f;
    vec3 ambient = ambientStrength * u_lightColor;
    vec3 result = (ambient + diffuse) * u_objectColor;

    FragColor = vec4(result, 1.0f);
}

  由于咱们提到了是用单位向量进行计算,因此保险起见对于咱们要使用到的向量,都调用normalize处理一下。最终获得的效果:

 

 

 光源的位置,是白色矩形的位置,从上图就能够看到与光源夹角越小的就更亮,不然就更暗。

镜面反射光(specular)

  在现实生活中,咱们也有这样的体会,咱们从不一样的角度去观察物体,看到的效果也会不同。在冯氏光照模型中,使用镜面反射光来模拟这个效果,以下图所示:

 

 

 在这里使用到的是光源向量基于法线对称后,与表示视线方向向量的夹角,来模拟镜面反射的效果,用两个向量点乘后获得的值,再乘以光源颜色,获得表示镜面反射光份量的颜色向量,再去乘以对象的颜色,获得模拟出来的效果。调整后的片元着色器代码:

void main(){
    float ambientStrength = 0.1f;
    vec3 ambient = ambientStrength * u_lightColor;

    vec3 norm = normalize(oNormal);
    vec3 lightDir = normalize(u_lightPos - oFragPos);
    float diff = max(dot(norm, lightDir), 0.0f);
    vec3 diffuse = diff * u_lightColor;

    float specularStrength = 0.5f;
    vec3 viewDir = normalize(u_viewPos - oFragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0f), 32);
    vec3 specular = specularStrength * spec * u_lightColor;

    vec3 result = (ambient + diffuse + specular) * u_objectColor;
    FragColor = vec4(result, 1.0f);
}

咱们使用GLSL内置的reflect函数计算反射后的光源方向,而后计算与视线方向单位向量的夹角值,最后咱们取这个值的32次方值,取的次方值越高,在表现上就越像聚焦的效果,以下图:

 

 最终咱们将三个份量的值相加,再去乘以对象的颜色,就获得了冯氏光照模型下模拟出来的效果。

材质(Material)Lighting map投光物(Light caster)

  现实生活中,光照的效果很是复杂,好比一组物体,离光源的远近,会影响物体的亮度;木头和铁块表现出来的效果不一样;以聚光灯为光源照射一组物体,中心区域的物体表现的会亮一点,周围区域的会表现的暗一点。咱们上面提到的冯氏光照模型其实也能够模拟出这种效果,咱们能够看到上面的片元着色器代码中,环境光、漫散射光、镜面反射光都有乘以一个常量,若是咱们动态的去调整这个常量,就能够获得不一样的效果。

  如,咱们模拟一个离光源远近,物体表现不一样的效果,咱们能够乘以一个衰减系数,这个衰减系数的计算有一个公式:

\begin{equation} F_{att} = \frac{1.0}{K_c + K_l * d + K_q * d^2} \end{equation}

  其中,分子部分为常量1,保证最终获得的系数值须要小于1,分母由三个部分组成,一个常量项,一个线性项,一个二次项部分;d则表示距离光源的远近,用来模拟下图的系数值:

 

   即随着距离的增长,系数值变小,到必定距离以后这个系数值基本维持在一个比较小的范围。调整后的片元着色器代码:

// attenuation
    float distance = length(u_light.position - oFragPos);
    float attenuation = 1.0 / (u_light.constant + u_light.linear * distance + u_light.quadratic * (distance * distance));

    // ambient
    vec3 ambient = u_light.ambient * vec3(texture(u_material.diffuse, oTexCoords));
    ambient *= attenuation;

    // diffuse
    vec3 norm = normalize(oNormal);
    vec3 lightDir = normalize(u_light.position - oFragPos);
    float diff = max(dot(norm, lightDir), 0.0f);
    vec3 diffuse = u_light.diffuse * (diff * vec3(texture(u_material.diffuse, oTexCoords)));
    diffuse *= attenuation;

    // specular
    vec3 viewDir = normalize(u_viewPos - oFragPos);
    vec3 reflectDir = reflect(-lightDir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0f), u_material.shininess);
    vec3 specular = u_light.specular * (spec * vec3(texture(u_material.specular, oTexCoords)));
    specular *= attenuation;

    vec3 result = (ambient + diffuse + specular);
    FragColor = vec4(result, 1.0f);

获得的效果以下:

 

 

  以上就是简单的理解一下opengl中的光照。学习的网站上经过一个章节来说解,主要就是围绕对冯氏光照模型的三个份量,乘以不一样的系数值,来达到模拟现实生活中不一样的效果。

相关文章
相关标签/搜索