欢迎阅读指正和转载,但请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com
 
深度探索DxFramework
 
第10章 进入三维的世界 7
 
10.7 照亮世界,灯光类
 
10.7.1 光照与渲染效果
 
光用于照亮场景中的物体。在渲染流水线中,流水线将根据光源来计算各个光照信息。当启用光照功能时,Direct3D根据下列组合计算每个顶点的颜色:
 
当前模型所使用的材质颜色及相关纹理贴图中的纹理像素(texels)。
顶点如果有指定的的漫反射色和镜面反射色,则使用之。
场景中光源产生的光的颜色和强度,或者场景中环境光的级别。
 
当使用Direct3D流水线中固定管道来处理光照和材质运算时,应用程序允许Direct3D代为处理照明的各种细节。如果需要的话,用户可以撰写顶点着色器(vertex shader)自己撰写的光照(lighting)计算代码。
 
应用程序使用光照和材质(material)渲染得到的场景,与不使用光照和材质的场景有很大外观差别。物体材质定义了光在物体模型表面上如何反射。直射光和环境光等级(level)定义了反射的光的种类。如果启用了光照,应用程序渲染场景时必须使用材质,才能计算表面的光照效果。
 
对于渲染场景而言,光不是必须的,但如果渲染的场景没有光照的话,那么会有许多细节不可见。渲染一个未经光照的场景至多也只能得到场景中物体的轮廓,显然这对大多数用途而言是远远不够的。
 
在自然界中,光从光源发出后,将会经过了多次的反射。每经过一次反射,一部分光被反射物体的表面所吸收,一部分被散射到各个方向,剩余的则被反射到其它表面或用户的眼睛里。这个过程一直持续,直到光衰减为零或到达用户眼中。
 
要完美地模拟自然光所需的计算量是相当浩大的,无法用于实时三维图形系统中。从效率的角度上考虑,Direct3D的光照模型对自然光采取了相当大的简化。Direct3D用红、绿、蓝三原色描述光,并将它们合成产生最终的颜色。在Direct3D中,当光从表面反射时,光的颜色与表面本身的反射属性相互作用,并产生最终显示在屏幕上的颜色。
 
10.7.2 Direct3D中光的分类
 
在前面的渲染流水线的介绍中,我们了解Direct3D光照模型将光归纳为两类:环境光(ambient light)和直射光(direct light)。每种光具有不同的属性,并以不同的方式与表面材质相互作用。
 
环境光是经过多次反射的光。所以它的方向和来源都无法确定。它给场景的各处提供一个均匀等值的光的强度。Direct3D中的环境光和自然界中的一样,没有实际的方向和光源,只有颜色和光强。事实上,环境光的级别完全独立于场景中的任何发光物体。环境光不参与镜面反射(specular reflect)。可以调用IDirect3DDevice8::SetRenderState方法设置环境光的级别,将State参数指定为D3DRS_AMBIENT,将Value参数设置为希望的光的颜色值。
 
直射光是场景中的由光源产生的光,它总是具有颜色和强度,并沿特定的方向传播。直射光与表面材质相互作用可产生镜面反射高光(specular reflect highlight)。直射光的方向将用作各种着色算法(例如Gouraud着色算法)中的一个因子。直射光和环境光是相互不干扰的。当直射光反射时,它不影响场景中的环境光级别。场景中可以产生直射光的光源具有不同的特征,这些特征将会影响光照亮场景的方式。不同类型的光源以不同的方式发出直射光,产生特殊的衰减效果。通过调用IDirect3DDevice8::SetLight方法,可以设置直射光参数
 
此外,多边形的材质(material)具有一些属性,这些属性将会影响多边形如何反射它接收的光线。应用程序可以设置一个专门的反射系数,用于描述材质如何反射环境光,应用程序还可以设置另一个反射系数以决定材质的镜面反射和漫反射。
 
虽然直射光与环境光都用于照亮场景中的物体,但它们互不相关,也互不影响。它们具有截然不同的效果,要按照完全不同的方法使用。
 
10.7.3 光与材质颜色的数据表示
 
Direct3D用红色、绿色、蓝色和alpha值来描述颜色,并将它们合成产生最终颜色。这样的原因是,在自然界中,每一种可见光都可以分解为按不同成分比例混合的红,绿,蓝色光。这三种光可称为三原光。alpha值则表示透明度。Direct3D定义了D3DCOLORVALUE结构,该结构包含了上述四个成员,每个成员是一个浮点数,一般在[0.0,1.0]范围内。光和材质使用相同的结构描述颜色,但对结构中的值的使用略有差异。
 
光的颜色值表示它的各个组成光的级别。因为光不包含alpha值,所以只有红光、绿光和蓝光的成员是有用的。三种光的颜色将组合成光的最终颜色。如红光值: 1.0,绿光值G: 1.0,蓝光值B: 1.0将组合成白光,而红光值:0.0,绿光值:0.0,蓝光值: 0.0则完全不发光。也可以产生只发出某个成员颜色的光,这样可以得到纯红、纯绿或纯蓝光,或者也可以将它们组合,得到黄色或紫色。还可以将颜色的成员设为负值,这样就产生了实际上将光从场景中移除的“暗光(dark light)”,得到场景变暗的效果。或者,也可以将成员设为大于1.0的值,产生特别亮的光。
 
Direct3D中光源发出三种光,这三种光的颜色将单独用于系统的光照计算:漫反射色、环境反射色、和镜面反射色。每种颜色在Direct3D光照模型的协助下,与当前物体材质中的对应部分相互作用,产生用于渲染的最终颜色。漫反射色与当前材质的漫反射系数属性相互作用,镜面反射色与材质的镜面反射反射系数属性相互作用,依次类推。Direct3D定义的D3DLIGHT8结构包含了三个与这些颜色——漫反射色、环境反射色、和镜面反射色——相对应的成员,每个成员都是一个D3DCOLORVALUE结构,定义了光所发出的颜色。
 
对系统计算量影响最大的颜色类型是漫反射色。最常用的漫反射色是白色(R:1.0 G:1.0 B:1.0),但是应用程序可以根据需要创建其它颜色以达到想要的效果。例如,应用程序可以为火炉使用红光,或者为处于“通行”状态的红绿灯使用绿光。一般来说,应用程序将光的颜色成员设为[0.0,1.0]之间的值,但不是一定要在这区间的值。例如,应用程序可以将所有成员设为2.0,这样子创建出一个“比白色更亮”的光源。当应用程序使用的衰减值不为常数时,这种设定尤其有用。虽然Direct3D使用RGBA值表示光的颜色,但是并没有使用颜色的alpha分量。
 
材质的颜色值表示用该使用该材质的表面对某种光成分的反射度。颜色成员为红光值:1.0,绿光值:1.0,蓝光值: 1.0,,Alpha值:1.0的材质会反射所有的入射光。同样地成员为红光值:0.0, 绿光值:1.0,蓝光值:0.0,Alpha值:1.0的材质会反射所有入射的绿光。同时具有漫反射、镜面反射等多重反射系数值的材质可以创建不同类型的效果。
 
10.7.4 光源的类型和属性
 
光源的类型属性定义了正在使用的光是什么类型。光源的属性由D3DLIGHT8结构的Type成员的值设置,在C++程序中该成员是一个D3DLIGHTTYPE枚举类型。Microsoft® Direct3D®中有三种类型的光——点光源、聚光光源和平行光。每种类型以不同的方式照亮场景中的物体,所需的计算量也不同。D3DLIGHTTYPE8枚举值和D3DLIGHT8结构如下:
 

typedef enum _D3DLIGHTTYPE {
    D3DLIGHT_POINT          = 1,      //点光源
    D3DLIGHT_SPOT           = 2,      //聚光源
    D3DLIGHT_DIRECTIONAL    = 3,  //有向光
   D3DLIGHT_FORCE_DWORD    = 0x7fffffff,
} D3DLIGHTTYPE;

typedef struct _D3DLIGHT8 {
    D3DLIGHTTYPE      Type;              //光源类型,D3DLIGHTTYPE中定义
    D3DCOLORVALUE   Diffuse;           //光源产生的光中用于漫反射计算的值
    D3DCOLORVALUE   Specular;         //光源产生的光中用于镜面反射计算的值
    D3DCOLORVALUE   Ambient;        //光源产生的光中用于环境光计算的值
    D3DVECTOR           Position;        //在世界空间中光源的位置 
    D3DVECTOR           Direction;       //在世界空间中光的方向
    float                      Range;            // 作用范围
    float                      Falloff;           //辐射值
    float                      Attenuation0;     //常量衰减值
    float                      Attenuation1;    //线性衰减值
    float                      Attenuation2;    //二次方衰减值 
    float                      Theta;            //聚光光源的内锥角,弧度为单位
    float                      Phi;              //聚光光源的外锥角,弧度为单位
} D3DLIGHT8;
 
场景中的点光源(point light)具有颜色和位置,但没有确定的方向。点光源向各个方向发出的光相等,如下图所示:
 
 
灯泡是点光源的一个很好的例子。点光源的光照效果受它的衰减值和作用范围的影响。定光源基于每个顶点对网格进行照明。在计算光照的过程中,Direct3D使用点光源在世界坐标中的位置和当前顶点的再世界坐标中的位置,得到光的方向向量,以及光传播的距离。两者连同顶点法向一起,用于计算光在表面照明中所起的作用。
 
有向平行光(directional parallel light)光源只有颜色和方向,没有具体位置。有向平行光源发出平行的光,意味着所有的有向平行光产生的光在场景中以相同的方向传播。所以可以认为有向平行光源位于无穷远处,比如太阳就可以认为是一个有向平行光源。有向平行光源发出的光不受衰减和范围的影响,因此光的方向和颜色是Direct3D计算顶点颜色时要考虑的唯一因子。因为照明因子的数量少,所以有向平行光是可用的计算量最小的光。
 
聚光光源(spot light)具有颜色、位置和发出光的方向。聚光光源发出的光由一个比较亮的内圆锥和一个较大的外圆锥组成,光强由内而外逐渐减小,如下图所示:
 
 
聚光光源受辐射(falloff)、衰减(attenuation)和范围(range)的影响。这些因子与光源到每个顶点的距离一起,参与计算场景中物体的光照效果。由于需要对每个顶点计算这些效果,因此这使得聚光光源成为Direct3D所有类型的光源中最为耗时的。
 
D3DLIGHT8结构包含了三个仅用于聚光光源的成员。这些成员——Falloff、Theta、和Phi——控制聚光光源内外锥的大小,以及光如何在两者之间减弱。Theta值为聚光光源内锥的角度,以弧度为单位,Phi值为聚光光源外锥的角度。Falloff值控制光强如何从内锥的外侧向外锥的内侧减弱。大多数应用程序将Falloff设为1.0,使光在两个圆锥间平滑地减弱,但也可以根据需要设成其它值。下图显示了这些成员的值之间的关系,以及它们如何决定聚光光源的内外锥。
 
 
聚光光源发出圆锥形的光,圆锥分为两部分:较亮的内锥和外锥。内锥中的光最亮,而外锥以外则没有光,在内外锥之间光强逐渐衰减。这种衰减一般被称为辐射。一个顶点接收到的光的数量(简称为光量)取决于顶点在内锥或外锥中的位置。Direct3D计算聚光光源的方向向量L和从顶点到聚光光源的向量D的点积。这个值等于两个向量夹角的余弦值,并作为顶点位置的一个指标,可与聚光光源的圆锥角度进行比较以确定顶点位于内锥或外锥的何处。下面提供了这两个向量之间关系的表示图:
 
 
Direct3D将此值与聚光光源的内锥及外锥角度的余弦值进行比较。在D3DLIGHT8结构中,Theta和Phi成员表示内锥和外锥的整个圆锥的角度。因为衰减和顶点与照明中心(亦即圆锥的中线)的夹角有关,所以在计算余弦值时Direct3D会将这两个圆锥的角度除以二。如果向量L和D的点积小于等于外锥角度的余弦值,那么顶点位于外锥以外,不接收任何光。如果L和D的点积大于内锥角度的余弦值,那么顶点位于内锥里面,接收最多的光量,此时仍需考虑光随距离的衰减。如果顶点位于这两个区域之间的某处,那么Direct3D使用以下公式计算顶点的辐射值。
 
 
在这个公式中,If为辐射后的光强,对正在计算光照的顶点来说,α为向量L和D之间的夹角,F为二分之一外锥角度的余弦值,φ为二分之一内锥角度,为聚光光源在D3DLIGHT8结构中的辐射属性Falloff。这个公式产生一个从0.0到1.0之间的值,用这个值对光强进行缩放就产生了辐射的效果。作为从顶点到聚光光源的距离的因子,衰减同时也被使用。p值对应D3DLIGHT8结构的Falloff成员,控制辐射曲线的形状。下图显示了不同的Falloff值如何作用于辐射曲线。
 
 
在实际光照中,不同的Falloff值产生的效果是很敏感的,并且如果使用除了1.0之外的值作为Falloff值描述辐射曲线,那么还会导致些许性能下降。为此,这个值一般被设为1.0。
 
10.7.5.光照的数学计算公式
 
Direct3D的光照模型涵盖了环境光、漫反射光、镜面反射光和放射光,对于大部分的光照情况而言足够了。场景中光的总和称为全局照明(global illumination),并使用以下公式计算:
 
全局照明 = 环境光 + 漫反射光 + 镜面反射光 + 放射光
 
环境光是恒定的光照。它在所有方向上不发生变化,对物体中所有像素产生的作用也完全相同。环境光是最快的一种类型的光,,但得到的物体看起来是平面的,没有真实感。环境光对所有物体的顶点的照明效果相同,因为它与其余光照因子无关,如顶点法向、光的方向、光的位置、范围或衰减等。Direct3D包含了一个全局的环境光属性,应用程序可以直接使用而无需创建任何光源。另外,应用程序也可以指定某个光源提供环境光。场景中物体的顶点所反射的环境光由以下公式描述。
 
Ambient Lighting = Ca*[Ga + sum(Lai)*Atti*Spoti]
 
这里Atti和Spoti为第i个光源的衰减和聚光灯因子。参数的定义如下表:
 
参数
默认值
类型
描述
Ca
(0,0,0,0)
D3DCOLORVALUE
材质的环境反射色
Ga
(0,0,0,0)
D3DCOLORVALUE
全局的环境反射色
sum
N/A
N/A
所有光源产生的环境光的总和
Lai
(0,0,0,0)
D3DCOLORVALUE
第i个光源产生的环境反射色
Atteni
(0,0,0,0)
D3DCOLORVALUE
第i个光源的衰减因子
Spoti
(0,0,0,0)
D3DCOLORVALUE
第i个光源的聚光灯因子
 
漫反射光取决于光的方向和表面的法向。由于光的方向和表面法向量的变化,因此漫反射光会随物体的表面而变化。因为漫反射光随着每个顶点而变化,所以需要更长的时间进行计算,但是使用漫反射光带来的好处是它使物体呈现出明暗变化和三维深度。漫反射光在根据任何衰减效果调整完光的强度之后,光照引擎用给定的顶点法向与入射光方向之间的夹角,计算剩余的光中有多少会从顶点反射。对于平行光,光照引擎略过这一步,因为平行光不随距离而衰减。系统会考虑两种反射类型,漫反射和镜面反射,并使用不同的公式计算每种类型各反射多少光。在计算完反射光的数量后, Direct3D把得到的新值应用于当前材质的漫反射和镜面反射反射系数属性。最终的颜色值是漫反射色和镜面反射色成员,会被光栅化器用于计算Gouraud着色和镜面反射高光。漫反射光由以下公式描述。
 
Diffuse Lighting = sum[Cd*Ld*(N.Ldir)*Atten*Spot]
 
参数定义如下表:
 
参数
默认值
类型
描述
sum
N/A
N/A
每个光源的漫反射色成分的总和
Cd
(0,0,0,0)
D3DCOLORVALUE
漫反射色
Ld
(0,0,0,0)
D3DCOLORVALUE
光源的漫反射色
N
N/A
D3DVECTOR
顶点法向
Ldir
N/A
D3DVECTOR
从顶点到光源的方向向量
Atten
N/A
FLOAT
光的衰减因子
Spot
N/A
FLOAT
聚光灯因子
 
Cd的值可以是:
 
顶点颜色1
如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR1,并且顶点声明中给出了第一个顶点的颜色。
顶点颜色2
如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR2,并且顶点声明中给出了第二个顶点的颜色。
材质的环境反射色
-
 
注意 如果使用了任何一种DIFFUSEMATERIALSOURCE,但是没有提供顶点颜色,那么系统会使用材质的漫反射色。要计算衰减(Atten)或聚光灯(Spot)属性,在所有光源都经过单独处理和插值后,漫反射成员被截取到从0到255之间。最终的漫反射光的颜色值是环境光、漫反射光和放射光的颜色值的组合。
 
镜面反射光代表了当光线照射到物体表面时反射回摄像机形成的明亮的镜面反射高光。它比漫反射光更强,但在物体表面也衰减得更快。计算镜面反射光需要比计算漫反射光更长的时间,但是使用镜面反射光带来的好处是它给表面增添了重要的细节。建立一个镜面反射模型需要系统不仅知道光传播的方向,还要知道从顶点到视点的方向。Direct3D光照系统使用了一个经过简化的Phong镜面反射模型,该简化模型使用一个半角向量(halfway vector)计算镜面反射光强的近似值。默认的光照状态不计算镜面反射高光。要启用镜面反射高光,需要将D3DRS_SPECULARENABLE设置为TRUE。
镜面反射光由以下公式描述。
 
Specular Lighting = Cs*sum[Ls*(N.H)P*Atten*Spot]
 
下表描述了所有变量,其类型及范围。

参数
默认值
类型
描述
Cs
(0,0,0,0)
D3DCOLORVALUE
镜面反射色
sum
N/A
N/A
每个光源的镜面反射成员的总和
N
N/A
D3DVECTOR
顶点法向量
H
N/A
D3DVECTOR
半角向量
P
0.0
FLOAT
镜面反射反射指数。范围从0到正无穷大
Ls
(0,0,0,0)
D3DCOLORVALUE
光的镜面反射色
Atten
N/A
FLOAT
光的衰减值
Spot
N/A
FLOAT
聚光光源因子
 
Cd的值可以是:
 
顶点颜色1
如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR1,并且顶点声明中给出了第一个顶点的颜色。
顶点颜色2
如果DIFFUSEMATERIALSOURCE = D3DMCS_COLOR2,并且顶点声明中给出了第二个顶点的颜色。
材质的环境反射色
-
 
注意 如果使用了任何一种SPECULARMATERIALSOURCE,但是没有提供顶点颜色,那么系统会使用材质的镜面反射色。在所有光源都经过单独处理和插值后,镜面反射成员被截取到从0到255之间。
 
半角向量(H)位于两个向量之间:从顶点到光源的向量,和从顶点到摄像机位置的向量。Direct3D提供了两种方法来计算半角向量。当D3DRS_LOCALVIEWER被设为TRUE时,系统使用摄像机的位置和顶点的位置,与光源的方向向量来计算半角向量。以下公式描述了这种方法。
 
H = norm(norm(Cp - Vp) + Ldir)
 
参数描述如下表:
 
参数
默认值
类型
描述
Cp
N/A
D3DVECTOR
摄像机的位置
Vp
N/A
D3DVECTOR
顶点的位置
Ldir
N/A
D3DVECTOR
从顶点位置到光源位置的向量
 
用这种方法计算半角向量的计算量会非常大。另一种可选的方法是将D3DRS_LOCALVIEWER设置为FALSE,告诉系统将视点当作在z轴无限远处进行计算。下面的公式反映了这种方法。
 
H = norm((0,0,1) + Ldir)
 
这种设定计算量不很大,但很不精确,因此它最适合用在使用正交投影的应用程序中。
 
放射光是物体发出的光,例如光晕(glow)。通过在三维场景中使用这些类型的光,可以得到真实的光照效果。要得到更为真实的光照效果,应用程序可以添加更多的光源,但是,这增加了渲染场景的时间。要达到(游戏)设计师想要的所有效果,一些游戏使用了超出一般的CPU计算能力。在这种情况下,一般通过在使用纹理贴图的同时,使用光照贴图和环境贴图给场景加入光照的效果。这样就可以把光照所需的计算量减到最少。
 
在Direct3D中,光照计算是在观察空间中进行的。经过优化的光照计算可以在建模空间进行,但需满足以下特殊条件:法向量已经归一化(D3DRS_NORMALIZENORMALS为TRUE),不需要进行顶点混合,变换矩阵为正交的,等等。环境光、漫反射光和镜面反射光会受到给定光源的衰减和聚光光源因子的影响。为环境光、放射光和漫反射光成分计算的颜色值被保存在输出顶点的漫反射色中。漫反射和镜面反射公式都包含了衰减和聚光灯因子属性。
 
经过上面的详细分析,读者对Direct3D体系中的灯光也应有一定的了解了。DxFrameWork中有C_Light类,其实就是对Direct3D的灯光作了一层的封装。代码如下:
 
extern C_View* gp_View;
vector<C_Light*> C_Light::allLights;
unsigned int C_Light::lightIndex;

C_Light::C_Light(D3DLIGHTTYPE type) 
{
    ZeroMemory(&light, sizeof(D3DLIGHT8));
    index = lightIndex++; //每一个灯光都有一个ID
    light.Type = type; //设置灯光的类型

   //设置灯光的各种参数
   light.Attenuation1 = 1.0f;
   light.Direction.z = 1.0f;
   light.Phi = D3DX_PI;
   light.Theta = D3DX_PI / 2;
   light.Falloff = 1.0f;
   allLights.push_back(this);
   enabled = false; //暂不启用本灯光
}

C_Light::~C_Light() 
{
    //清空本灯光
    vector<C_Light*>::iterator iter = allLights.begin();
    while(iter != allLights.end()) {
       if ((*iter) == this) {
         allLights.erase(iter);
         break;
      }
      iter++;
   }

  if (allLights.empty()) {
    allLights.clear();
  }
  //禁用本灯光
  C_View::p_D3DDevice->LightEnable(index, FALSE);
}

void C_Light::Enable() 
{
    C_View::p_D3DDevice->LightEnable(index, TRUE);
    enabled = true;
}

//禁用本灯光
void C_Light::Disable()
{
    C_View::p_D3DDevice->LightEnable(index, FALSE);
    enabled = false;
}

void C_Light::Update()
{
     C_View::p_D3DDevice->SetLight(index, &light);
}
// 接下来是对灯光属性的一系列设置读取操作接口。
void C_Light::SetDiffuse(D3DCOLORVALUE diffuse){ light.Diffuse = diffuse; }
void C_Light::SetSpecular(D3DCOLORVALUE specular){ light.Specular = specular; }
void C_Light::SetAmbient(D3DCOLORVALUE ambient){ light.Ambient = ambient; }
void C_Light::SetPosition(D3DXVECTOR3 position){ light.Position = position; }
void C_Light::SetDirection(D3DXVECTOR3 direction){ light.Direction = direction; }
void C_Light::SetRange(float range){ light.Range = range; }
void C_Light::SetFalloff(float falloff) { light.Falloff = falloff; }
void C_Light::SetAttenuation0(float attenuation0){light.Attenuation0 = attenuation0; }
void C_Light::SetAttenuation1(float attenuation1){ light.Attenuation1 = attenuation1; }
void C_Light::SetAttenuation2(float attenuation2){ light.Attenuation2 = attenuation2; }
void C_Light::SetTheta(float theta) { light.Theta = theta; }
void C_Light::SetPhi(float phi){ light.Phi = phi; }
D3DCOLORVALUE C_Light::GetDiffuse() { return light.Diffuse; }
D3DCOLORVALUE C_Light::GetSpecular(){ return light.Specular; }
D3DCOLORVALUE C_Light::GetAmbient(){ return light.Ambient; }
D3DXVECTOR3 C_Light::GetPosition() { return light.Position; }
D3DXVECTOR3 C_Light::GetDirection(){ return light.Direction; }
float C_Light::GetRange() { return light.Range; }
float C_Light::GetFalloff() { return light.Falloff; }
float C_Light::GetAttenuation0(){ return light.Attenuation0; }
float C_Light::GetAttenuation1(){ return light.Attenuation1; }
float C_Light::GetAttenuation2(){ return light.Attenuation2; }
float C_Light::GetTheta() { return light.Theta; }
float C_Light::GetPhi() { return light.Phi; }

// 下面是对灯光的停用,恢复操作

void C_Light::Invalidate(){}

void C_Light::Restore() 
{
    Update();
    if (enabled) {
        Enable();
    }
}

void C_Light::InvalidateAll() {
    vector<C_Light*>::iterator iter = allLights.begin();
    while(iter != allLights.end()) {
       (*iter)->Invalidate();
       iter++;
    }
}

void C_Light::RestoreAll() {
     vector<C_Light*>::iterator iter = allLights.begin();
     while(iter != allLights.end()) {
       (*iter)->Restore();
       iter++;
    }
}