shader入门之基础光照知识

随着汽车电子行业的发展,自动驾驶和智能驾驶舱越来越成熟,人机交互界面HMI(Human Machine Interface)也越来越炫酷,这对于HMI领域来说是一个挑战,比如:如何调试出效果精美的车辆模型?如何更直观、流畅的显示周围行车环境?

这些都涉及到丰富的计算机图形学的相关知识,今天这篇文章就给大家简单介绍基础光照的相关知识。

01向量点乘

两个向量的点乘等于他们的数乘结果乘以两个向量之间夹角的余弦值,公式如下:

这非常有用,两个向量之间的夹角记作θ,如果将向量化为单位向量后,它们的长度等于1,那么公式简化成:

从公式可以看出,两个单位向量的点乘就是它们夹角的cosθ值,0°的余弦值是1,90°的余弦值是0。使用点乘可以很容易的知道两个向量是否正交或者平行,以及它们之间的夹角。点乘是通过将对应分量逐个相乘,然后再把所得积相加来计算,如下:

使用反余弦函数,-0.8大概就是143度,点乘在计算光照得时候非常有用。向量的其他运算,就不在此一一介绍了,感兴趣的话,大家可以复习复习。

02颜色

计算机图形学使用一个3维向量来表示颜色,由红色(red)、绿色(green)、蓝色(blue)三个分量组成,就是我们通常说得RGB,每个分量一个字节,取值0~255。

由此可计算出RGB三个值可以组合出大概1677万种颜色,在计算机图形学中,红色可以表示为:glm::vec3 coral(1.0, 0.0, 0.0);(人为的将各分量换算为0~1之间,方便运算)。

其实我们看到某个物体呈现的颜色并不是这个物体真正拥有的颜色,而是它所反射的光的颜色。之所以能看到物体,一是有光,二是物体对光的反射,三是人的眼睛。不同波长的可见光投射到物体上,有部分波长的光被吸收,有部分波长的光被反射出来进入眼睛,经过视觉神经传递到大脑,形成对物体的色彩信息,即人的色彩感觉。

那么物体反射什么波长(颜色)的光呢?篇幅的原因,直接给出结论了:光打到一个物体,物体反射了它本身的颜色,吸收了除了它本身颜色之外的所有颜色。

从现实生活中也可以找到很多例子,比如用电筒照射到红色的玩具,那么我们看到就是红色的。在图形学中,将灯光的颜色和物体的颜色三个分量分别相乘,结果就是得到了最终的颜色。

1. glm::vec3 lightColor(1.0f, 1.0f, 1.0f);//白光
2. glm::vec3 toyColor(1.0f, 0.0f, 0.0f);//玩具为红色
3. glm::vec3 result = lightColor * toyColor; // = (1.0f, 0.0f, 0.0f);
4. //通过光照后,我们就看到了红色的玩具

如上代码,可以理解为:白光(1.0f, 1.0f, 1.0f)打到一个红色(1.0f, 0.0f, 0.0f)的玩具上,它反射了R分量上所有颜色,吸收了G,B两个分量的所有颜色,呈现出来的就是红色。

03光照模型

现实世界的光照极其复杂,会受到非常多因素的影响,就当前的计算能力还没有办法完全模拟,好在前人提出了很多简化的模型来模拟现实情况的光照,而模拟简化后的光照计算方法就称为光照模型,下面我们主要介绍其中phong光照模型以及Blinn-phong光照模型。

兰伯特定理

在介绍光照模型之前,我们先来了解一下兰伯特定理。光源照射到物体表面后,向四面八方反射,产生漫反射效果,而漫反射光的强度近似地服从于lambert定理,即漫反射光的光强仅与入射光的方向和反射点处表面法向量夹角的余弦值成正比。

diffuse = I*max(0,cosθ)

▷ diffuse:反射光线的光强;

▷ I:入射光线的光强;

▷ cosθ:入射光线和该顶点法线的余弦,cosθ = L*N(还记得前面我们说到的cosθ就等于两个向量的点乘)

Phong 光照模型

phong光照模型主要有3个分量组成: 环境(Ambient),漫反射(Diffuse)和镜面(Specular)光照

环境光照(Ambient lighting):光是向着四面八方反弹的,这些从四面八方反弹过来的光能对物体产生间接的影响,这部分称为环境光。为了模拟这个,使用一个环境光照常量,它永远会给物体一些颜色。(这很好理解,房间里开着灯,其他房间也会被照亮,不过其他房间会很暗).

漫反射光照(Diffuse lighting):光打到物体上,会向四面八方反射,遵循兰伯特定理,入射光的方向和法向量之间的夹角越小,它就会越亮,相反夹角越大,它就会越暗。(想想cosθ,0°~90°的曲线)

镜面光照(Specular lighting):模拟有光泽的物体上面出现的亮点, 模拟高光部分。

计算环境光非常简单,通常会给一个很小的常量环境因子,表示环境光对最终光照的影响大小。在调试效果时,我们通常会调试这个环境因子。

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

计算漫反射,则遵循兰伯特定理。漫反射光的光强仅与入射光的方向和反射点处表面法向量夹角的余弦值成正比,如下:

attribute vec3 kzPosition;//输入点
attribute vec3 kzNormal;//输入法向量
uniform vec3 lightPos;//光源的位置
void main(){
vec3 norm = normalize(kzNormal);//标准化,让其变为单位向量
vec3 lightDir = normalize(lightPos - kzPosition);// 入射光方向
float diff = max(dot(norm, lightDir), 0.0);//余弦值
vec3 diffuseColor = lightColor * objectColor * diff;
}

计算镜面光照相对来说,要复杂一些。与漫反射光照一样,镜面光照也决定于光的方向向量和光照点的法向量,还取决于摄像机方向。反射方向与摄像机的方向越近,光照越强烈,由此产生的效果就是:我们看向入射光在表面的反射方向时,会看到耀眼的高光。

其中n是表面法线,v是相机方向,l是光照方向,r是反射方向,θ是观察向量与反射向量的夹角。假设以上向量均为单位向量,根据下图:

根据上图辅助线可以计算出反射向量 : r = 2(n.l)n - l

根据高光公式可计算出高光颜色:

· Cspec为计算高光颜色,

· Sspec光源颜色,

· Mspec物体颜色,

· ⊗表示颜色分量分别相乘,

· Mgls表示表面光泽度。

由以上公式可知光泽度越大,v.r不变的情况下其反射出来的光的辐射度越小,反过来说要保持同样大小的辐射度,v和r向量就要越靠近,此时反射出来的光斑越小越集中。光泽度越小光斑范围越大越分散也越接近漫反射。

attribute vec3 kzPosition;//输入点
attribute vec3 kzNormal;//输入法向量
uniform vec3 lightPos;//光源位置
uniform vec3 cameraPos;//摄像机位置
void main(){
vec3 viewDir= normalize(cameraPos-kzPosition);//观察方向
vec3 lightDir = normalize(lightPos - kzPosition);//入射光方向
vec3 reflectDir = reflect(-lightDir,N);// r = 2(n.l)n - l
float spec =pow(max(dot(viewDir,reflectDir),0.0),Mgls); //
vec3 specularColor = lightColor * objectColor * spec ;
}

至此,我们算出了phong光照模型的3个分量,phong光照模型的最终结果就是3个分量相加:

vec3 resultColor= ambientColor+diffuseColor+specularColor

Blinn-Phong光照模型

我们思考一下,如果camera与光源处于同一侧时,会发生什么呢?

此时反射向量relectDir和观察方向viewDir夹角大于90°,余弦值为负数,表现在渲染结果中,当旋转camera到于光源同一侧时,有部分高光突然消失了,这不符合真实的物理现象,如下图:

为了解决phong光照,camera和光源同一侧的问题, 引入了Blinn-Phong光照。

Blinn-Phong光照在Phong的基础上修改了高光部分,其他保存不变,引入了半程向量的概念(h向量),定义为入射光和观察者方向的中间向量,h向量和法向量之间的夹角越小,高光越亮,如下图:

Blinn-Phong 计算h向量特别简单,就是l+v,然后标准化就Ok了,简化了计算量并且效果还更好。

attribute vec3 kzPosition;//输入点
attribute vec3 kzNormal;//输入法向量
uniform vec3 lightPos;//光源位置
uniform vec3 cameraPos;//摄像机位置
uniform float Mgls;//光泽度,聚光度
void main(){
vec3 viewDir= normalize(cameraPos-kzPosition);//观察方向
vec3 lightDir = normalize(lightPos - kzPosition);//入射光方向
vec3 h = normalize(viewDir + lightDir );
float spec = pow(max(dot(kzNormal,h),0.0),Mgls); //
vec3 specularColor = lightColor * objectColor * spec ;
}

改进之后的效果对比如下:摄像机与光源同侧时,高光过渡的更真实。

当光泽度越大, 高光越小,并且越亮。这更符合常理。

到此,我们再去看phong/blinn-phong 材质shader时,是不是简单了很多。以上就是这篇文章的主要内容,篇幅有限,更多的细节就不一一赘述了,如果您有其他问题,欢迎留言探讨!


↓↓↓↓↓↓↓

喜欢此篇文章欢迎评论收藏分享支持小编~

更多技术干货,行业前沿动态,请关注上海怿星科技官方公众号:怿星科技eplanet

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值