OPENGL基本了解(十四) (光照基本数据)

一. 现实中的光照模型
1. 颜色是什么?

在没有学过光学的人的认识中,颜色是物体的自身属性,很多的物体的颜色是一成不变的,比如雪是白色,树叶是绿色,血液是红色。但如果我们试着呆在一 个隔绝任何光线的纯黑小屋中,我们会发现所有颜色都消失了,于是我们可以得到这样的结论:颜色并不是这样物体自身的属性,不同的颜色,是物体表面光学特性 和光线相互作用的结果。

日光是所有可见光的集合,照射与物体表面时,某些波长的光被吸收,某些波长的光线则被反射,反射的光线最终进入人的眼睛,投影在视网膜,形成了颜色。比如红色就是物体表面反射红光,吸收其余光线的结果。


2. 物体表面和光线不同夹角对颜色产生的影响:

我们都有这样的经历,玻璃杯,瓷器等表面光滑的对象,无论表面原来的颜色是什么,在日光下总会有一块纯白的“高光”区域,并且这个区域会随着光照方 向的变化或者人观察角度的变化,在物体表面移动。说明对于表面光滑的物体而言,颜色不但和表面特性,光线相关,还和光照角度和观察角度相关。


3. 物体表面光滑度对颜色的影响:

同一束光线,照在同样颜色的光滑表面上和粗糙表面上,视觉效果也是不同的,明显的区别是,在光滑表面上,通常都有那种前面说的高光效果,而且越光滑 越明显,在粗糙表面上,那种高光效果消失了,颜色的分布变的很均匀,颜色的变化也变的很平滑, 改变光线照射的角度,依然会对颜色的分布产生影响,但是似乎已经和观察角度无关了。这种反射,被称为漫反射,实际上漫反射依然严格遵循光线的反射和折射定 律,只不过光线在接触粗糙表面时,在微观尺度上表面的不规则形状之间进行了很多次的镜面反射,因此出射角度已经随机分布。 不过在计算器处理漫反射时,如果对微观尺度上的镜面反射进行完整的模拟,计算量过于庞大,所以会将其近似为均匀向所有方向散射


4. 自发光物体在光线中产生的颜色:

如果我们要仿真的物体本身是个光源,那么它在外部光源下会呈现什么? 我们可以自己动手实验 - 比如将发绿光的物体,置于红色外部光源下,可以观察到黄色。因此我们可以得到的结论是 自发光体的颜色由自发光颜色和外部光源叠加产生。

运行 OpenGL 程序在屏幕上显示的最终颜色,受场景中光线的特性以及物体反射和吸收光的属性(即材质)影响。 

在OpenGL 光照模型中光源和光照效果可以细分为红,绿,蓝三个部分,光源由红,绿,蓝强度来定义,而物体表面材料由其反射红,绿,蓝的程度和方向来定义。OpenGL 光照模型使用的计算公式是对于现实世界光照的一个近似但效果非常好并适合快速计算。

OpenGL 光照模型中定义的光源可以分别控制,打开或关闭,OpenGL ES支持最多八个光源。

OpenGL 光照模型中最终的光照效果可以分为四个组成部分:Emitted(光源), ambient(环境光),diffuse(漫射光)和specular(镜面反射光),最终结果由这四种光叠加而成。

http://img.iteches.com/images/92/194CFF88F0D5B7B3BDC6D56AF8315F.png

1. Emitted 
(emission)发射光: 一般只发光物体或者光源,这种光不受其它光源的影响。 2. ambient 环境光: 指光线经过多次反射后已经无法得知其方向(可以看作来自所有方向),可以成为环境光,该光源如果射到某个平面,其反射方向为所有方向。

Ambient 不依赖于光源的方向。就是哪些在环境中进行了充分散射的光,而无法分辨其方向的光。光线在物体表面上向各个方向上均匀泛射,场景中的物体都会泛射光,这些泛射光又会照射到其他 物体上继续被泛射,直到光子能量耗尽为止,这样整个场景中散布着这样的泛射光。在编程时,可通过设置一个颜色常量来表示环境光或使用 ambient occlusion map (环境闭包贴图)来处理环境光。在 OpenGL 中,全局环境光的强度为 (0.2, 0.2, 0.2, 1.0),这弱弱的白色全局环境光确保即使没有额外的光源,场景中的物体依然是可见的。在白天非阳光直射,和外部光源很多的情况下,现实环境中会弥散着几乎一致的亮度,OpenGL将这部分光称作环境光,它给3D世界整体亮度设定了基线。 3. diffuse 漫反射光:当一束平行的入射光线射到粗糙的表面时,因面上凹凸不平,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同 的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这个反射的光则称为漫射光。漫射光射到某个平面时,其反射方向也为所有方向。
diffuse 只依赖于光源的方向和法线的方向。因此为只和表面与光线入射夹角有关的光线。我们之所以能看到物体,就是因为物体将入射的光然后向各个方向反射(所以称之为漫反射)。物体的漫反射材质属性对物体的颜色起着决定性作用。 4. specular 镜面反射光: 一般指物体被光源直射的高亮区域,也可以成为镜面反射区,如金属。
specular依赖于光源的方向,法线的方向和视角的方向。不但和表面与光线入射夹角有关,而且还和观察角度有关的光线,并且根据表面光滑程度,呈现不同亮度,越光滑,越容易产生高光区域。在 OpenGL 中,镜面光的强度可通过光泽度(shininess)来调节。
 
  同样的,对应于光线的类型,OpenGL也定义了3种材质属性(Material):

Ambient,Diffuse, Specular. 每种材质类型的值实际上是代表各自的光线类型在最终合成的亮度上的权重 。也可以看作在物体表面,镜面反射,漫反射和环境光反射各占多大成分。

尽管光源可能只发送某一频率的光线,但ambient,diffuse和specular可能不同。比如使用白光照射一堵红墙,散射的光线可能为红色。OpenGL允许为光源分别设置红,绿,蓝三个元素的值。

最终决定所看到物体的颜色除了光源的颜色和方向外,还取决于物体本身的颜色,比如红色的光照在红色的物体和蓝色的物体,最终看到的物体一个还是红 色,一个为黑色。OpenGL 中对物体材料(Material)的颜色是通过其反射红,绿,蓝的比例来定义的。 和光源一样,物体的颜色也可以有不同的ambient,diffuse和specular,表现为反射这些光的比例。ambient,diffuse反射 通常为同样的颜色,而specular常常表现为白色或灰色光,如使用白光照射一个红色的球,球的大部分区域显示为红色,而高亮区域为白色。


如何决定入射光和表面的夹角 - 法向量:

OpenGL中的表面,都是由三角形组成的,三角形的三个顶点,可以决定一个平面,凡是平面,就会有法线,所以我们只要知道了这个平面的法向量,那么


入射光和平面的



90


入射光向量和法向量的



不过不同于前述的一个三角形对应一个法向量,OpenGL中的法线向量是和顶点联系在一起的(具体原因也许是数据组织的方便性),因此在光照模型 中,需要给每个顶点指定法向量,如不指定,则默认为(0,0,1)。最终三角形内部各个位置的法向量也由3个顶点的法向量线性插值而来。不过通常我们会保 持三角形三个顶点的法向量相同。

二,光照的计算


OpenGL




色的最

合成:

我们已经知道了这4种类型的光线,以及它们各自的反射亮度受哪些因素的影响,那么它们如何互相影响,最终决定一个顶点的颜色呢?

OpenGL给出了一套光线合成的标准公式,不论真实世界的光线是否真由如此组成,但这套公式无疑从经验上是最接近现实效果的。

以下是OpenGLES1.1的固定渲染管线中,它们的合成公式:

vertex color =


the material emission at that vertex +


the global ambient light scaled by the material’s ambient property at that vertex +


the ambient, diffuse, and specular contributions from all the light sources, properly attenuated


最终合成的颜色,即由自发光,全局环境光和所有点光源三部分叠加而成.

Ambient + Diffuse + Specular = Final (图右)

Ambient + Diffuse + Specular = Final

其中全局环境光的公式为:


global ambient light = ambientlight model * ambientmaterial

点光源部分又由ambient,diffuse,specular三种光源各自的公式相加而成,并且光强按离光源的距离衰减:

contribution = attenuation factor * spotlight effect *


(ambient term + diffuse term + specular term)

另外衰减因子(
attenuation factor) 和距离相关,距离越大,衰减因子越小。如不考虑衰减,则可用常量1代替。

光照计算的终极公式
光照颜色 = 发射颜色 + 全局环境颜色 + (环境颜色 + 漫反射颜色 + 镜面反射颜色) × 聚光灯效果 × 衰减因子

如果场景中有多个光源(包括环境光),那么分别计算来每个光源的光照颜色,然后把这些光照颜色累加即可。如果物体不发射光,则没有发射颜色这一成分。

以下是个别次项目的计算公式

1. 发射光计算 前面说过,在 OpenGL 中是给物体材质设置发射颜色来模拟发射光的,因此它的计算非常简单:


发射颜色 = 物体的发射材质颜色

2. 环境光计算 前面说到,一般我们是设置一个颜色常量来表示环境光(或来自 ambient occlusion map),因此环境光的计算也是很简单的。

其中环境光源公式:


ambient term=ambientlight *ambientmaterial


(环境颜色 = 光源的环境光颜色 × 物体的环境材质颜色)

3. 漫反射光计算 散射光源公式:





漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × DiffuseFactor

其中漫反射因子 DiffuseFactor 是光线与顶点法线向量的点积:


(df)DiffuseFactor = max(0, dot(N, L))      ***  0 <= df <= 1

在图形学中,点积几何意义其实就是表示两个向量之间夹角的 cos 值。因此这个公式直观地揭示了漫反射的规律:顶点法线正对入射光线,漫反射效果最强,顶点法线背对入射光线(角度大于等于90度)就完全没有漫反射效果。

diffuse_light

In the diagram,
L is the unit length vector pointing to the light source, and
N is the
surface normal, which is a unit-length vector that’s perpendicular to the surface.

注意:在光照计算中,顶点法线必须是经过规范化的(normalize)。

如果将光源设在无限远处,则可以简化公式如下


我们可以设置一个颜色常量来表示漫反射材质颜色,来得到平滑颜色的表面。但有些物体表面是很粗糙的,如裂缝,隆起,刮痕等。为了获得这样的粗糙表面 效果,可以使用 displaced polygon 技术,但这需要大量的计算,效率低下。另外一种更为高效的方式就是使用凹凸贴图(bump map)。凹凸贴图就是一种纹理,其内容包含编码在RGB颜色空间中的经过扰动的顶点法线。在使用凹凸贴图进行漫反射计算时时,首先和前面一样用正常法线 计算出 NormalDiffuseFactor,再根据扰动的顶点法线计算出 PerturbedDiffuseFactor,然后将两者相乘作为最终的 DiffuseFactor。如下图所示:

 
diffuse 
diffuse_bump_map

4. 镜面光源公式:

镜面反射颜色 (
Specular Lighting)= 光源的镜面光颜色 × 物体的镜面材质颜色 × SpecularFactor 


SpecularFactor = power(max(0, dot(N, H)), shininess)


H = normalise(L + E)

当观众在无限远处时,E可以简化成 
[0, 0, 1]

与漫反射不同,镜面反射受观察者的位置影响,这一点在上面的计算公式中可以清楚地看出来。

1. H 向量是视线向量
E 与光线向量
L 的半向量(注意:它经过规划化的)H向量又称为半角(half-angle),因为它是位于E与L的中间。

2. L代表的是光线入射方向的反向,其几何意义就是视线与光线夹角的平分线。

3. 而
H 和 N 的点积的几何意义就是说这个平分线与法线的夹角的 cos 值,然后将这个 cos 值进行 shininess 次乘方计算得到最终的镜面反射因子 Specularfactor。

这里的关键点在于视线与光线的平分线与法线的点积计算上,这表示,当视线与光线在表面处的反射光线夹角(可看成是N与H的夹角)越少时,镜面反射效果最明显(角度越小,cos 值越大)

一般可使用常量颜色作为镜面材质颜色,但这样得到的效果有时候并不理想。同漫反射一样,我们也可以使用镜面贴图来获得更好的镜面反射效果。镜面贴图通过控制物体表示上特定像素允许的镜面反射强度来获得较为真实的效果。

specular_theroy

 

普通镜面反射                               镜面贴图反射


specular  
specular_map

5. 衰减因子 光源发射的光线在其传播过程中,会与空气中的其他粒子碰撞,其能量会逐渐衰减。在 OpenGL 中,这是通过将光照强度乘以随传播距离变化的衰减因子来模拟实现的。这个衰减因子的计算公式如下:


衰减因子 = 1.0/(距离衰减常量 + 线性衰减常量 × 距离 + 二次衰减常量 × 距离的平方)


其中距离衰减常量,线性衰减常量和二次衰减常量均为常量值。

注意:环境光,漫反射光和镜面光的强度都会受随着距离的增大而衰减,只有发射光和全局环境光的强度不会受此影响。

6. 聚光灯因子 在这里,我们讨论聚光灯的发射光照计算(也即位置型光源:如台灯,相对方向性光源:如太阳)。聚光灯就是朝某个特定发射发射光线的光源。你可以想象 下漆黑的夜晚里,一个手电筒给与你光明的这个场景,手电筒就是一个很好的聚光灯示例。聚光灯的计算分为两部分:在光线照射角度范围之外的部分被忽略,只有 在照射角度范围之内的部分需要计算。


聚光灯夹角cos值 = power(max(0, dot(单位光源向量, 单位光线向量)), 聚光灯指数)

其中单位光线向量是从光源指向顶点的单位向量,聚光灯指数表示聚光灯的亮度程度。前面说过点积的几何意义就是表示角度的,这里聚光灯因子就表示光源向量与光线向量之间的夹角。

而为了模拟真实聚光灯光环效果,在照射角度范围之内与之外的接壤处,设置一个渐变过渡区域,以避免光照从有到无巨变:

无过渡 有过渡
spotlight spotlight_falloff

增加过渡区之后的计算如下:


spotlight_falloff_compute

内环是完全光照的,而过渡区域(中环)是从完全光照到完全没有光照的渐变过渡,外环是完全没有光照的。


聚光灯因子 = clamp((外环的聚光灯角度cos值 – 当前顶点的聚光灯角度cos值)/(外环的聚光灯角度cos值 – 内环聚光灯角度cos值), 0, 1)

clamp 函数是将聚光灯因子限定在[0, 1]之间,这样,内环是完全光照的,而中环是从完全光照到没有光照的渐变过渡,外环是没有光照的。因此:


聚光灯效果 = 聚光灯光源颜色  × 聚光灯因子

三,高洛德着色(Gouraud Shading)与冯氏着色(Phong Shading) 在图形渲染中有两种着色方式,高洛德着色与冯氏着色。高洛德着色也被称为Per-Vertex着色,它是在顶点着色阶段对顶点进行颜色计算,然后在 光栅化阶段对这些顶点颜色进行线性插值形成片元的颜色;冯氏着色也被称为Per-Pixel像素着色,它是在片元着色阶段对每一个片元(像素)进行颜色计 算。无疑,插值的颜色效果没有针对每一个片元进行颜色计算的效果好(除非你的图元切分到像素近似大小,不过这样 GPU 肯定吃不消,计算量巨大!)。

Gouraud 着色 Phong 着色
Gauroud Phong


参考网页

http://www.cnblogs.com/kesalin/archive/2012/12/29/light_theroy.html

http://rritw.com/a/bianchengyuyan/C__/20130306/318310.html

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页