OpenGL 光源分类 行为理论及实现

(一)光照分类探讨

上篇博客中,实现的镜面反射光,漫反射光探讨的都是简单点光源(不包括衰减),但是并没有考虑到点光源的衰减,接下来将光分为三类,并根据物理抽象出数学模型进行较为真实的光照计算。

共性:对于每一种光源都有漫射光和平行光的两种成分,漫射光是指在光源中能够被漫反射的光的颜色成分(白色则包括所有颜色),一般分为两种,平行光和点光源。环境光不是光线,但是需要存储,最简单的方法,就是为每个光源设置一个环境等级,然后把它们都放到一个单一项中。

平行光是指光源中所有能够被镜面反射的光的颜色成分。

设计光照系统:

 其中:

材质的漫反射和镜面反射可以用光照贴图来处理。

一般需设定光的颜色和方向。

struct Light
{
   vec3 m_pos;   //光的方向
   vec3 m_color; //光的颜色
   vec3 m_diffuse;
   vec3 m_specular;//后两者需根据方向实时计算,平行光除外。
};
uniform Light myLight;
struct Material
{
   sampler2D m_diffuse; //漫反射+环境光
   sampler2D m_specular;//镜面反射
   float m_shiness;     //反光度
};

1.1平行光

(1)区分点光源与平行光,看四维向量的最后一个参数,w。

如果w=0,表示这是一个向量,即平行光。

如果w=1,表示这是一个点,即点光源。

(2)光的方向是固定的。

//平行光片段着色器
#version 330 core
out vec4 FragColor;

in vec2 outUV;
in vec3 outNormal;
in vec3 outFragPos;

uniform sampler2D  ourTexture;
uniform   vec3 _view_pos;

struct Light
{
   vec3 m_pos;//平行光方向
   vec3 m_color;
   vec3 m_diffuse;
   vec3 m_specular;
};
uniform Light myLight;
struct Material
{
   sampler2D m_diffuse;
   sampler2D m_specular;
   float m_shiness;
};
uniform Material myMaterial;
void main()
{   
    vec3 lDir=normalize(myLight.m_pos);//平行光方向
   //环境光不属于光线,为光源设置环境等级
    vec3 ambient= myLight.m_color * vec3(texture(myMaterial.m_diffuse,outUV));
    //漫反射
    vec3 _normal=normalize(outNormal);
    float _diff= max(dot(_normal,-lDir),0.0f);
    vec3 diffuse= vec3(texture(myMaterial.m_diffuse,outUV)) * myLight.m_diffuse;
    //镜面反射
    vec3   _viewDir =normalize(_view_pos-outFragPos);
    vec3  _reflectDir=reflect(lDir,outNormal);
    float _re = pow(max(dot(_viewDir,_reflectDir),0.0f),myMaterial.m_shiness);
    vec3 specular=_re * myLight.m_specular * vec3(texture(myMaterial.m_diffuse,outUV));
    //作用于物体
    vec3 result= ambient+diffuse+specular;
    FragColor=texture(ourTexture,outUV) * vec4(result,1.0f);
   
};

由图我们可以发现,每个箱子接收到光照效果相同。 

缺点:对于每个物体光的入射角度都相同,不适合制造特殊的光的效果。

优点:光线均匀,衰减不明显。适合模拟环境光,太阳光。


1.2 点光源

(1)点光源功能:位于环境中某一个位置,朝所有的方向发光,光线随距离衰减。

(2)状态:设定Kc,Kl,Kq来确定衰减的速度,然后根据光源与物体之间的距离来决定光的衰减强度。每个图元的光照根据距离实时变化.

(3)步骤:

[1]确定光的3个衰减参数(看做光的属性封装到光的类里面);

[2]表示出距离;

[3]将光的衰减作用于环境光,漫反射,镜面反射光分量。

(4)细节:光的衰减参数与实际意义无关,只是几何术语。

故想实现环境中的最佳效果,我们可以来自由配置位置和衰减。

代码实现:

   struct Light
   {
   vec3 m_pos;
   vec3 m_color;
   vec3 m_diffuse;
   vec3 m_specular;
   //衰减参数
   float m_c;
   float m_l;
   float m_q;
   };

    float dist= length(myLight.m_pos-outFragPos);
    float attenuation=1.0f/(myLight.m_c+myLight.m_l*dist+myLight.m_q*dist*dist);

    point_shader.setFloat("myLight.m_c",1.0f );
	point_shader.setFloat("myLight.m_l",0.14f );
	point_shader.setFloat("myLight.m_q",0.07f );

运行效果:

点光源运行效果

 由图我们可以观察到,靠近点光源的箱子,接收到的光照就明显,远离光源的箱子接收到的光照随距离减小而衰减。

缺点:衰减参数不易调控,画面发暗。

优点:适合局部照明。


1.3聚光灯 

(1)聚光灯功能:是位于环境中某个位置的光源,只朝一个特定方向并不是所有的方向照射光线。

关键点:光源固定位置,固定方向,图元与光源连线决定光的照射范围。

(2)状态:只有在聚光方向的特定半径内物体的物体被照亮。聚光方向外物体保持黑暗。(黑暗用环境光弥补)

(3)步骤:明确光源位置与照射方向;明确光照区域的范围(用切光角来表征);先判断在聚光区域内,再计算光照。

(4)细节:聚光灯照射方向=法线

聚光灯优化 

 聚光灯的原理类似于手电筒,当我们 靠近某个物体时,观察到,光线的分布并不均匀,明暗分界生硬。但是我们可以对此进行边缘模糊处理。

缺点:聚光灯的光照区域内外,光照过渡周遍,不够柔和。

优点:照明效果适中。根据距离衰减,一定程度使效果更真实。


聚光灯边缘优化图示

1,功能分析:在传统聚光灯基础上,使光照范围内外分界变化柔和。

关键点分析:聚光灯可照明最大角度,与此时此刻的片段着色器角度。可以添加边缘缓冲,进行边缘模糊处理,效果真实。

2.状态:位于可照明最大角度的范围内,正常光照,无边缘模糊处理。

位于可照明最大角度外,边缘缓冲最大角度内,边缘模糊处理。

位于缓冲最大角度外,光照效果系取决于环境光颜色。

3.步骤:

(1)距离衰减:距离;距离衰减公式;

(2)角度衰减:边缘缓冲最大角度(outerCutOff);可照明最大角度(cutOff);此时此刻角度变化(theta)。

根据角度求出边缘模糊强度(intensity),再作用于光的两种主要成分(specular,diffuse);

4.细节:

(1)图示的角度,是以余弦值的方式进行传递。通过cos(radians()函数。

所以在代码书写时,边缘缓冲(Espsilon)需要用cutOff-outerCutOff。

(2)原则上,以可照明最大角度为界,决定是否要进行边缘模糊处理。采用夹逼函数clamp()进行处理。边缘模糊强度,大于1则说明,位于可照明范围内,处理为值取1,无边缘模糊处理。另外,小于0,说明不位于可照明范围内(最小值)处理为值取0,无照明效果。

代码实现: 

#version 330 core
out vec4 FragColor;

in vec2 outUV;
in vec3 outNormal;
in vec3 outFragPos;

uniform sampler2D  ourTexture;
uniform   vec3 _view_pos;

struct Light
{
    vec3 m_pos;
    vec3 m_direction;
    float m_cutOff;
    float m_outCutOff;

    vec3 m_ambient;
    vec3 m_diffuse;
    vec3 m_specular;

    float m_c;
    float m_l;
    float m_q;
};
uniform Light myLight;
struct Material
{
   sampler2D m_diffuse;
   sampler2D m_specular;
   float m_shiness;
};
uniform Material myMaterial;
void main()
{   
 //优化1:边缘模糊参数
    vec3 _lightDir=normalize(myLight.m_pos - outFragPos);
    vec3 _spotDir=normalize(myLight.m_direction);
    float theta=dot(_lightDir,-_spotDir);

    float Epsilon=myLight.m_cutOff-myLight.m_outCutOff;//模糊角度范围(cos)
    float intensity=clamp(myLight.m_outCutOff-theta)/Epsilon,0.0,1.0);//此时此刻占模糊范围角度百分比
    
    //优化2:衰减参数
    float dist=length(myLight.m_pos-outFragPos);
    float attenuation=1.0f/(myLight.m_c+myLight.m_l*dist+myLight.m_q*dist*dist);

    //环境光
    vec3 ambient= myLight.m_color * vec3(texture(myMaterial.m_diffuse,outUV));

    //漫反射
    vec3 _normal=normalize(outNormal);
    float _diff= max(dot(_normal,_lightDir),0.0f);
    vec3 diffuse= vec3(texture(myMaterial.m_diffuse,outUV)) * myLight.m_diffuse*_diff;

    //镜面反射
    vec3   _viewDir =normalize(_view_pos-outFragPos);
    vec3  _reflectDir=reflect(-_lightDir,outNormal);
    float _re = pow(max(dot(_viewDir,_reflectDir),0.0f),myMaterial.m_shiness);//这块写错
    vec3 specular=_re * myLight.m_specular * vec3(texture(myMaterial.m_diffuse,outUV));

    diffuse*=intensity;
    specular*=intensity;
    vec3 result= ambient+diffuse+specular;

    //作用于物体
    FragColor=texture(ourTexture,outUV) * vec4(result,1.0f)*attenuation;
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘敬_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值