您好,欢迎来到爱go旅游网。
搜索
您的当前位置:首页7第七章:灯光与材质

7第七章:灯光与材质

来源:爱go旅游网
DirectX Tutorials www.andypike.com By Andy Pike 第七章:灯光与材质

Introduction (序)

新的一章又开始了,这一章,我们将学习DirectX中的灯光和材质。在例程中,我们将在原点附近创建四个旋转的立方体,在它们的中间(原点附近),我们会放置一个光源,你将会看到光照的效果是怎样形成的。

DirectX Lighting vs. Real World Lighting (DX光照 对 真实世界光照)

在DX中,我们能创建不同类型的光,使场景看起来更真实。但要记住,DX中的光只是在近似地模拟自然界的光。在自然界中,光由光源(如灯泡)发出,然后延直线传播,直到消耗完毕或传入眼睛;光在遇到物体时会发生反射,每次反射都会有能量的消耗;实际上,光可以在物体间反射千百万次,而且光滑表面会比不光滑的表面反射更多的光。这一切如果全算进来的话,计算量是巨大的,所以,DX只是在近似地模仿。

Attributes of a light (光的属性):

想要不同的光可以指定不同的属性,不是所有类型的光都拥有下面的这些属性:

Position (位置)

这是在3D空间中光源的位置,例如(0, 10, 0)。

Direction (方向)

这是一个向量(例如(0, 1, 0)),表示从光源发射出来的光的方向。

Range (范围)

这是光从光源发出后所能到达的最大距离,范围以外的物体将不会接受到此光源的光。

Attenuation (衰减)

在光源到它的范围之间,光会逐渐衰减,这是很正常的。如果想要,你也可以指定让光不衰减,或反而越来越亮。

Diffuse Light (漫反射)

光的漫反射颜色。漫射光是分散的,但仍然具有方向,不像环境光没有方向。

Ambient Light (环境光)

这是环境光的颜色,它是通用的背景光,它是分散的且没有方向和光源,它充满整个场景。

Specular Light (镜面反射)

镜面反射的的颜色。它是相对于漫射光来说的,它根本不会分散,你可以用它在对象上创建高亮。

Types of lighting (光的类型):

在场景有四中类型的光可以被你创建,每种都有不同的行为与属性。

Ambient Light (环境光)

可以为场景指定通用的环境光,与其它灯光互不影响,用红、绿、蓝三个分量来设置颜色。

Point Light (点光源)

电光源的一个很好的例子就是灯泡,它没有方向(因为向全部方向发出),但是有颜色、范围和衰减。下面是图示(7.1)。

- 1 -

DirectX Tutorials www.andypike.com By Andy Pike Fig 7.1

Directional Light (直射光)

直射光具有颜色,没有位置。举个例子应该是太阳,场景里的所有对象都会从同样的方向接收到同样的光。直射光也没有范围和衰减。下面是图示(7.2)。

Fig 7.2

Spotlight (聚光灯)

手电筒是聚光灯的好例子,它具有位置、方向、范围、发光内径和发光外径属性,光照强度还会随距离而衰减。下图(7.3)中,Theta是内径,Phi是外径。

- 2 -

DirectX Tutorials www.andypike.com By Andy Pike Fig 7.3

所有的类型的光都会增加程序的负担,从小到大作负担排序:环境光、直射光、点光源、聚光灯。要记住在做程序时需要考虑这些。

Materials (材质)

材质用于描述对象(三角形)的反光性能,使它看起来有没有光泽和带有什么颜色。下面是描述材质的一些设置:

Diffuse Reflection (漫反射)

物体的漫反射颜色,能使物体看起来是什么颜色。

Ambient Reflection (环境反射)

这也是一个颜色值,用于描述对象反射环境光的数量。你甚至可以指定它根本不反射环境光,这意味着它将不可见,除非接收到其它类型的光。

Specular Reflection and Power (镜面反射与高光强度)

镜面反射的值,可以用镜面反射和高光强度设置对象,使它看起来发亮。

Emission (放射)

使物体自发光,但不会影响到场景内的其它物体。

Normals (法线)

法线是垂直于三角形面的一个向量,它的方向是由三角形的顶点定义顺序所决定的。下图(7.4)演示了一个顺时针定义的三角形的及它的法线。顶点的法线一般均分它周围的三角形法线。下图(7.5)演示了4个三角形的横截面和它们的法线(亮红),同样也演示了3个顶点的法线(红)。本教程只把法线用于描述阴影(译者:还有其他用途)。

Fig 7.4 - 3 -

Fig 7.5 DirectX Tutorials www.andypike.com By Andy Pike

Implementing lights in DirectX (在DX中实现灯光)

在此章的代码中,我们设置了一个点光源,而且我们把环境光设置的很暗以突出它。 我们还设置了能使立方体棱角分明的法线,而且使材质没有高光。

Step 1: Modify FVF and Custom Vertex (修改FVF和自定义定点)

第一件有做的就是修改FVF和自定义定点结构。在FVF中我们移除了D3DFVF_DIFFUSE而替换为

D3DFVF_NORMAL,然后在自定义定点结构中去掉了颜色属性而添加了法线属性。记住,最后还有纹理坐标属性。 //Define a FVF for our cuboids #define CUBOID_D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1) //Define a custom vertex for our cuboids struct CUBOID_CUSTOMVERTEX { FLOAT x, y, z; //Position of vertex in 3D space FLOAT nx, ny, nz; //Lighting Normal FLOAT tu, tv; //Texture coordinates }; Step 2: Creating the lights (创建灯光)

我们给CGame增加了一个新的模块InitialiseLights,我们把所有的灯光设置代码都放在这了。首先,我们为场景设置了一个点光源。我们需要一个D3DLIGHT8类型的结构变量,然后为它设置正确的值。要指定它是一个点光源(而不是别的),我们用D3DLIGHT_POINT常量。如果你想要直射光那就是D3DLIGHT_DIRECTIONAL,想要聚光灯就是D3DLIGHT_SPOT。然后,我们要决定光源发出什么样的光,所以我们要指定Diffuse(漫射)、Ambient(环境)和Specular(镜面)的RGB值,记住它们的值应该是在0和1之间的,0就是没有,1就是满。然后还有它的位置值x,y和z,最后是范围和衰减。把衰减设置为值1意味着它不会衰减。

嗯,我们已经设置好灯光了,接下来要把它应用到场景中然后激活它。首先我们得给它分配一个灯光索引号,我们用SetLight,它的第一个参数就是了,这是一个从0开始的索引列表,所以这第一个灯光我们设置为0。然后,激活灯光(turn it on),用LightEnable。第一个参数是灯光索引号,第二个参数是开关(TRUE 开, FALSE 关)。

然后我们有调用了SetRenderState以允许灯光,最后再次调用了SetRenderState为场景设置了环境光。 D3DLIGHT8 d3dLight; //Initialize the light structure. ZeroMemory(&d3dLight, sizeof(D3DLIGHT8)); //Set up a white point light at (0, 0, -10). d3dLight.Type = D3DLIGHT_POINT; d3dLight.Diffuse.r = 1.0f; d3dLight.Diffuse.g = 1.0f; d3dLight.Diffuse.b = 1.0f; d3dLight.Ambient.r = 0.0f; d3dLight.Ambient.g = 0.0f; d3dLight.Ambient.b = 0.0f; d3dLight.Specular.r = 0.0f; d3dLight.Specular.g = 0.0f; d3dLight.Specular.b = 0.0f; d3dLight.Position.x = 0.0f; d3dLight.Position.y = 0.0f; d3dLight.Position.z = -10.0f; d3dLight.Attenuation0 = 1.0f; d3dLight.Attenuation1 = 0.0f; - 4 -

DirectX Tutorials www.andypike.com By Andy Pike d3dLight.Attenuation2 = 0.0f; d3dLight.Range = 100.0f; //Assign the point light to our device in poisition (index) 0 m_pD3DDevice->SetLight(0, &d3dLight); //Enable our point light in position (index) 0 m_pD3DDevice->LightEnable(0, TRUE); //Turn on lighting m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE); //Set ambient light level m_pD3DDevice->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(32, 32, 32)); Step 3: Setup Material (设置材质)

我们又给CCuboid增加了一个新的模块SetMaterial,用于给立方体设置材质。我们还给CCuboid增添了一个新的D3DMATERIAL8型变量m_matMaterial,然后为它设置属性值。我们要设置漫反射(Diffuse)、环境反射(Ambient)和镜面反射(Specular)的RGBA值,然后是放射(Emissive)的RGBA 值。记住要初始化这些值(尤其是Emissive),否则将不能正确工作。 bool CCuboid::SetMaterial(D3DCOLORVALUE rgbaDiffuse, D3DCOLORVALUE rgbaAmbient, D3DCOLORVALUE rgbaSpecular, D3DCOLORVALUE rgbaEmissive, float rPower) { //Set the RGBA for diffuse light reflected from this material. m_matMaterial.Diffuse = rgbaDiffuse; //Set the RGBA for ambient light reflected from this material. m_matMaterial.Ambient = rgbaAmbient; //Set the color and sharpness of specular highlights for the material. m_matMaterial.Specular = rgbaSpecular; m_matMaterial.Power = rPower; //Set the RGBA for light emitted from this material. m_matMaterial.Emissive = rgbaEmissive; return true; } 在CCuboid中我们调用了SetMaterial,来给我们的立方体设置默认的材质。 //Set material default values (R, G, B, A) D3DCOLORVALUE rgbaDiffuse = {1.0, 1.0, 1.0, 0.0,}; D3DCOLORVALUE rgbaAmbient = {1.0, 1.0, 1.0, 0.0,}; D3DCOLORVALUE rgbaSpecular = {0.0, 0.0, 0.0, 0.0,}; D3DCOLORVALUE rgbaEmissive = {0.0, 0.0, 0.0, 0.0,}; SetMaterial(rgbaDiffuse, rgbaAmbient, rgbaSpecular, rgbaEmissive, 0); 最后,在CCuboid的Render模块中,我们调用SetMaterial来告知DX我们想要为后续的顶点应用我们的材质。 m_pD3DDevice->SetMaterial(&m_matMaterial);

Step 4: Generate Normals (生成法线)

我们改变了原来的3个三角带的立方体构造方式,现在我们用有12个三角形、36个顶点的一个三角带来构造立方体。我们还像以往那样定义顶点,只不过这回去掉了颜色值增添了法线向量(初始化为0)。然后我们有一个循环,它会调用GetTriangeNormal来计算每个三角形的法线向量,然后这个三角形的三个顶点的法线向量将会被设置为与三角形相同。下面的代码片断是GetTriangeNormal模块和那个循环。

- 5 -

DirectX Tutorials www.andypike.com By Andy Pike 我们之所以把三角形的顶点的法线设置的与三角形相同,是因为我们想要立方体更棱角分明,而不是看起来有平滑的圆角。如果我们需要某些对象有平滑的外形(例如球体),那么我们需要把顶点的法线设置为平分周围的三角形法线。 D3DVECTOR CCuboid::GetTriangeNormal(D3DXVECTOR3* vVertex1, D3DXVECTOR3* vVertex2, D3DXVECTOR3* vVertex3) { D3DXVECTOR3 vNormal; D3DXVECTOR3 v1; D3DXVECTOR3 v2; D3DXVec3Subtract(&v1, vVertex2, vVertex1); D3DXVec3Subtract(&v2, vVertex3, vVertex1); D3DXVec3Cross(&vNormal, &v1, &v2); D3DXVec3Normalize(&vNormal, &vNormal); return vNormal; } //Set all vertex normals int i; for(i = 0; i < 36; i += 3) { vNormal = GetTriangeNormal(&D3DXVECTOR3(cvVertices[i].x, cvVertices[i].y, cvVertices[i].z), &D3DXVECTOR3(cvVertices[i + 1].x, cvVertices[i + 1].y, cvVertices[i + 1].z), &D3DXVECTOR3(cvVertices[i + 2].x, cvVertices[i + 2].y, cvVertices[i + 2].z)); cvVertices[i].nx = vNormal.x; cvVertices[i].ny = vNormal.y; cvVertices[i].nz = vNormal.z; cvVertices[i + 1].nx = vNormal.x; cvVertices[i + 1].ny = vNormal.y; cvVertices[i + 1].nz = vNormal.z; cvVertices[i + 2].nx = vNormal.x; cvVertices[i + 2].ny = vNormal.y; cvVertices[i + 2].nz = vNormal.z; } 下图(7.6)显示了立方体的三个面上的(顶点的)法线,其它未显示的法线也是雷同的。如果我们平分顶点法线,那末立方体看起来将是边缘平滑的,那不是我们想要的。当我们真的想要平滑的边缘时,我们才需要平分顶点法线,以后的章节中我们会学习到这个。

- 6 -

DirectX Tutorials www.andypike.com By Andy Pike Fig 7.6

最后,在CCuboid的Render模块中,我们要用D3DPT_TRIANGLELIST替代原来的D3DPT_TRIANGLESTRIP: m_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 12); 好了,这样我们就会得到像下图这样的4旋转的个立方体。它们分别都有不同的纹理,在它们的中间有一个点光源,这样你会看到光效的变幻。

Summary (摘要)

这一章就到此结束了。在此章中,我们学习了灯光的各个类型和纹理对物体外观的影响。呵呵,我们的场景会更漂亮了。下一章我们要学的是——索引缓冲。

In this tutorial we've covered a lot of stuff. We've learnt all about the different types of lights and their properties. We've seen how materials can affect how an objects looks once rendered. In the next tutorial we'll take a look at Index Buffers.

- 7 -

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- igat.cn 版权所有 赣ICP备2024042791号-1

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务