Direct3D通用技术与特殊效果

原文见http://dev.gameres.com/Program/Visual/3D/d3dimhlp/d3dindex.htm

Direct3D提供了一套强有力的工具来帮助我们提高三维场景的逼真度。下面我们将讨论一些Direct3D产生的通用的特殊效果。但是,可能出现的效果并不只限于这里所讨论的内容。下面我们分成几个主要的部分:

1. 雾化

  下面我们介绍有关雾化的内容以及不同雾化特性的使用:

1.1 雾化介绍

  在一个三维场景中使用雾化效果可以增加真实感、产生一定的气氛或者设置一种情绪,也可以减少由于远处的几何体靠近观察者所产生的失真。Direct3D立即模式支持两种雾化模型,像素雾化和顶点雾化,它们都有各自的特点和编程接口。

  从本质上来说,雾化就是将场景中对象的颜色与根据对象深度或者离观察的距离而选择的一个雾的颜色进行混合而得到的。当对象的距离不断增加时,它原始的颜色也就更多的融合到雾的颜色中,这样就好像对象在逐渐的模糊。下面的插图中的一幅没有使用雾化,而另一幅使用了雾化。

pic95.gif (56669 bytes)

  上图中,左边的场景有清晰的地平线,场景中所有的风景都是可见的。右边的场景使用了雾化效果,雾的颜色预备将的颜色一样,这样就使物体逐渐的消失在背景中。如果结合使用分立的(discrete)雾化效果,那么就可以在场景中添加一定的情绪,使场景中对象的颜色变的柔和。

Direct3D提供了两种产生雾化的方法,像素雾化顶点雾化,它们是根据产生雾的方式来命名的。

1.2 雾化公式

  通过改变Direct3D计算雾化的方式,我们可以控制如何对对象进行雾化处理。D3DFOGMODE枚举类型中包含了用来确定三种雾化公式的成员。所有计算雾化因子的公式都是距离的函数。如何计算距离也要根据投影矩阵或者Range-Based雾化是否有效而定。

  在线性公式中,start是雾化效果开始的距离,end是雾化结束的距离,d表示深度(或到观察点的距离)。d值的增加就表示对象在不断远离。像素雾化和顶点雾化都支持线性公式,但是目前只有像素雾化才支持指数公式

f =1 / ed×density

  上面的两个公式中,e表示自然对数的低(它的值约为2.71828),density表示任意的雾化密度,范围从0.01.0d表示深度(或离观察点的距离)。

注:系统将雾化因子存储在顶点镜面颜色的alpha乘分钟。如果程序执行自己的变换和光线处理,可以手控的插入雾化因子值。

下图中描绘出了上述公式的曲线图:

pic96.gif (16077 bytes)

Direct3D计算雾化效果时,就会使用上述公式中的一个来计算雾化因子,再应用到下面的公式中:

C=f·Ci+(1-f)·Cf

  公式中,使用雾化因子f对当前多边形的颜色Ci进行缩放,然后再和雾的颜色Cf(1-f)的乘积相加。得到的颜色值就是雾的颜色与原始颜色的混合值。这个公式适用于DirectX 6.0中支持的所有设备。对于遗留的ramp设备,雾化因子对漫射和镜面颜色成分进行缩放,范围也在0.01.0之间(包括它们)。通常,近处平面的雾化因子为1.0,远处的为0.0

1.3 雾化融合

  雾化融合操作是指将雾化因子应用到雾和对象的颜色,从而产生最终表现在场景中的颜色。D3DRENDERSTATE_FOGENABLE渲染状态控制雾化融合。将它设置为TRUE表示雾化融合有效,缺省时为FALSE


// For this example, g_lpDevice is a valid pointer
// to an IDirect3DDevice3 interface.
HRESULT hr;
hr = g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE,TRUE);
if FAILED(hr)
return hr;

1.4 雾化颜色

  像素雾化和顶点雾化的雾化颜色都通过D3DRENDERSTATE_FOGCOLOR渲染状态来设置。这个值可以是任意的RGB颜色,它以RGBA方式来声明,alpha成分被忽略了。

  下面例子中将雾化颜色设置为白色:


/* For this example, the g_lpD3DDevice variable is
* a valid pointer to an IDirect3DDevice3 interface.
*/

HRESULT hr;
hr = pd3dDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR,0x00FFFFFF); // Highest 8 bits aren't used.
if(FAILED(hr))
return hr;

1.5 基于范围的雾化

  有时,使用雾化可能会产生图形失真,会使对象以非直观的方式来对雾化颜色进行渲染。例如,一个场景中有两个可见的对象,其中一个的距离可以使用雾化效果,而另一个则距离较近,不受雾化的影响。这时,如果观察区域发生了旋转,那么所表现出来的雾化效果就会改变,即使场景中的对象并没有移动。下图向我们描绘出了这样的情况:

pic97.gif (11867 bytes)

  基于范围的雾化是又一种决定雾化效果的方法,并且它更加精确。在这种方式中,Direct3D使用视点到顶点的真实距离来计算雾化效果,并随着两点间距离的增加,逐渐增加雾化效果,而不是根据顶点在场景中的深度变化,这样就能避免旋转产生的失真。

  如果当前设备支持基于范围的雾化,那么可以调用IDirect3DDevice3::GetCaps方法在D3DPRIMCAPS结构的dwRasterCaps成员中设置D3DPRASTERCAPS_FOGRANGE能力标志。要使基于范围的雾化有效,要将D3DRENDERSTATE_RANGEFOGENABLE渲染状态设置为TRUE

Direct3D在变换和光线处理时计算基于范围的雾化。程序不使用Direct3D的变化和光线处理引擎时,也可以执行自己的顶点雾化计算。这时,要为每个顶点的镜面成分的alpha成分提供基于范围的雾化因子。

注:目前,还没有硬件支持每像素的基于范围的雾化(per-pixel range-based fog)。因此,只有在使用Direct3D的变换和光线处理引擎进行顶点雾化时,才能使用基于范围的雾化。如果你的程序执行自己的变换和光线处理,那么也必须执行自己的雾化计算。

1.6 像素雾化

  这一部分介绍像素雾化的内容:

1.6.1 关于像素雾化

  像素雾化的名称来源于它的处理方式,它是通过计算每个像素来得到雾化效果的。(这与顶点雾化不同,Direct3D在执行变化和光线处理时使用顶点雾化方式。)像素雾化有时也被称为“表格雾化”,因为一些设备要使用一个预先计算好的查询表格来决定雾化因子(使用每个像素的深度)。像素雾化可以使用任何D3DFOGMODE枚举类型的成员声明的雾化公式。像素雾化公式的指向是根据驱动器而定的,如果驱动器不支持复杂的雾化公式,那它就只能使用较简单的公式来进行计算了。

  1. 注:我们在“基于范围的雾化”中讨论过,像素雾化不支持基于范围的雾化运算。

1.6.2 目相关对基于Z深度

  为了减少由于深度缓冲中z-值的不均匀分布引起的有关雾化的图形失真,大多数硬件设备都使用了目相关(eye-relative)深度来代替基于z的深度值。目相关深度从本质上来说是一个同质坐标系统中的w成分。(Direct3D通过一个设备空间坐标设置的RHW成分的倒数来产生真正的w。)如果设备支持目相关雾化,那么当你调用IDirect3DDevice3::GetCaps方法时,就要在D3DPRIMCAPSdwRasterCaps成员中设置D3DPRASTERCAPS_WFOG标志。(传递给GetCapsD3DDEVICEDESC结构包含了多个D3DPRIMCAPS结构来描述对于不同类型图元所要使用的能力。)

注:除了参考光栅之外,软件设备总是使用z来计算像素雾化效果。

  当支持目相关雾化时,如果提供的投影矩阵在世界空间中产生的z值与设备空间中的w值等价,那么系统会自动使用目相关深度来代替基于z的深度。(调用IDirect3DDevice3::SetTransform方法时,使用D3DTRANSFORMSTATE_PROJECTION值并传递一个D3DMATRIX结构来代表需要的矩阵,这样来设置投影矩阵。)如果投影矩阵不满足需要,雾化效果将会不正确。有关产生一个合适的矩阵的内容,请参看“一个W-Friendly投影矩阵”部分。(“什么是投影变换?”部分中提供的透视投影矩阵是一个合适的投影矩阵。)

使用注意事项:要使用目相关雾化,透视修正纹理映射必须有效。将D3DRENDERSTATE_TEXTUREPERSPECTIVE渲染状态设置为TRUE可以使透视修正纹理映射有效。对于DirectX 6.0及以后的版本,这一设置是默认的。

  Direct3D在基于w(w-based)深度运算中要使用正确设置的投影矩阵。这样,程序就必须设置一个合适的透视矩阵才能得到基于w特性,即使它们不使用Direct3D变换管道。

  Direct3D检查投影矩阵的第4列,如果系数为[0,0,0,1](对于一个仿射投影affine projection),那么系统会在雾化时使用基于z的深度。在这种情况下,也要在设备空间中对线性雾化声明开始和结束距离,范围从最近点的0.0到最远点的1.0。

1.6.3 像素雾化参数

  像素雾化的所有参数都通过设备渲染状态来进行控制。它支持所有“雾化公式”中介绍的公式。将D3DRENDERSTATE_FOGTABLEMODE设置为D3DFOGMODE枚举类型中相应的成员,就可以得到我们想要使用的公式。

  使用线性雾化公式时,通过D3DRENDERSTATE_FOGTABLESTARTD3DRENDERSTATE_FOGTABLEEND渲染状态来设置开始和结束距离。这些距离都使用世界空间中的单位,但是当使用软件设备时除外,这时距离是在设备空间中。

  使用指数雾化公式时,D3DRENDERSTATE_FOGTABLEDENSITY渲染状态控制雾化密度雾化密度也是一个很重要的因子,范围从0.01.0(包括它们),它对能指数中的距离值进行缩放。

D3DRENDERSTATE_FOGCOLOR渲染状态用来控制在雾化融合时使用的颜色。

1.6.4 使用像素雾化

  我们按下述步骤使用像素雾化:

  1. D3DRENDERSTATE_FOGENABLE渲染状态设置为TURE,使雾化融合有效。
  2. D3DRENDERSTATE_FOGCOLOR中设置所需的雾化颜色。
  3. D3DRENDERSTATE_FOGTABLEMODE渲染状态设置为D3DFOGMODE枚举类型中相应的成员,选择所需的雾化公式。
  4. 在相应的渲染状态中设置雾化参数。包括线性雾化的开始、结束距离和指数雾化的雾化密度。

  下面我们来看一个例子:


// For brevity, error values in this example are not checked 
// after each call. A real-world application should check 
// these values appropriately.
//
// For the purposes of this example, g_lpDevice is a valid
// pointer to an IDirect3DDevice3 interface.

void SetupPixelFog(DWORD dwColor, DWORD dwMode)
{
float fStart = 0.5f, // for linear mode
fEnd = 0.8f,
fDensity = 0.66; // for exponential modes
// Enable fog blending.
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE);
// Set the fog color.
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR, dwColor);
// Set fog parameters.
if(D3DFOG_LINEAR == dwMode)
{
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGTABLEMODE, dwMode);
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGTABLESTART, *(DWORD *)(&fStart));
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGTABLEEND, *(DWORD *)(&fEnd));
}
else
{
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGTABLEMODE, dwMode);
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGTABLEDENSITY, *(DWORD *)(&fDensity));
}
}

注:一些雾化参数需要是浮点值,但是IDirect3DDevice3::SetRenderState方法的第二个参数只接受DWORD值。上面例子中没有进行数据变化就将一些浮点值提供给了SetRenderState,它是将浮点变量的地址赋予DWORD指针,然后再使用它们。

1.7 顶点雾化

  下面我们讨论有关顶点雾化的内容:

1.7.1 关于顶点雾化

  当系统执行顶点雾化时,它在多边形的每个顶点处进行雾化运算然后在进行光栅处理时在多边形面内对结果进行内插。与像素雾化不同,顶点雾化只支持线性雾化公式(D3DFOG_LINEAR)。由于顶点雾化效果要通过Direct3D的光线处理和变换引擎来计算,因此它的大部分参数都要通过IDirect3DDevice3::SetLightState方法在设备的光线状态中来进行设置。

  如果你的程序不使用Direct3D来进行变换和光线处理,那么就必须执行你自己的雾化运算。这时,你的程序可以将计算的雾化因子放置在每个顶点的镜面颜色的alpha成分中。你可以使用任意的公式——基于范围的或其它的。Direct3D使用提供的雾化因子在每个多边形面内进行内插。不使用Direct3D的变化和光线处理的程序不需要通过光线状态来设置顶点雾化参数,但是仍然要使雾化有效,并通过相关的渲染状态设置雾化颜色。

1.7.2 顶点雾化参数

  我们通过IDirect3DDevice3::SetLightState方法设置相应的设备光线状态来控制顶点雾化参数。目前,顶点雾化值支持线性雾化公式,它要通过将D3DLIGHTSTATE_FOGMODE灯光状态设置为D3DFOG_LINEAR来进行选择。通过D3DLIGHTSTATE_FOGSTARTD3DLIGHTSTATE_FOGEND灯光状态来设置线性雾化的开始和结束距离。所有的距离都在世界空间中。

注:尽管目前顶点雾化不支持指数公式,但是SetLightState方法所使用的D3DLIGHTSTATETYPE枚举类型中仍然包含了一个D3DLIGHTSTATE_FOGDENSITY成员。这个成员目前是没有用的,它是为将来的能力扩展而设置的。

  雾化融合使用颜色通过D3DRENDERSTATE_FOGCOLOR设备渲染状态来进行控制。

  执行自己的变换和光线处理的程序也必须执行自己的顶点雾化运算,并且也不能再使用灯光状态了。这样一来,这样的程序就只需要使雾化融合有效和通过相应的渲染状态设置雾化颜色了。

1.7.3 使用顶点雾化

  按下面的步骤来使用顶点雾化:

  1. D3DRENDERSTATE_FOGENABLE设置为TRUE使雾化融合有效。
  2. D3DRENDERSTATE_FOGCOLOR渲染状态中设置雾化颜色。
  3. D3DLIGHTSTATE_FOGMODE灯光状态设置为D3DFOGMODE枚举类型的一个成员来选择一个雾化公式。(目前止支持D3DFOG_LINEAR。)
  4. 在相应的灯光状态中设置所需的雾化参数。不使用与渲染状态相关的雾化参数——它们只适用于像素雾化。

  我们来看一个例子:


// For brevity, error values in this example are not checked 
// after each call. A real-world application should check 
// these values appropriately.
//
// For the purposes of this example, g_lpDevice is a valid
// pointer to an IDirect3DDevice3 interface.

void SetupVertexFog(DWORD dwColor, BOOL fUseRange)
{
float fStart = 0.5f, // linear fog distances
fEnd = 0.8f;
// Enable fog blending.
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGENABLE, TRUE);
// Set the fog color.
g_lpDevice->SetRenderState(D3DRENDERSTATE_FOGCOLOR, dwColor);
// Set fog parameters.
//
// REMEMBER: Vertex fog formula parameters are connected
// to the lighting states, not render states.
g_lpDevice->SetLightState(D3DLIGHTSTATE_FOGMODE, D3DFOG_LINEAR);
g_lpDevice->SetLightState(D3DLIGHTSTATE_FOGSTART, *(DWORD *)(&fStart));
g_lpDevice->SetLightState(D3DLIGHTSTATE_FOGEND, *(DWORD *)(&fEnd));

// Enable range-based fog if desired (only supported for vertex fog).
// For this example, it is assumed that fUseRange is set to non-zero 
// only if the driver exposes the D3DPRASTERCAPS_FOGRANGE capability.
// 
// Note: this is slightly more performance intensive
// than non-range-based fog.
if(fUseRange)
g_lpDevice->SetRenderState(D3DRENDERSTATE_RANGEFOGENABLE,TRUE);
}

注:一些雾化参数需要是浮点值,但是IDirect3DDevice3::SetRenderState和IDirect3DDevice3::SetLightState方法的第二个参数只接受DWORD值。上面例子中没有进行数据变化就将一些浮点值提供给了这些方法,它是将浮点变量的地址赋予DWORD指针,然后再使用它们。


2. 公告板技术

  当我们创建一个三维场景时,我们可以通过对一些看起来具有三维效果的二维对象进行渲染的方法来提高性能。公告板技术正是出于这种思想来考虑的。

  通常,公告板就是指路边的一些告示牌。Direct3D程序中,可以通过定义一个固定的矩形并对它使用纹理的方法来创建和渲染这样一种公告板。三维图形学中的公告板就是这种公告板的一种扩展。它的目的就是使二维对象能够看起来具有三维效果。这项技术将一幅包含了对象图象的纹理应用到一个矩形图元上。这个图元随着观察者不断旋转,使它总能朝向观察者。我们不用考虑对象的图象是否是矩形。公告板上的某些地方可以是透明的,这样就能使我们不想见到的公告板图象的某些部分变得不可见。

  许多游戏都对它们的动画精灵使用公告板技术。例如,当我们在一个三维迷宫中行走时,我们可能会看到一些武器或者其他一些奖品,并且能够拾起它们。这些物品通常都是将一些二维纹理图象应用到一个矩形图元上生成的。我们还经常使用公告板技术来渲染场景中的树木和灌木丛等的图象。

  当我们要将一个图象应用到一个公告板时,我们首先得将矩形图元进行旋转,使图象能够朝向观察者的方向。然后,程序将它平移到所需的位置。接着,就可以将纹理应用到图元上了。

  公告板技术最好能用于对称的物体,特别是沿垂直方向对称的物体。它还要求观察者的水平高度不能太高,否则就有可能很明显的感觉到对象是二维的,而不是三维的。


3. 云、烟与蒸汽效果

  云、烟和蒸汽效果都可以通过公告板技术来实现。通过沿两个轴对公告板进行旋转,我们就可以从任意角度看到公告板的内容。通常,我们的程序都是沿着水平和垂直两个轴来进行旋转。

  要制造一个简单的云的效果,我们可以将一个矩形图元沿一个或两个轴进行渲染,使它总能朝向观察者方向。然后,将一幅云的纹理应用到图元上,并使用透明处理。我们也可以根据时间的变化改变云的图象来产生动画效果。

  我们也可以使用一组图元来产生更加复杂的云的效果。云的每一部分都是一个矩形图元。这些图元可以随时间独立的移动位置,从而产生出动态效果。下图展示了这种方法:

pic98.gif (25763 bytes)

  烟的产生方法与云相似。通常,烟需要多个公告板,就和复杂的云一样。一般情况下,烟需要有翻滚和上升的效果,这样就要求组成烟的公告板能够进行相应的移动。随着烟的上升与扩散,需要的公告板数量也会增加。

  蒸汽效果实际上就是一些不会上升的烟。但是与烟一样,它也会逐渐扩散。下面的插图展示了使用公告板来模拟蒸汽效果的技术:

pic99.gif (25681 bytes)

4. 纹理融合技术

这一部分我们介绍使用纹理融合技术来产生一些特殊效果或增加真实感等的内容。我们分以下几个部分来进行讨论:

4.1 凹凸映射

  纹理融合技术可以为一个图元创建具有复杂纹理的表面。特别是对于光滑的表面,效果明显。例如,我们可以将一幅经过抛光的平滑的木谷仓的纹理应用到场景中的一个桌面上。

  但是,如果只使用纹理融合技术就不能满足粗糙表面的要求了,比如一些粗糙的树皮等。但幸运的是,Direct3D中有一种被称为凹凸映射(Bump Mapping)可以解决这一问题。一个凹凸映射实际上就是一幅包含了深度信息的纹理。也就是说,它储存了一些用来表示表面上高低位置的值。

  程序使用融合stage将凹凸映射应用到纹理上。将包含凹凸映射的融合stage的纹理融合操作设置为D3DTOP_BUMPENVMAPD3DTOP_BUMPENVMAPLUMINANCE。详细内容见D3DTEXTUREOP和“创建融合stage”部分。

4.2 细节纹理映射

  通过多纹理或多通道融合,Direct3D使程序可以细节纹理应用到图元上。使用细节纹理,可以在表面上产生磨损的痕迹、凹凸的表面以及其他一些表面属性。我们也可以对使用细节纹理应用mipmap。详细内容,清参看“Mipmap纹理过滤”部分。

  细节纹理还可以被用作深度提示(depth cue)。例如,我们现在要模拟一架正在着陆的直升机。当直升机接近地面时,由于放大,地面纹理会变得模糊不清。这时,我们要区分到地面的距离就会有一定困难。如果我们对地面使用了类似于沙砾等物体的细节纹理,我们就可以获得足够的深度提示来正确的操纵降落的直升机。

  如果观察者远离图元,我们大概不会想得到很多的细节展示。但要注意,当程序不使用细节纹理时,图元可能会看起来更加明亮一些。这时,我们可以通过使用一个光线映射纹理使图元变暗来进行补偿。


5. 火焰、闪光、爆炸效果等

  我们可以使用Direct3D来模拟自然界中能量释放的各种现象。例如,我们可以将一些火焰一样的纹理应用到公告板上来产生火的效果。我们还可以利用一系列的纹理来产生动画效果,并可以控制公告板变化的速度来提高真实感。如果我们要看到三维的火焰效果,可以将公告板分层放置,然后再对它们使用纹理。

  闪光效果可以通过将连续的明亮光线映射到场景中所有的图元上来进行模拟。但是这种方法的系统开销很大,因此只能允许在某些区域内来使用。也就是说,场景中闪光出现的地方可以先使用这种方法。

  还有一种技术是将公告板放置在视口的前面,这样就挡住了整个视口。然后,我们将一些连续的白色纹理应用到公告板上,并根据时间逐渐减少透明度,这样,整个场景就会逐渐的消退成白色。这是一种系统开销较小的模拟闪光的方法。但是,这种方法很难模拟从一个点光源产生的闪光。

  使用上面所说的模拟火焰、闪光效果的方法,我们也可以模拟爆炸效果。我们可以用一个公告板来模拟爆炸的冲击波产生的一缕烟雾,同时,使用一系列的公告板来模拟火焰,另外还可以在视口前放置一个单独的公告板来模拟闪光效果。

  能量柱(beam)可以使用公告板来进行模拟。也可以使用用直线列表和直线带定义的图元来进行模拟。

  程序可以使用公告板或者是由三角形列表定义的图元来创建force fields。使用图元时,要在三角形列表中定义一些分离的三角形,它们均匀的覆盖住force field所占的区域。三角形之间可以有缝隙,这样就能看到三角形后面的场景了。然后,将纹理应用到三角形上。


6. 运动模糊

  如果我们要在一个三维场景体现出物体的速度感,我们可以将这个物体模糊化来进行处理,同时可以在物体后面留下一条模糊的运动痕迹。Direct3D程序可以通过在每一帧中对物体进行多次渲染的方法来实现之一效果。

  我们知道,Direct3D程序通常是将场景渲染到一个离屏缓冲器中。当程序调用IDirectDrawSurface4::Flip方法时,在将缓冲器的内容显示到屏幕上。这样,我们就可以在显示之前对每一帧中的对象进行多次渲染。

  因此,程序需要多次调用DrawPrimitive的方法,重复的传递同一个三维物体。在每次调用之前,物体的位置要有轻微的变化,这样就能制造出一系列模糊的物体图象。如果物体有一个或多个纹理,我们可以将它所有的纹理进行透明处理,然后渲染出来作为运动模糊的第一幅图象。以后每次渲染这物体时,将它的透明度逐渐减小。当在最后的位置上渲染物体时,物体的纹理不能透明。但是如果我们需要纹理透明效果时,可以将最后位置的物体纹理处理为透明。在任何情况下,帧中物体的原始图象应该是最透明的,最后一幅图象应该是最不透明的。

  当我们渲染完一系列物体图象以及场景中其他部分之后,就可以调用IDirectDrawSurface4::Flip方法来显示这一帧了。

  如果我们要模拟观察者高速移动的效果,可以对整个场景进行模糊处理。这样,程序就需要在每一帧中对整个场景进行多次渲染。每一次渲染场景时,还必须移动观察点的位置。如果场景十分复杂,那么当移动速度增加时,效果就会有所降低,这是因为每一帧中需要渲染的东西增加的缘故。


7.  模板缓冲技术

  我们使用模板缓冲来对图象中的像素进行掩模处理,它可以决定是否绘制该像素。详细内容清参看“模板缓冲”部分。

  使用模板缓冲,Direct3D程序能够得到很多特殊的效果。我们将一些常用的效果列在下面:

7.1 渐隐、淡入淡出与Swipe

  现在,越来越多的特殊效果出现在电影和视频图象中,这其中就包括了渐隐、淡入淡出以及swipes

  渐隐技术就是将一幅图象逐渐的用另一幅图象替换掉。我们可以使用Direct3D的多纹理融合技术来达到这样的效果,但是我们一般还是选用模板缓冲来完成这一任务。这样,我们就能在使用模板缓冲进行渐隐处理时,仍然可以使用多纹理融合来进行其他效果的处理。

  当程序执行渐隐操作时,要渲染两个不同的图象。这时,使用模板缓冲来控制将哪幅图象的像素绘制到渲染目标表面。我们可以定义一系列模板掩模,并将它们拷贝到模板缓冲中。另一方面,我们也可以为第一帧定义一个基本的模板掩模,然后随着帧的变化,再对基本掩模进行适当的改变。

  在渐隐操作开始时,我们设置的模板函数和模板掩模应该使开始图象的大部分像素能够通过测试;而对于终止图象,它的大部分像素则不应该通过测试。对于连续的帧,模板掩模要不断变化,这样才能使能够通过测试的开始图象的像素不断的减少,而随着帧的变化,越来越多的终止图象的像素将能够通过测试。通过这样的处理,我们就可以使用任意的图象来实现渐隐效果了。

  淡入淡出实际上是渐隐技术的一种特殊情况。淡入技术就是由一幅黑色或白色的图象逐渐变换为一幅场景中的图像。淡出则刚好相反,它是由一个场景图象逐渐变换为黑色或白色。

Direct3D程序还可以使用另一种类似的技术——swipe。例如,如果我们执行了一个从右至止左的swipe操作,最终的图象就会逐渐从右至左的滑出到开始图象的上面。同执行渐隐操作时一样,我们需要定义一些列的模板掩模,并将这些掩模加载到连续变化的帧的模板缓冲中;或者,我们可以对开始模板掩模进行连续的调制,再将调制后的掩模值应用到图象上。我们用这些模板掩模来控制开始图象和终止图象的像素的填写,从而实现预想的效果。

  一个swipe操作要比一个渐隐操作更加复杂,因为它要按照与swipe相反的顺序来读取像素。也就是说,当一个swipe操作从右到左执行时,那么程序就必须在终止图象中从左到右的读取像素。

7.2 贴纸

Direct3D程序用贴纸技术来将一个特殊图元图象中的像素绘制到渲染目标表面。我们可以对具有共面多边形的图元使用这一技术,这样可以保证它们能够被正确的渲染。

  现在我们举一个例子,我们要把一些轮胎痕迹以及黄色警戒线渲染到一条公路上。这时,这些痕迹就会直接位于公路的表面上,因此,痕迹的z值就与公路的z值完全相同。这样,深度缓冲就不能清楚的将它们两者分离开来,位于后面的图元上的一些像素就会被渲染到前面的图元上。最终的图象就会在帧与帧之间产生微弱的闪光。这种效果,我们称之为“Z Fighting”或“flimmering”。

  要解决这一问题,我们就要使用一个模板来将贴纸出现地方的后面图元的像素掩模掉。然后,关闭z-buffering,将前面图元的图象渲染到目标表面上被掩模掉的区域上。

  我们也可以使用多纹理融合来解决这一问题,但是,这样做就会限制其他的使用多纹理融合才能实现的效果的数量。而使用模板缓冲就可以节省多纹理融合的能力,从而能够实现更多的特殊效果。

7.3 合成

  我们可以使用模板缓冲来将2-D3-D图象合成到一个3-D场景中。我们用模板缓冲中的一个掩模来封闭渲染目标表面上的某个区域,然后就可以将2-D信息,例如文本或位图,填写到被封闭的区域。另一方面,程序还可以在渲染目标表面上被模板掩模掉的区域绘制额外的3-D图元,甚至可以绘制一个完整的场景。

  游戏中经常将多个3-D场景合成在一起。例如,驾驶类游戏中通常要显示一个后视镜,镜中显示了驾驶员背后的3-D场景,这时,我们就可以用合成技术来将后视的三维场景合成到后视镜中。

7.4 轮廓与剪影

  模板缓冲还能够用来实现一些抽象的效果,如轮廓与剪影。

  如果我们将一个模板掩模应用到一个与图元有相同形状但是尺寸较小一些的图象上,那么最终的图象就会得到这个图元的轮廓。然后,我们可以在模板掩模的区域填充一个具有固定颜色的图象,从而得到一种类似于浮雕的效果。

  如果模板掩模的大小和形状与图元的大小和形状完全一样,那么最后的图象就会在图元存在的地方出现一个“洞”。我们可以将这个“洞”填充上黑色,这样就得到了这个图元的剪影效果。


8.  彩色光

D3DLIGHT2结构的dcvColor成员声明了一个D3DCOLORVALUE结构。这个结构定义的颜色值是RGBA值,范围从010代表黑色。我们不必将光线的颜色值限制在这个范围内,超出这个范围的颜色值往往会产生一些特殊的效果。例如,我们可以将颜色设置为一个很大的值,这样就可以创建在场景中创建一个强光。也可以将颜色设置为一个负值来创建黑色光,它实际上将光线从场景中删除掉了,通常,这种方法用来加强阴影效果或者是其他一些特殊的效果。

  当我们使用单色光模式时,环境光就会是单色的,这样,我们就无法使场景比当前的环境光等级更暗。另外我们还要注意,RGB模式下的彩色光在单色模式中会转变成灰度光;一个RGB模式中的红色光在单色模式中将会是一个暗淡的白色光而已。


9.   反走样

  使用反走样技术可以减少图象的锯齿状走样——也就是图象中的非水平和垂直线都呈阶梯状。在三维场景中,这种失真经常会在不同颜色的多边形交界的地方变得非常明显。反走样技术能够有效的将这些边界处的像素进行融合,从而使场景看起来更加自然。

Direct3D支持两种反走样技术:边缘反走样(edge antialiasing全表面反走样(full-surface antialiasing。具体使用哪种技术要视我们对性能和视觉效果的要求而定。

9.1 边缘反走样

  在边缘反走样技术中,我们先渲染一个场景,然后再重新渲染走样了的对象的轮廓。系统重新绘制这些边缘,使它们变得稍微模糊从而减少失真。

 如果一个设备支持边缘反走样,那么D3DPRIMCAPS结构中的D3DPRASTERCAPS_ANTIALIASEDGES能力标志会有所反映(调用IDirect3DDevice3::GetCaps方法)。如果真的支持,要将D3DRENDERSTATE_EDGEANTIALIAS标志设置为TRUE,然后使用IDirect3DDevice3::DrawPrimitiveD3DPT_LINESTRIPD3DPT_LINELIST图元类型中的一个来重新绘制场景中的边缘。边缘反走样对于图元是没有定义的,因此在反走样结束之后,要将D3DRENDERSTATE_EDGEANTIALIAS重新设置为FALSE

  重新绘制场景中的每一个边缘可以减少主要的失真,但是它的计算量是很大的。另外,决定哪些边缘是走样的也是非常困难的。一般来说,位于颜色变化较大或者是材质变化较明显的边缘处的走样问题是比较重要的。对于两个颜色相同的多边形的边缘进行反走样是没有意义的。基于这些原因,我们一般还是选择全场景反走样。

9.2 全场景反走样

  全场景反走样是指在一个通道内(不需要第二个通道)进行光栅时,将每个多边形的边缘都进行模糊处理。我们要注意的是,全场景反走样只对三角形和三角形组有效;使用Direct3D是不能对线进行反走样的。

  在某些硬件上,只有当程序渲染从后到前分类的多边形时,才能使用全场景反走样。我们可以用IDirect3DDevice3::方法来检查硬件是否具有这样的特性。调用了GetCaps方法后,检查相关的D3DPRIMCAPS结构的dwRasterCaps成员。如果设备需要从后到前(back to front rendering)的渲染,那么该成员中就会有D3DPRASTERCAPS_ANTIALIASSORTDEPENDENT就会有能力标志。如果设备执行反走样时不考虑多边形的顺序,那么dwRasterCaps成员中就会有D3DPRASTERCAPS_ANTIALIASSORTINDEPENDENT标志。当然,两个标志都没有的话也就是表示设备不能执行全场景反走样。

  检查完多边形顺序的支持后,要将D3DRENDERSTATE_ANTIALIAS渲染状态设置为D3DANTIALIAS_SORTDEPENDENTD3DANTIALIAS_SORTINDEPENDENT并绘制场景。

  当程序不再需要全场景反走样时,要将D3DRENDERSTATE_ANTIALIAS设置为D3DANTIALIAS_NONE来使它无效。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值