1.光源
1.1点光源
点光源通过 位置和 发射光颜色来定义。-
光辐射衰减 : 点光源在空间中传播,会随着光源的的距离进行衰减.
f ( d ) = 1 a 0 + a 1 d + a 2 d 2 ( 局 部 光 辐 射 衰 减 公 式 ) f(d)=\frac{1}{a_0+a_1d+a_2d^2} (局部光辐射衰减公式) f(d)=a0+a1d+a2d21(局部光辐射衰减公式)
如果点光源的位置在无穷远, f ( d ) = 1.0 ( 如 果 光 在 无 穷 远 位 置 , d = 正 无 穷 ) f(d) = 1.0 (如果光在无穷远位置,d=正无穷) f(d)=1.0(如果光在无穷远位置,d=正无穷) -
解释 : 实际上光源的距离为d时,它的振幅将按照因子 1 / d 2 1/d^2 1/d2进行衰减,但是对于接近光源的对象, 1 / d 2 1/d^2 1/d2会产生过大的强度变化,而d很大时变化又太小,造成这样的原因是实际的光源并不是无穷小的点,而是用点发光体照明一个场景是真实光照效果的简单近似.所以加上 a 0 , a 1 , a 2 三 个 参 数 a_0,a_1,a_2三个参数 a0,a1,a2三个参数,使用者可以通过调整这三个系数的值,以得到场景中不同的光照效果,例如,当d非常小的时候,可以赋予 a 0 a_0 a0一个大的值来防止 f ( d ) f(d) f(d)变得很大.
对应WebGl和Three.js里面的光 :
1 . 光的距离在无穷远时,d=正无穷时,对应WebGL里面的平行光, 对应Three.js里面的方向光
- WebGL里需要给着色器传两个矢量,光色和光方向 : uniform vec3 u_LightCorlor和 uniform vec3 u_LightDirection.
- Three.js里面定义方向光的构造器DirectionalLight( color, intensity ).
//Three.js定义方向光的WebGL源码.
var uniforms = lightCache.get( light );
uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
uniforms.direction.setFromMatrixPosition( light.matrixWorld );
_vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( _vector3 );
uniforms.direction.transformDirection( viewMatrix );
2.光的位置在局部时,对应WebGL和Three.js里面的点光源.
- WebGL编程指南并没有关于点光源定义的着色器代码.
- Three.js里面定义点光源的构造器PointLight( color, intensity, distance, decay ).
//Three.js定义点光源的WebGL源码.
var uniforms = lightCache.get( light );
uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );
uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
uniforms.distance = light.distance;
uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;
1.2方向光
一个方向光通过一个*方向向量*和*从该方向开始的角度范围$\theta$*来确定(类比成光锥). $$cos\alpha= V_{obj}• V_{light}$$ $V_{obj}$表示光源到光照对象的单位方向向量.-
角强度衰减 :
f ( ϕ ) = c o s a ϕ ( a > 0 , 0 < ϕ < θ ) f(\phi)=cos^{a}\phi(a>0,0<\phi<\theta) f(ϕ)=cosaϕ(a>0,0<ϕ<θ)
如果光源不是一个投影光源, f ( ϕ ) = 1.0 , f(\phi)=1.0, f(ϕ)=1.0,如果对象处于光锥之外, f ( ϕ ) = 0.0 f(\phi)=0.0 f(ϕ)=0.0 -
解释 :衰减指数a是某个正值, ϕ \phi ϕ指圆锥轴到圆锥表面之间的夹角范围内的某个角度值.
-
对应Three.js里面的聚光灯 :
- Three.js里面定义聚光灯的构造函数:SpotLight( color, intensity, distance, angle, penumbra, decay )
//Three.js定义聚光灯的源码.
var uniforms = lightCache.get( light );
uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );
uniforms.color.copy( color ).multiplyScalar( intensity );
uniforms.distance = distance;
uniforms.direction.setFromMatrixPosition( light.matrixWorld );
_vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( _vector3 );
uniforms.direction.transformDirection( viewMatrix );
uniforms.coneCos = Math.cos( light.angle );
uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;
1.3环境光
通过设定场景的亮度来定义环境光.所有对象都相同的一个环境光,并且近似的给出了各个光照表面的全局漫反射.Three.js定义环境光的构造函数 : AmbientLight( color, intensity )
//Three.js里面定义环境光的源码.
r += color.r * intensity;
g += color.g * intensity;
b += color.b * intensity;
1.4Three.js里面的其他光
- 半球光:构造函数HemisphereLight( skyColor, groundColor, intensity )
//Three.js里面定义半球光的源码
var uniforms = lightCache.get( light );
uniforms.direction.setFromMatrixPosition( light.matrixWorld );
uniforms.direction.transformDirection( viewMatrix );
uniforms.direction.normalize();
uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );
uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );
- 长方形面光:构造函数RectAreaLight( color, intensity, width, height )
var uniforms = lightCache.get( light );
uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height ) );
uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );
_matrix42.identity();
_matrix4.copy( light.matrixWorld );
_matrix4.premultiply( viewMatrix );
_matrix42.extractRotation( _matrix4 );
uniforms.halfWidth.set( light.width * 0.5,0.0, 0.0 );
uniforms.halfHeight.set(0.0, light.height * 0.5, 0.0 );
uniforms.halfWidth.applyMatrix4( _matrix42 );
uniforms.halfHeight.applyMatrix4( _matrix42 );
2.对象表面光照效果
- 物体的光学特性是影响光照效果的主要因素,这些特性包括透明度,颜色反射系数,表面纹理参数.
2.1漫反射
粗糙或颗粒状表面会将反射光向各个方向发散出去,这样的反射称为 漫反射.- 理想漫反射体 :假设入射光在各个方向以相同的强度发散而与观察者的位置无关 . 理想漫反射体表面上反射光能量由朗伯余弦定律来计算.因此也被称为朗伯反射体.对于朗伯反射,光强度在所有观察方向都相同.
如果每一个表面都按照理想漫反射体来对待,不同的光源在漫反射下的反射光强度如下:
-
环境光下的漫反射 :
I a m b = k d I a ( k d 为 漫 反 射 系 数 , I a 为 环 境 光 强 度 ) I_{amb}=k_dI_a(k_d为漫反射系数,I_a为环境光强度) Iamb=kdIa(kd为漫反射系数,Ia为环境光强度) -
点光源下的漫反射 :
I = k d I l c o s θ ( k d 为 漫 反 射 系 数 , I l 为 入 射 光 强 度 , θ 是 入 射 光 与 平 面 法 向 量 夹 角 , 必 须 在 0 − 90 度 之 间 才 会 有 效 果 ) I=k_dI_lcos\theta(k_d为漫反射系数,I_l为入射光强度,\theta是入射光与平面法向量夹角,必须在0-90度之间才会有效果) I=kdIlcosθ(kd为漫反射系数,Il为入射光强度,θ是入射光与平面法向量夹角,必须在0−90度之间才会有效果) -
环境光和点光源同时存在下的漫反射 :
I a m b , d i f f = k a I a + k d I l ( N • L ) ( k a 是 环 境 光 反 射 系 数 , k d 是 漫 反 射 系 数 ) I_{amb,diff}=k_aI_a+k_dI_l(N• L)(k_a是环境光反射系数,k_d是漫反射系数) Iamb,diff=kaIa+kdIl(N•L)(ka是环境光反射系数,kd是漫反射系数)
k a 和 k d 都 由 表 面 的 材 质 决 定 , 对 于 单 色 光 而 言 其 值 都 介 于 0 到 1 之 间 . k_a和k_d都由表面的材质决定,对于单色光而言其值都介于0到1之间. ka和kd都由表面的材质决定,对于单色光而言其值都介于0到1之间.
2.2镜面反射
反射光集中成醒目或明亮的一个点,成为**镜面反射**.假设现有一表面,法向量是N,L表示光源方向,R表示反射光方向,V表示视点方向,L与N的夹角=R与N的夹角=$\theta,$R与V的夹角是$\phi$.*理想的镜面反射材料*只有当$\theta=\phi$的时候,观察者才能看到反射光 .非理想反射体系统系统的反射方向分布在向量R周围的有限范围内.较光滑的反射范围比较小,较粗糙的反射范围比较大.Phong Bui Tuong提出了一个计算镜面范围的经验公式,被成为Phong的反射模型.
(Phong)
I
s
p
e
c
=
W
(
θ
)
I
l
c
o
s
n
s
ϕ
(
V
•
R
<
=
0
或
者
N
•
L
<
=
0
时
,
I
s
p
e
c
=
0
)
I_{spec}=W(\theta)I_lcos^{n_s}\phi(V• R<=0 或者N• L<=0时,I_{spec}=0)\tag{Phong}
Ispec=W(θ)Ilcosnsϕ(V•R<=0或者N•L<=0时,Ispec=0)(Phong)
(
W
(
θ
)
表
示
镜
面
反
射
系
数
,
I
l
表
示
光
源
强
度
,
ϕ
表
示
观
察
方
向
V
与
反
射
方
向
R
的
夹
角
.
N
s
表
示
镜
面
反
射
参
数
,
由
材
料
决
定
)
(W(\theta)表示镜面反射系数,I_l表示光源强度,\phi表示观察方向V与反射方向R的夹角.N_s表示镜面反射参数,由材料决定)
(W(θ)表示镜面反射系数,Il表示光源强度,ϕ表示观察方向V与反射方向R的夹角.Ns表示镜面反射参数,由材料决定)
简化版的Phong模型可以用N•H代替V•R,H表示L和V的半角向量.
2.3漫反射和镜面反射的合并
(1) I = I d i f f + I s p e c I=I_{diff}+I_{spec}\tag{1} I=Idiff+Ispec(1) (2) = k a I a + k d I l ( N • L ) + W ( θ ) I l c o s n s ϕ =k_aI_a+k_dI_l(N• L)+W(\theta)I_lcos^{n_s}\phi\tag{2} =kaIa+kdIl(N•L)+W(θ)Ilcosnsϕ(2) (3) = k a I a + k d I l ( N • L ) + W ( θ ) I l ( N • H ) n s =k_aI_a+k_dI_l(N• L)+W(\theta)I_l(N• H)^{n_s}\tag{3} =kaIa+kdIl(N•L)+W(θ)Il(N•H)ns(3)
2.4多光源的漫反射和镜面反射的合并
(1) I = I a m b , d i f f + ∑ i = 0 n [ I l , d i f f + I l , s p e c ] I=I_{amb,diff} +\sum_{i=0}^{n}\ [I_{l,diff}+I_{l,spec}]\tag{1} I=Iamb,diff+i=0∑n [Il,diff+Il,spec](1) (2) = k a I a + ∑ i = 0 n I l [ k d ( N • L ) + k s ( N • H ) n s ] =k_aI_a+\sum_{i=0}^{n}I_l[k_d(N• L)+k_s(N• H)^{n_s}]\tag{2} =kaIa+i=0∑nIl[kd(N•L)+ks(N•H)ns](2)
3.表面绘制算法
Gouraud明暗处理
- 确定每个多边形顶点处的平均单位法向量.
- 对于每个顶点根据光照模型来计算其光强度.
- 在多边形投影区域对顶点强度进行线性插值.
缺点 : 表面上的高光有时会出现异常形状,线性光强度插值会造成表面上出现过亮或过暗的条纹,这被成为马赫带效应.
Phong明暗处理
- 确定每个多边形顶点处的平均单位法向量.
- 在多边形投影区域上对顶点法向量进行线性插值.
- 根据光照模型,使用插值的法向量,沿每条扫描线计算投影像素的光强度.
缺点:需要比Gouraud方法更多的计算.
快速Phong明暗处理
(泰勒展式)
I
d
i
f
f
(
x
,
y
)
=
T
5
x
2
+
T
4
x
y
+
T
3
y
2
+
T
2
x
+
T
1
y
+
T
0
I_diff(x,y) = T_5x^2+T_4xy+T_3y^2+T_2x+T_1y+T_0\tag{泰勒展式}
Idiff(x,y)=T5x2+T4xy+T3y2+T2x+T1y+T0(泰勒展式)
T
k
为
光
照
方
向
和
各
个
顶
点
的
光
照
强
度
确
定
的
函
数
.
T_k为光照方向和各个顶点的光照强度确定的函数.
Tk为光照方向和各个顶点的光照强度确定的函数.
缺点 : 虽然快速Phong的方法减少了Phong表面绘制算法的计算量,但它的计算量仍然相当于Gouraud表面绘制算法的两倍的时间.基本的Phong表面绘制算法比Gouraud绘制多耗时6到7倍.