首先我们将要了解如下问题
1. 学习Direct3D支持的光照资源, 以及它们照射出的灯光类型
2. 弄懂怎样定义灯光去影响其照射的表面
3. 找出怎样算出描述三角形的方向以便我们能够确定光线照射到三角形的角度
我们在这里将融合《3D游戏编程大师技巧》和《Direct3D》的部分内容。
即, 将它们一一映射。
颜色模型和材质
物体的材质决定了它将如何于光线交互, 材质用来描述物体被光线照射后将对光线产生何种影响。学习过物理课, 我们知道不同材料,或者说不同的原子对光子产生不同的影响。
这里说,也许不是那么容易理解, 我们还是来看看究竟光是什么吧。
首先我们回顾下光的基本的特性:
一、 光可以沿着直线传播
二、 光会发生折射
三、 光会发生反射
四、 光会发生色散
五、 光会发生干涉
六、 光会发生衍射
七、 光是一种电磁波
八、 光存储偏振
九、 光的高聚合形态-激光
十、 光电效应和光子
十一、 光即是波又是粒子
看, 我们的高中物理部分所讲解的光的现象有这么多有关光的特性。
下面我们将简述光的特性, 并联系如何联系到用Direct3D表达出来。
能够发光的物体叫做光源(Light Source). 太阳就是一个巨大的自然光源。
而到了夜晚人们不得不自己制作人造光源。人造光源有火把。蜡烛,电灯
等等。 根据光源发射光的不同性质, 我们可以将光源简单的归类:
一、 定向光源
定向光源是没有确切位置的光源, 它离场景非常远, 其光线平行第照射到表面(surface)上, 这种光源也被称为无穷远光源。定向光源的特征是其光照强度(Light Intensity)不会随距离而衰减, 这是因为光线已经传播了无穷远的距离。 定向光源本身也有强度和颜色。要定义定向光源, 只需指定其初始颜色和强度即可。为了表示光源的光照强度我们有这么个公式:
I(d)dir = I0dir * Cldir ( 光照强度 = 初始光源强度 * 光源颜色 )
I0dir - 表示初始光源强度, Cldir 表示光源的颜色, I(d)dir 表示将产生目标光的光照强度。 那么Direct3D中如何表示呢?
首先我们来看一个结构, 我们以Direct3D9为主:
typedef struct _D3DLIGHT9 {
D3DLIGHTTYPE Type; //光源的类型
D3DCOLORvalue Diffuse; //光源发出的漫射光颜色
D3DCOLORvalue Specular; //光源发出的镜面反射光颜色
D3DCOLORvalue Ambient; //光源发出的环境光颜色
D3DVECTOR Position; //光源的位置, 只对点光源和聚光灯有效
D3DVECTOR Direction; //光源的方向
float Range; //光照射的范围
float Falloff; //对聚光灯有效,表示内锥到外锥之间的光照强度衰减,通常为1.0f
float Attenuation0; //对点光源和聚光灯有效
float Attenuation1; //对点光源和聚光灯有效
float Attenuation2; //对点光源和聚光灯有效
float Theta; //只用于聚光灯,指定内锥的角度,单位是弧度
float Phi; //只用于聚光灯,指定外圆锥的角度,单位是弧度
} D3DLIGHT9;
这个结构用来存储光的属性
那么, 我们就利用这个结构来定制出不同的光源类型,其中D3DLIGHTTYPE Type; 有三种不同取值分别和点光源,定向光源,聚光灯一一对应,分别为:D3DLIGHT_POINT, D3DLIGHT_DIRECTIONAL, D3DLIGHT_SPOT.
定向光源最直接的现实例子就是我们的太阳, 它离地球非常远, 它的光线平行的照射到地球表面上,它的光不衰减。
二、 点光源
点光源用来表示实际生活中的电灯等形状像点一样的光源, 其光照强度随光源离表面S的距离衰减, 衰减使用三个因子来模拟, 常量衰减因子(kc)、线性衰减因子(kl)和二次衰减因子(kq) 用d (distance)表示位于点p处的点光源和表面S上的点s之间的距离, 光照强度和距离的关系如下
I(d)point = I0point * Clpoint / kc + kl * d + kq * d^2
其中 d = |p-s|
Andre LaMothe在实际应用中,发现不包含二次项的更简单的光源模型也很好使:
I(d)point = I0point * Clpoint / kc + kl * d;
三、 聚光灯
聚光灯的现实例子可以认为是手电筒,我们家唯一的家用电器。或者类似于手电筒的发光设备, 比如探照灯, 它们的特征是, 当光源将光线投射到表面时,会形成内锥和外锥。其中内锥的光照强度高一些,并逐步向外锥衰减。由于聚光灯复杂很多,所以在实时游戏中, 非常粗略地模拟聚光灯的模型就能满足需要。 我们使用简化后的公式:
I(d)spotlight = I0spotlight*Clspotlight * MAX(cosθ,0)^pf / kc + kl*d + kq*d^2
其中 d = |p-s| pf 为指数因子。
了解了三种基本光源类型后, 我们来看看前面没有展开的话题:
一、 光可以沿着直线传播
光在空气中沿着直线传播, 在水、玻璃等透明物质中的传播路线也是直的。
所以我们把光的传播方向的直线叫做光线(ray)。但是光线必须在均匀的介质中才沿着直线传播, 如果介质不均匀光线也会发生弯曲。例如地球周围的大气就是不均匀的。所以我们可以说 光在均匀介质中是沿着直线传播的。如果了解了引力的作用,我们也可以说在一般引力环境下,光线将沿着直线传播,如果在强引力作用下光线也将发生弯曲。
二、 光会发生折射(refraction)
光从一种介质斜射入另一种介质(medium)时, 传播方向一般会发生变换, 这种现象叫做光的折射。前面说过光从不均匀的空气中传播路径是弯曲的, 实际上这也是折射现象。
折射光线和入射光线分居法线两侧, 当入射角不和介质表面垂直时传播方向将改变。当垂直时,传播方向不变。
三、 光会发生反射
从光的入射点O 所做的垂直于镜面的线ON叫做法线, 入射光线与法线的夹角叫做入射角, 反射光线与法线的夹角叫做反射角。
光的反射可以分为镜面反射(Specular reflect)和漫反射(Diffuse reflect)
镜面很光滑, 射到平面镜上的平行光线反射后仍然是平行的。光滑镜面的反射叫做镜面反射。
一个凹凸不平的物体的表面被光线照射会发生漫反射(Diffuse reflect)。
不同介质的折射率不同, 我们把折射率较小的介质称为光疏介质(medium) , 折射率较大的介质称为光密介质。光疏介质和光密介质是相对的, 比如拿水、水晶和金刚石三种物质比较, 水晶对水来说是光密介质。对金刚石来说是光疏介质。当入射角达到一定程度,使折射角达到90度时,折射光完全消失, 只剩下反射光, 这种现象叫做全反射。
可见,很多时候光线的折射现象和反射现象是交互(interactive)发生的, 随时将因为入射角的改变而导致仅反射, 即反射又折射。
四、 光会发生色散
棱镜也叫三棱镜。 楞镜可以改变光的传播方向还可以使光发生色散。太阳、日光灯等发出的光, 没有特定的颜色, 叫做白光。一条光线照射到棱镜上,我们预期会看到白色亮线,但是实际上却出现了许多不同颜色的亮线。这条亮带叫做光谱。 这个现象说明白光实际上是由各种单色光组成的复色光。复色 在介质中由于折射率不同分解成单色光的现象, 叫做光的色散。
五、 光会发生干涉
我们现在已经知道,光即是李子又是电磁波,可以这样理解,光粒子沿着波动轨迹运动。那么我们先看由于光发生干涉而说明光是一种波的现象。首先我们看波的干涉。频率相同的两列波叠加, 使某些区域的振动加强, 某些区域的振动减弱, 而且振动加强的区域和振动减弱的区域相互隔开。 这种现象叫做波的干涉。 1801年,英国物理学家托马斯 杨在实验室里成功第观察到了光的干涉。 光发生干涉现象时, 光在一些地方互相加强, 在另一些地方互相削弱。
六、 光会发生衍射
光的衍射, 波的衍射现象, 波可以绕过障碍物继续传播, 这种现象叫做波的衍射。
由于光的波长很短, 所以一般我们看不到光的衍射现象。 而当光射向一个针孔、一条狭缝、一根细丝时, 可以清楚地看到光的衍射现象。
七、 光是一种电磁波
光的衍射和干涉证明了光是一种波。19世界60年代,麦克斯韦预先了电磁波的存在,并从理论上得出, 电磁波在真空中的传播速度应为3.11x10^8m/s, 而当时实验测得的光速为3.15x10^8m/s, 所以他认为这不是一种巧合, 它表明光与电磁现象之间有本质的联系。由此它提出光的本质上是一种电磁波。后来经过赫兹的实验证明光的速度和电磁波的速度确实相同, 这样就证明了光的电磁说的正确性。
八、 光的偏振
光的干涉和衍射现象说明光具有波动性, 但不能确定光究竟是横波还是纵波。但是光的偏振现象说明, 光是横波。 太阳、电灯等普通光源发出的光, 包含这垂直于传播方向上沿一切方向振动的光, 而且沿着各个方向振动的光波的强度都相同。 这种光叫做自然光。 自然光通过偏振片P(叫做起偏器)之后, 只有振动方向跟偏振片的透振方向一致的光波(light-wave)才能通过。 也就是说, 通过偏振片P的光波, 在垂直于传播方向的平面上, 只沿着一个特定的方向振动。 这种光叫做偏振光。 横波只沿着某一个特定的方向振动, 称为波的偏振。
九、 光的高聚合形态-激光
这种光非常好,我们经常期望在游戏中实现激光效果。Laser
普通光源由于向各个方向发射的光是不确定的, 发光频率也不一样, 所以这样的光叫非相干光。 只有频率相同、并满足一定条件的光才是相干光。 激光是一种人工产生的相干光。
激光的平行度非常好, 可以传播很远的距离后仍能保持一定的强度。
激光的亮度高, 在很小的空间和很短的时间内集中很大的能量。即, 激光可以产生高温高压条件。
十、 光电效应和光子
光电效应 – 在光照射下物体发射电子的现象, 叫做光电效应, 发射出来的电子叫做光电子。 最开始发现光电效应时物理学家们没有感到惊奇, 认为当光入射金属时, 金属里的自由电子会由于变化着的电场的作用而振动。 如果光足够强, 振幅足够大, 不论光的频率高低, 经过一段时间后电子的振幅就会很大, 就有可能飞出金属表面。
但是进一步研究发现, 对各种金属都存在着极限频率和极限波长, 如果入射光的频率比极限频率低, 那么无论光多强, 照射时间多长, 都不会发生光电效应。如果入射光频率高于极限频率, 即使光不强, 当它射到金属表面时也会观察到光电子的发射。
光电效应具有瞬时性, 即无论光的亮度如何,只要高于极限频率 光电子的产生都几乎是瞬时的, 不超过10^-9s
1900年, 德国物理学家普朗克在研究物体热辐射(radiation)的规律时发现, 只有认为电磁波的发射和吸收不是连续的, 而是一份一份地进行的, 理论计算的结果才能跟实验事实相符。这样的一份能量叫做能量子。 普朗克还认为每一份的能量子等于hv, 其中v是辐射电磁波的频率, h是一个常量, 叫做普朗克常量。 h = 6.63x10^-34Js.
受普朗克启发, 爱因斯坦于1905年提出, 在空间传播的光也不是连续的, 而是一份一份的, 每一份叫做一个光量子, 简称光子。 E = hv
那么 由于光本身是一种电磁波, 所以根据普朗克的结论, 光量子就是能量子。
好以上即浓缩了一份关于初中和高中关于光的特征的常识。
光照计算是基于大量的面法线(face normal)的, 这要求我们必须根据预先计算好的法线来执行光照计算: 要么在背面消除阶段执行光照计算, 要么在背面消除阶段将法线存储起来,以便在光照计算阶段使用它们。
着色处理
着色处理发生在光栅化和指定图元上的顶点颜色怎样被计算成像素颜色之间。目前这里有三种着色模式可用: 平面着色(float shading)和高洛德(Gouraud shading)。平面着色,图元像素的颜色是均匀的, 且就是指定图元第一个顶点的颜色。 因此一但三角形的第一个顶点被指定成红色, 那么它的其他三个顶点也将会是红色。
高洛德着色, 图元表面的颜色是由每个顶点通过线性插值来赋予。
着色模式受Direct3D设置状态决定。
IDirect3DDevice9::SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
IDirect3DDevice9::SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
IDirect3DDevice9::SetRenderState(D3DRS_SHADEMODE, D3DSHADE_PHONG);
光照首先要处理的如何定义光源, 如何执行着色, 如何定义材质。而纹理,材质(介质), 光照都可以看做是增强真实感的部分。
面法线(face normal) 是描述多边形表面方向的一个向量。
顶点法线(Vertex normals)也是基于同样的概念, 但是我们与其指定每个多边形的法线, 还不如为每个顶点指定。