【Visual C++】游戏开发笔记四十 浅墨DirectX教程之八 绘制真实质感的三维世界:光照与材质专场
本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
文章链接: http://blog.csdn.net/zhmxy555/article/details/8499438
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
本篇文章里,我们对Direct3D之中固定功能流水线中的3D光照编程相关的知识进行了详尽的剖析,文章末尾依旧是提供文章配套的详细注释的demo源代码的欣赏,并在文章末尾提供了源代码下载。
一、引言
光,乃万物之源。我们根本无法想象,这个美丽怡人的世界,如果没有光的陪伴,会是怎样的一副满目疮痍。计算机3D世界作为现实世界的高度逼真的模仿,必然也少不了光的陪伴。回到我们的Direct3D应用程序中来,在Direct3D中运用光照,能有效地增强3D场景的真实感。在3D场景中使用光照其实非常地简单,我们不需要为物体的每个顶点都去指定颜色值,只要我们告诉Direct3D我们使用的是什么类型的光照,我们的物体的材质的具体参数以及物体表面相对于光源的朝向,Direct3D就会根据其内置的算法计算出每个顶点的颜色值,产生出逼真的光照效果。
当然随着我们学习的深入,功力的加深,就可以不单单依赖于Direct3D中内建的光照算法,可以根据各种功能的着色器的编写,自己写出更加优化更加逼真的光照效果来。
作为目前刚刚接触到Direct3D中的光照和材质这一块内容,我们还是老老实实地先把固定功能流水线中的这一套非常好学好掌握的光照与材质的体系系统地进行讲解,先把基础打牢,先把走学会,这样才能为后面我们的腾飞做铺垫。
说到一套完整的光照体系,有两对组成方面,第一,光照,第二,材质。这两者天生就是一对好搭档,我们可以把它们看做光照计算的两要素,想要绘制出具有光照的真实三维世界,两者缺一不可。
下面就开始正式讲解,首先我们来看看四大光照类型:
二、四大光照类型
1.环境光(Ambient Light)
一个物体即使没有直接被光源照射,但是只要有光线通过其他物体的折射、反射到达物体,它也可能被看见。这种基于整个自然界环境的整体亮度,称为环境光(Ambient Light)或者背景光。环境光没有位置或者方向上的特征,只有一个颜色亮度值,而且不会衰减,所以在所有方向和所有物体表面上投射的环境光的数量是恒定不变的。想要以较低的代价和开销来近似模拟光照的话,直接开启环境光是一个不错的选择。
在Direct3D中环境光的设置非常简单,也就是用一下SetRenderState,代码如下:
漫反射光在我们的生活中最为普遍,太阳的直射,日光灯的照射都可以看成漫反射的近似。这种类型的光沿着特定的方向传播。当它到达某一表面时,将沿着各个方向均匀反射,所以我们无论从哪个方向观察,物体表面的亮度都是相同的,所以采用漫反射这种光照模型时,无需考虑观察者的位置,但是需要考虑漫反射光的空间位置和方向。从一个光源发出的光一般都是这种类型的。漫反射光并没有简洁的设置方法,具体下文会讲到的,请大家继续往下看。
3.镜面反射光(SpecularLight)
镜面反射光,顾名思义,沿着特定的方向传播,当此类光到达一个表面时,将严格地沿着另一个方向反射,从而形成只能在一个角度范围内才能观察到的高亮度照射。这种光照模型模拟了从光滑发光面如镜子、一块金属或者一块发光塑料等材料来进行光线反射的情形。如果我们移动一下光源的话,就会发现镜面亮光区所发生的变化,这意味着镜面反射取决于观察者的角度。我们可以这样来归纳,漫反射与视觉无关,而镜面反射与视觉相关。
需要注意的是,镜面光与其他类型的光相比,计算量要大得多,Direct3D默认情况是把镜面反射关起来的。如果我们想启用镜面反射的话,用下面的代码,即把渲染状态D3DRS_SPECULARENABLE设为true:
pd3dDevice->SetRenderState(D3DRS_SPECULARENABLE,true);
4.自发光
自发光就是对象自己发出的光,其实他是根据通过对象的自发光材质实现的,下面我们在讲解材质时讲到的D3DMATERIAL9结构体,这个结构体的成员Emissive描述自发光的颜色和透明度。自发光影响着一个对象的颜色,比如我们可以通过设置自发光的颜色属性,把一些灰暗的材质变得明亮一些。需要提出来的是,我们可以使用材质的自发光属性来“照亮”这个对象,而不用在场景中内部添加灯光,从而缩小了计算量。自发光属性创建的材质并不发射出能被场景内其他对象反射的光,也就是说,它发出的光并不参与光运算,而为了实现反射光,需要在场景中添加额外的灯光。
讲解完四大光照类型,接下来当然少不了三大光源类型。
三、三大光源类型
在Direct3D中的光源类型和光照类型是两个完全不同的概念,光照模型描述的是光线的反射特征,而光源类型主要强调的是能够产生这些光照模型的方式以及光线的位置、方向、强度等特征。
Direct3D中主要有3种类型的的光源,我们把他们合称为三大光源:点光源(Point Light)、方向光—irectional Light)和聚光灯(Spot Light)。
而在Direct3D 9.0c中,讲到光源,必须讲到一个结构体,那就是D3DLIGHT9结构体,在展开讲解各中光源类型之前,先让我们看看这个结构体的具体内容,我们可以在MSDN中查到这个结构体有如下原型:
其中,D为光源到顶点的距离,A0~A2分别对应于Attenuation0~ Attenuation2。
讲解完成,下面我们看看具体怎么使用。在Direct3D中使用光照的话,也就是用我们这个D3DLIGHT9结构体实例化一个具体的光源类型,然后无脑地进行喜闻乐见的填空题操作,对这个结构体的参数进行赋值,赋值完成后调用IDirect3DDevice9接口的SetLight方法设置光源,然后调用IDirect3DDevice9接口的LightEnable方法启用光照就可以了。下面我们来分别看看这两个方法。首先是SetLight,SetLight方法用于设置光源:
点光源(Point Light)具有颜色和位置,但没有方向,它向所有方向发射的光都一样。
它是一个从中心向空间中各个方向发射相等强度光线的光源,且光的亮度会不随着距离而衰减。要定义一个点光源的话,实例化一个D3DLIGHT9结构体,将第一个参数设为D3DLIGHT_POINT然后进行其余参数的设置即可,这样实例化出的这个结构体就是一个点光源了。下面我们看一个点光源设置的实例:
上面我们讲到,光线强度从内锥体到外锥体逐渐衰减,是通过聚光灯的Falloff、Theta、和Phi这三个属性共同来控制其衰减规律。下图显示了这Phi和Theta参数之间的关系和他们如何影响着一个聚光灯的内外锥体的:
而Falloff用于控制光强如何从内锥体的外侧向外锥体的内侧减弱的,通常我们将其设为1.0f,来让光线在两个圆锥间平滑地减弱。下图清晰地显示了Falloff参数是如何来取决内锥体和外锥体之间的光强变化的。
因为聚光灯受到衰减规律和光照范围的影响,场景中的每个顶点在计算光照时,都要考虑这些因素,这使得聚光灯成为在Direct3D中首屈一指的高开销光源,因此我们要谨慎使用聚光灯。
同样地,要定义一个聚光灯光源的话,实例化一个D3DLIGHT9结构体,将第一个参数设为 D3DLIGHT_SPOT然后进行其余参数的设置即可,这样实例化出的这个结构体就是一个聚光灯光源了。
讲完聚光灯的概念,依然是一个设置聚光灯光源的实例:
四、材质
对于光照计算,光照和材质两者缺一不可。物体表面的材质属性决定了它能反射什么颜色的光线以及能反射多少,而在Direct3D中,关于物体表面的材质属性,由一个结构体D3DMATERIAL9来负责管理。
我们可以在MSDN中查到这个结构体有如下原型:
其中等式左边的I total表示物体最终的颜色值,通过这个式子我们可以知道,物体的颜色总和=物体反射环境光+物体反射漫反射光+物体反射镜面反射光+自发光。也就是说,物体的最终颜色值由D3DMATERIAL9结构体中设置的四种颜色值共同决定。
在做完填空题,设置好我们的材质属性后,就需要调用一个SetMaterial方法来设置我们当前使用的材质属性,我们可以在MSDN中查到这个参数有如下原型:
顶点法线:
在一般情况下,顶点法线与面法线的方向是相同的。但是在某些特殊的情况下,顶点法线并不与面法线相同。比如一个近似的球体或圆的顶点法线和面法线就不一致:
顶点法线可以在定义的顶点结构中进行描述。我们需要在前面已经拥有的顶点结构体中添加一组用于描述顶点法向量的数据成员。当然修改了顶点结构体,对应的FVF灵活顶点格式的宏需要和结构体对应,也就是添加一句D3DFVF_NORMAL。
下面我们拿之前笔记三十八里关于顶点格式设计的代码来做演示,首先上笔记三十八里面的原版代码:
也就是首先计算位于三角形平面内代表两条边的向量,然后对这两条向量做叉乘运算就可以了。
当然,当我们使用一组三角形渐进来表示曲面时,使用上述方法计算出的顶点法向量将会产生不光滑的效果。因此,另一种计算顶点法向量的方式应运而生——计算法向量的均值(normal averaging):首先我们求出共享该顶点的3个三角形的面法向量,然后取他们的平均值作为该顶点的顶点法向量,如图:
也就是说np=(n1+n2+n3)/3
另外,在变换过程中,我们的顶点法线有可能不再是规范化的了。所以,最好的方法是,在变换完成之后,通过在SetRenderState方法中将D3DRS_NORMALIZENORMALS这个参数设为true来把所有的法向量规范化,也就是这样写:
第二张,线框填充模式+点光源:
第三张,实体填充模式+方向光源:
第四张,线框填充模式+方向光源:
第五张,实体填充模式+聚光灯光源:
第六张,线框填充模式+聚光灯光源:
文章最后,依旧是放出本篇文章配套源代码的下载:
本节笔记配套源代码请点击这里下载:
【浅墨DirectX提高班】配套源代码之八下载
其中图标素材取自的游戏大作 质量效应3
以上就是本节笔记的全部内容,更多精彩内容,且听下回分解。
浅墨在这里,希望喜欢游戏开发系列文章的朋友们能留下你们的评论,每次浅墨登陆博客看到大家的留言的时候都会非常开心,感觉自己正在传递一种信仰,一种精神。
文章最后,依然是【每文一语】栏目,今天的句子是:
执著和放弃,选择哪个并不重要;重要的是,一旦作出决定,给自己一个把这个决定坚持下去的理由。否则,当你仰望苍穹的时候,你的眼神也许和那些茫然战死的沙漠佣兵,没什么区别。我们都是怀揣梦想、穿越沙漠的流浪者。电脑屏幕前的你,早安~
下周一,让我们离游戏开发的梦想更近一步。
下周一,游戏开发笔记,我们,不见不散。
- 71楼XingFuampLife16分钟前
- 不错
- 70楼huanyue1st17分钟前
- 哇哦
- 69楼xieau199226分钟前
- 好东西 谢谢谢
- 68楼oBingZuoLin123450分钟前
- kankan .怎么的
- 67楼qwuyw51分钟前
- 体五明
- 66楼ghghad1小时前
- 厉害
- 65楼zs7021045651小时前
- ;;;;;
- 64楼Z376122009昨天 23:38
- 额
- 63楼ming_311昨天 23:22
- 好
- 62楼jx_dishi昨天 21:53
- asdfasdfadsfasdfadf
- 61楼wuwu1122昨天 21:15
- hao ya
- 60楼a369258147b昨天 21:11
- 很强大、我喜欢
- 59楼yulanfeiyang___昨天 20:18
- 好
- 58楼peiseng521昨天 19:53
- 呵呵不 多哦 顶起来
- 57楼Bhanghh昨天 19:03
- 很好的,太强了
- 56楼zhuyao1234昨天 17:42
- 喔喔喔喔喔喔喔喔喔喔喔喔喔噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢噢哦哇呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜呜
- 55楼tx2277323昨天 17:23
- 不错
- 54楼Razor87昨天 17:21
- 辛苦了!!
- 53楼xiaoxiao54321昨天 17:09
- 我要学太好了
- 52楼zxc122333昨天 15:51
- 不明觉厉
- 51楼BuKeShi昨天 15:27
- 牛逼啊
- 50楼chenyong2254昨天 15:25
- 顶起
- 49楼skayxuxinlun昨天 13:54
- 厉害啊。。。
- 48楼xb657735921昨天 13:51
- 不错
- 47楼jsiang昨天 13:16
- 非常好
- 46楼a745489231昨天 13:06
- aaaaaaaaaaaaa
- 45楼vdvadfvadv昨天 12:51
- 好好
- 44楼woshilirongxin昨天 12:33
- 。。。。。
- 43楼zhonglichao00昨天 11:19
- 浅墨,我是做java的web开发的,平时没什么时间研究游戏开发,但对游戏开发很感兴趣,上周日认真看了你的关于游戏开发的前几篇,就像你之前提到的,DirectX可以与底层硬件直接对话,我可不可以理解为DirectX主要用来直接操作底层硬件(除了CPU)才可以使游戏性能提升,而GDI则是用软件来模拟硬件来达到效果,所以GDI速度慢;如果硬件不支持,或是没有硬件,则DirectX是不是也要像GDI一样用软件来模拟呢,这样的话会不会是GDI更快一些呢
- Re: BonChoix昨天 11:57
- 回复zhonglichao00n不能说GDI是用软件来模拟硬件,GDI最底层其实也是用到硬件的功能,只是在GDI的API和硬件之间加了中间层,对抽象的硬件进行了大量的封装,呈现给我们的是非常直观、易用的API,比如画线,诸如LineTo等这种调用一句话就搞定了,从来不需要考虑什么清屏啊、顶点|索引缓存啊这类复杂的底层东西,说它慢主要就是因为这个中间层的存在而导致的。DirectX就没有这个中间层了,它仅仅是对硬件进行了抽象,以API的形式呈现给我们,我们通过这些API直接操纵硬件,因此很多底层硬件级别上的操作都要靠我们自己来,这样速度当然就很快了。n当然,就是你说的,依赖于硬件,如果硬件不支持某功能,DirectX就不能正常工作了,必须使用DX的软件实现(REF设备)。所以DX9里面初始化阶段的硬件能力检测相当重要。不过这种软件实现,说白了,没什么用,除了用来Debug、试验下某些功能,其他地方完全不用它。没有哪个游戏敢寄希望于REF软件来运行它的某些特性,奇慢无比,可以打开DX SDK里面的Sample切换成REF设备来感受一下。
- 42楼oXiangCaoTianKong昨天 11:01
- 挺认真的
- 41楼a1521200751昨天 10:40
- 看不懂啊
- 40楼caonimeixiaoke520昨天 10:23
- 好
- 39楼ll89122昨天 08:41
- 不错好
- 38楼guquan0815昨天 07:43
- good
- 37楼huaweiruanjian昨天 07:21
- 站个沙发。。
- 36楼hellolonewolf昨天 02:42
- 呵呵
- 35楼liyitroy昨天 23:36
- 好厉害啊,顶一个
- 34楼qq574174558昨天 23:24
- 好东西
- 33楼ljcolm昨天 23:16
- 挺好
- 32楼xingzd昨天 23:03
- 好!!!!!
- 31楼machaothink昨天 22:21
- 厉害,受教
- 30楼a052800昨天 22:00
- 学习
- 29楼GRAY_XIAOXIAO昨天 21:20
- 哈哈,终于又向3D迈进了一步!
- 28楼jc_tzc昨天 20:51
- 写的很好
- 27楼dk520123456昨天 20:36
- 号,顶
- 26楼miss_clear昨天 20:33
- good
- 25楼sbluntan321昨天 20:32
- 支持 支持
- 24楼blisson昨天 20:02
- 厉害哦
- 23楼lf19702000昨天 19:24
- 不懂好好学习 天天向上
- 22楼dingyansong168昨天 18:48
- 不错
- 21楼w420524608昨天 18:15
- 不错不错...
- 20楼xzshen2011昨天 18:04
- 如果硬件不支持,或是没有硬件.henhao
- 19楼y074085024昨天 17:44
- 浅墨,我是做Jsp的web开发的,平时没什么时间研究游戏开发,但对游戏开发很感兴趣,上周日认真看了你的关于游戏开发的前几篇,就像你之前提到的,DirectX可以与底层硬件直接对话,我可不可以理解为DirectX主要用来直接操作底层硬件(除了CPU)才可以使游戏性能提升,而GDI则是用软件来模拟硬件来达到效果,所以GDI速度慢;如果硬件不支持,或是没有硬件,则DirectX是不是也要像GDI一样用软件来模拟呢,这样的话会不会是GDI更快一些呢
- 18楼pipasan昨天 17:01
- 非常非常好啊
- 17楼gingerguo2005昨天 16:30
- 写的真是太好了,受益匪浅。
- 16楼blackdukezwp昨天 16:05
- 学习
- 15楼caonimeixiaoke520昨天 15:56
- 不错
- 14楼BonChoix昨天 15:55
- 写得十常好,占个沙发~
- 13楼A8313847昨天 15:46
- HAO
- 12楼angelosky昨天 14:48
- 不错饿!
- 11楼wah2099昨天 14:45
- good xinkule
- 10楼likui0514昨天 14:24
- 好
- 9楼viviou_zou昨天 14:24
- 支持支持。很好
- 8楼xunyicaoyangmei昨天 13:55
- 非常好
- 7楼jx_dishi昨天 13:18
- asdfasdf
- 6楼y531016950昨天 12:06
- 很不错的
- 5楼HeNiuEr昨天 11:38
- 好
- 4楼sbluntan321昨天 11:26
- 支持
- 3楼he812昨天 11:22
- 我的积分怎么小于零,你这个写得不错哟!评价一下
- 2楼wlmhappy前天 09:46
- haihao
- 1楼q778941341前天 04:22
- 好,不错