4. 明暗处理Shading

  这一部分我们将讨论Direct3D明暗处理的有关内容。

4.1 明暗处理模式

  渲染多边形时所采用的明暗处理模式对于多边形的外观有很大的影响。明暗处理模式决定了一个多边形的表面上任意一点的颜色和光线的强度。Direct3D目前支持两种明暗处理模式:

4.1.1 平面明暗处理模式

  使用平面明暗处理模式来渲染一个多边形时,Direct3D的渲染管道(rendering pipeline)使用多边形上第一个顶点的材质的颜色来作为整个多边形的颜色。使用平面明暗处理pic8.gif (1302 bytes)模式进行渲染的三维对象,在相邻的不共面的两个平面之间,会出现较明显的边缘。

  左图显示了一个采用平面明暗处理模式进行渲染的茶壶的图片。图片中,每一个多边形的轮廓都非常清晰。平面明暗处理模式所要使用的计算量是两种明暗处理模式中最小的。

4.1.2 Gouraud明暗处理模式

  使用Gouraud明暗处理模式渲染一个多边形时,Direct3D利用顶点法线(vertex normal)和灯光参数(lighting parameters)来计算每个顶点的颜色。然后,在多边形的表面上进行线性内插运算(见“边面和顶点法向量”部分)。举例来说,如果顶点1的红色值为0.9,顶点2的红色值为0.4,使用Gouraud明暗处理模式和RGB色彩模式,那么这两个顶点间连线的中pic9.gif (1252 bytes)点的的红颜色的值就是0.6

  左图中显示了使用Gouraud明暗处理模式的效果。图中的茶壶由许多小的三角形平面所组成。然而Gouraud明暗处理模式使得它的表面看起来非常的平滑和完整。

  Gouraud明暗处理模式也可以用来显示具有明显边缘的对象。具体细节见“表面和顶点法向量”部分。

4.2 明暗处理模式的比较

  在平面明暗处理模式中,下图中的金字塔的相邻两个面之间会有明显的边缘。而采用Gouraud明暗处理模式时,边缘处的明暗值会由内插运算产生,因而最后会得到一个弯曲的表面。

pic10.gif (3056 bytes)

  使用Gouraud明暗处理来照亮平坦的表面要比使用平面明暗处理模式更加真实。平面明暗处理模式中的同一个面的颜色是相同的,而Gouraud处理模式允许光线在表面上有更逼真的效果。当离一个表面很近的地方有一个点光源时,它们的区别将会更明显的表现出来。

  Gouraud模式会将在平面处理模式中明显的边缘平滑掉,然而这样可能会导致马赫带效应(Mach bands)的产生,也就是相邻的颜色或光线带之间不能很平滑的相互融合。对于程序开发人员来说,可以通过增加构成对象的多边形的数目来降低马赫带效应,当然也可以通过提高屏幕分辨率,或者增加程序的颜色深度来达到目的。

  使用Gouraud模式可能会丢失一些细节。下图的例子显示了这一情况,图中的聚光灯完全位于一个多边形表面上。

pic11.gif (3207 bytes)

  这样,当Gouraud模式在两个顶点间进行内插的同时,也就将聚光灯一同丢失了;渲染出来的表面将不再有聚光灯。

4.3 设置明暗处理模式

  Direct3D允许每次选中一种明暗处理模式。缺省情况下为Gouraud模式。可以通过调用IDirect3DDevice3::SetRenderState方法来改变模式。dwRenderStateType参数应该被设定为D3DRENDERSTATE_SHADEMODEdwRenderState参数应该被设定为D3DSHADEMODE枚举类型的一个成员。下面的代码显示了如何来设定当前的明暗处理模式。

// Set to flat shading.
// This code fragment assumes that lpDev3 is a valid pointer to
// an IDirect3DDevice3 interface.

hr = lpDev3->SetRenderState(D3DRENDERSTATE_SHADEMODE, D3DSHADE_FLAT);
if(FAILED(hr))
{
// Code to handle the error goes here.
}

// Set to Gouraud shading (this is the default for Direct3D).

hr = lpDev3->SetRenderState(D3DRENDERSTATE_SHADEMODE,
D3DSHADE_GOURAUD);
if(FAILED(hr))
{
// Code to handle the error goes here.
}

4.4 面和顶点法向量

  一个多面体的每一个面都有一个与这个面正交的法向量。这个向量的方向由构成面的顶点的组成顺序以及所使用的坐标系统来决定。面的法线总是远离这个面的正面一侧,即由这个面的正面一侧开始指向向外的方向。在Direct3D中,只有面的正面一侧是可见的。面的正面也就是指,从这一侧来看面上的顶点按照顺时针方向来构成这个面。

pic12.gif (5556 bytes)

  任何一个面只有正面和反面两个面。Direct3D是不对反面进行渲染的,因此反面可以说被Culling掉了(be culled)。(如果需要的话,可以改变Culling模式(culling mode)来对反面进行渲染。纤细内容见“Culling状态”部分。)

  Direct3D程序不需要对面法向量进行声明;当需要用到它们时,系统会自动进行计算。在平面明暗处理模式中,我们会用到面法向量。而在Gouraud模式中,Direct3D使用顶点法向量(vertex normal)。它还使用顶点法向量来控制灯光和纹理效果。

pic13.gif (4490 bytes)

  Direct3D程序通常使用顶点的D3DVERTEX类型。D3DVERTEX结构的成员用来描述顶点的位置和方向。这里的方向就是指顶点的法向量。下面的代码显示了如何来设置顶点的各种值,包括顶点法向量。其中法向量指向位于世界坐标系的原点的视口(viewport)。这个例子中顶点的位置就位于世界坐标系中。


D3DVERTEX lpVertices[3];
// A vertex can be specified one structure member at a time.
lpVertices[0].x = 0;
lpVertices[0].y = 5;
lpVertices[0].z = 5;
lpVertices[0].nx = 0; // X component of the normal vector.
lpVertices[0].ny = 0; // Y component of the normal vector.
lpVertices[0].nz = -1; // Points the normal back at the origin.
lpVertices[0].tu = 0; // Only used if a texture is being used.
lpVertices[0].tv = 0; // Only used if a texture is being used.

// Vertices can also by specified on one line of code for each vertex
// by using some of the D3DOVERLOADS macros.
lpVertices[1] = D3DVERTEX(D3DVECTOR(-5,-5,5),D3DVECTOR(0,0,-1),0,0);
lpVertices[2] = D3DVERTEX(D3DVECTOR(5,-5,5),D3DVECTOR(0,0,-1),0,0);

  使用Gouraud模式时,Direct3D用顶点法向量来计算光源和表面之间的夹角。它还计算顶点的颜色和亮度,并且在原先的表面上通过内差运算来得到其它点上的值。Direct3D是通过夹角来计算亮度值。夹角越大,表面上光线的亮度就越小。.

  如果你所创建的对象的表面是平坦的,那么顶点的法向量就应该与对象的表面正交。下图向我们显示了这一情况。图中平坦的表面由两个三角形构成。所有顶点的法向量都与表面正交。

pic14.gif (2065 bytes)

  一般情况下,我们所创建的对象都是由许多的小的三角带(triangle strip)组成的,并且这些三角形往往是不共面的。这样,在我们在对对象的表面进行平滑的明暗处理时,就有一种简单的方法来得到各个顶点的法向量。我们可以先来计算每一个小多边形平面的法向量,而顶点的法向量就是与这些小平面的法向量的夹角相等的那一个向量。要注意的是,对于复杂的图元,这种方法可能不够有效。

  下面左图中向我们演示了这种方法,图中由两个面,S1S2,我们看到的是它们的侧面。S1S2的法向量用蓝色表示。顶点的法向量用红色表示。我们可以看到,顶点法向量与S1S2的面法向量的夹角是相等的。使用Gouraud明暗处理模式时,这两个面之间将是平滑的,不再有明显的边缘或棱角。

pic15.gif (8287 bytes)

  如果顶点的法向量偏向任何一个面,会使表面上的亮度增加或减少,而这又由它与光源之间的夹角来决定。上面右图的例子显示了这一情况。图中我们还是只能看到两个面的侧面,并且顶点的法向量偏向S1一侧,这样就使它与光源的夹角比正常情况下有所减小。

  在使用Gouraud明暗处理模式时,我们也可以使一些对象具有尖锐的边缘。如果正在使用执行缓冲(execute buffers),,那么程序就需要对边缘上的点的法向量制作一个副本,如下图所示,我们可以看到,位于尖锐边缘的点的法向量都有两个。

pic16.gif (3070 bytes)

  如果使用IDirect3DDevice3::DrawPrimitiveIDirect3DDevice3::DrawIndexedPrimitive方法来渲染场景,必须将具有尖锐边缘的对象定义为三角形列表(triangle list)的形式,而不是采用三角带(triangle strip)的形式。如果对象有三角带来构成,那么Direct3D就会把它当作由许多三角形面片组成的一个单独的多边形来对待。而这时,Gouraud模式对多边形的每一个面片和两个相邻面片之间的部分都有效。这样最终的结果就会使对象具有平滑的表面。然而以三角形列表的形式构成的多边形则是由一系列不相连贯的三角形面片来组成,这种情况下,Gouraud模式只对多边形的每一个面片有效,而对面片之间的部分是无效的。这时,如果三角形列表中的几个三角形相邻的话,那么他们之间就会出现尖锐的边缘。

  另一种选择是使用平面明暗处理模式。这样在计算量上是很有效的,但是它的效果会不如采用Gouraud模式时的逼真。

4.5 三角形内插

当系统渲染一个场景时,它会在三角形上对三角形的顶点间进行内插运算。三角形内插运算包括:

内插运算在不同的明暗处理模式下也是不同的,如下所示:

  当色彩模式不同时,颜色内插和镜面属性内插也是不同的。在RGB颜色模式下,系统使用红、绿、蓝三种颜色成分来进行内插。单色模式(或ramp模式)下,系统仅使用顶点颜色的蓝色成分。

  颜色的阿尔法成分通常单独来进行内插运算,这是因为设备驱动器可以有两种方法来实现透明效果:纹理融合(texture blending)或点画法(stippling)。

  程序可以使用D3DPRIMCAPS结构的dwShadeCaps成员来决定当前的设备驱动器支持哪种内插形式。

 

上一页 | 目录 | 下一页