DirectX3D中的雾化效果及其实现
2010-02-23徐成虎田东平
徐成虎, 田东平
(1.宝鸡文理学院教育科学与技术系, 陕西 宝鸡 721007; 2.宝鸡文理学院计算机软件研究所, 陕西 宝鸡 721007)
0 前 言
雾化是Direct3D程序模拟真实世界的云雾、水汽和灰尘等自然景观.在雾气比较严重的地区,往往距离观察点很近的物体比较清晰,而远处的物体随着距离的增加会变得越来越模糊,雾化效果正是能够增加三维场景真实感的一种技术.随着计算机技术的飞速发展,越来越多的计算机硬件支持更多、更好、更快的三维图形显示效果,利用Direct3D开发的实时三维图形已经可以和电影效果相媲美.
1 雾化计算及其实现
有两种方法可以将雾化添加到场景中:像素雾化和顶点雾化.本文着重阐述了雾化方程式中用到的公式以及实现顶点雾化和像素雾化.一个应用程序使用顶点渲染可以实现雾化,与此同时,亦可实现像素雾化.
C++应用程序可以通过变换距离进行雾化效果处理,从而改变场景中物体的颜色.枚举型D3DFOGMODE识别包含了3种雾化公式[1],所有的公式都计算一个雾化因子并作为距离函数,在此假定参数由应用程序设置.
1.1 线性雾化
1.2 指数雾化
像素雾化和顶点雾化都支持线性及指数雾化公式.用D3DFOG_EXP设置f=1/ed×density,其中e为自然对数的基(近似为2.718 28),density是从0到1之间变化的任意雾密度,d含义同上.用D3DFOG_EXP2设置f=1/e(d×density)2,其中e,density和d的含义均同前所述.
系统将雾化因子保存在顶点镜面颜色的Alpha组件中.如果应用程序执行自身的变换和光照,则可手动地插入雾化因子的值,系统在渲染阶段会用到它,图1给出了这些公式的量化曲线.
图1 不同指数和雾密度对应的量化曲线
D3DFOG_LINEAR以1作为起始点,0.0作为最大点,它不是相对于近平面或远平面进行测量.当D3D计算雾化处理效果时,从前述3个方程式中选择一个用于计算混合公式中所需的雾化因子,公式为C=f×Ci+(1-f)×Cf.此公式通过雾化因子f可以对当前多边形C的颜色进行有效换算,并将它们的乘积加到雾化颜色C上,这主要是对雾化因子逐位求逆来进行计算,结果颜色值作为一个距离因子是雾化颜色和物体原颜色的混合.该公式适用于支持所有DirectX7.0以及其后的更高设备.
1.3 雾化参数
设备渲染状态控制雾化参数.像素雾化和顶点雾化均支持前述所有的雾化公式.枚举型D3DFOGMODE定义了一些常量,可以用它们来标识D3D所要使用的雾化公式,其中渲染状态D3DRS_FOGTABLEMODE控制像素雾化方式,D3DRS_FOGVERTEXMODE控制顶点雾化方式.当使用线性雾化公式时,可通过渲染状态D3DRS_FOGSTART与D3DRS_FOGEND设置初始距离和最大距离.系统如何识别这些值,主要依赖于应用程序所使用的雾化类型.当使用像素雾化时,若基于z和w的深度被使用,则相应的雾化类型与它们的起始和最大单元对应关系如下:
Fog type Fog start/end units
Pixel(Z) Device space[0.0,1.0]
Pixel(W) Camera space
Vertex Camera space
当使用一个指数雾化公式时,渲染状态D3DRS_FOGDENSITY控制所用的雾化密度.雾化密度实际上是一个加权因子,它在包含端点1的0~1范围内变化,用来缩放指数中的距离.系统所使用的雾化混合的颜色通过渲染状态设备D3DRS_FOGCOLOR来控制.
1.4 雾化混合
正如在雾化公式中所描述的,雾化混合指雾化因子和物体颜色在场景中共同作用,以产生最终的效果颜色[2-4].渲染状态D3DRS_FOGENABLE控制雾化混合,设置渲染状态为TRUE时使用雾化混合的代码示例如下,缺省值为FALSE:
//For this example,g_pDevice is a valid pointer
//to an IDirect3DDevice9 interface.
HRESULT hr;
hr= g_pDevice-> SetRenderState (D3DRS_FOGENABLE,TRUE);
If FAILED(hr)
Return hr;
1.5 雾化颜色
像素雾化和顶点雾化的颜色都是通过渲染状态D3DRS_FOGCOLOR来进行设置,渲染状态值可以是任意RGB色,指定值为RGBA色,其中Alpha组件忽略不计.如下的C++示例将雾化颜色设置为白色:
/*For this example,the d3dDevice variable is a
/* valid pointer to an IDirect3DDevice9 interface.
HRESULT hr;
hr=d3dDevice->SetRenderState(
D3DRS_FOGCOLOR,
0x00FFFFFF);//Highest 8 bits are not used.
If (FAILED(hr))
Return hr;
固定函数流水线与可编程流水线对雾化的运用完全不同:(1)驱动程序支持D3DPMISCCAPS_FOGANDSPECULARALPHA时,如果使用固定函数流水线并对D3DRS_FOGCOLOR进行设置,则像素渲染中的v1.w等于雾化渲染状态中的值域;如果使用可编程流水线,尽管d1.w被显式地写入顶点渲染,但像素渲染中的v1.w仍等于0.(2)驱动程序不支持D3DPMISCCAPS_FOGANDSPECULARALPHA时,如果使用固定函数流水线并对D3DRS_FOGACOLOR进行设置,则像素渲染中的v1.w等于雾化渲染状态中的值;如果雾化被显式写入顶点渲染,则像素渲染中的v1.w等于雾化值,且介于0~1之间;如果上述两种情况都不适用,即使d1.w被显式写入顶点渲染,但像素渲染中的v1.w仍为0.
1.6 顶点雾化
系统执行顶点雾化时,对多边形中每一顶点进行雾化计算,在光栅化过程中再对多边形面上的计算结果进行插值运算[5,6].顶点雾化效果由D3D光照和变换引擎来计算.
如果应用程序不使用D3D进行变换和光照处理,那么应用程序必须执行雾化计算,在这种情况下,把在镜面颜色中Alpha组件里计算出的雾化因子放置于每一个顶点中.此时,用户可以随心所欲地使用自己想要的基于发散的、变体积的或其他的任何公式.D3D使用被提供的雾化因子在每一个多边形的面上进行插值计算,那些执行自身变换和光照的应用程序也必须进行自己的顶点雾化计算.因此,如在雾化混合和雾化颜色中所叙述的,这样的应用程序仅需通过相关的渲染状态使用雾化混合和设置雾化颜色即可.
按照下列步骤可以使用应用程序中的顶点雾化:
(1)设置D3DRS_FOGENABLE为TRUE启用雾化混合;
(2)在渲染状态D3DRS_FOGCOLOR中设置雾化颜色;
(3)通过设置枚举型D3DFOGMODE中的变量渲染状态D3DRS_FOGVERT-EXMODE来选择所需的雾化公式;
(4)针对在渲染状态中选择的雾化公式,设置相应的雾化参数.
下面的C++代码示例,给出了这些步骤在程序中的具体实现:
Void SetupVertexFog(DWORD Color, DWORD Mode, BOOL UseRange, FLOAT Density)
{
Float Start=0.5f, //Linear fog distances
End=0.8f;
g_pDevice->SetRenderState (D3DRS_FOGENABLE,TRUE);
}
g_pDevice->SetRenderState (D3DRS_FOGCOLOR, Color);
//Set the fog parameters.
If (D3DFOG_LINEAR==Mode)
{
g_pDevice->SetRenderState (D3DRS_FOGVERTEXMODE,Mode);
g_pDevice->SetRenderState (D3DRS_FOGSTART,*(DWORD*)(&Start));
g_pDevice->SetRenderState (D3DRS_FOGEND,*(DWORD*)(&End));
}
else
{
g_pDevice->SetRenderState (D3DRS_FOGVERTEXMODE,Mode);
g_pDevice->SetRenderState (D3DRS_FOGDENSITY,*(DWORD*)(&Density));
}
//Enable range-based fog if desired (only supported for vertex fog).For this
//example,it is assumed that UseRange is set to a nonzero value only if the driver
//exposes the D3DPRASTERCAPS_FOGRANGE capability. Note: This is slightly
//more performance intensive than non-range-based fog.
If (UseRange)
g_pDevice->SetRenderState (D3DRS_RANGEFOGENABLE,TRUE);
}
2 基于发散的雾化
图2 基于发散雾化的俯视图
只有D3D在使用带有D3D变换和光照引擎的顶点雾化时,才使用基于发散的雾化计算,这是因为像素雾化是在设备驱动程序中完成的.目前不存在支持基于发散的单个像素雾化硬件,如果应用程序执行自身的变换和光照,则它也必须执行自身的雾化计算和基于发散的雾化计算,否则不必计算.有时候,使用雾化会产生图形赝象[7,8],以致在非直觉方式下使物体和雾化颜色混为一体.例如,在一个有两个可见物体的场景中一个在足够远处,可通过雾化影响,另一个在足够近处,不受雾化影响.如果视觉区域在空间旋转,即使物体是静止的,雾化效果也会明显发生变化.图2给出了这种情况的俯视图.
3 结束语
(1)使用顶点渲染时,必须使用顶点雾化,这是通过使用顶点渲染将每一顶点的雾化强度写入雾化寄存器来完成的.像素渲染完成之后,对雾化数据与雾化颜色进行线性插值运算.该亮度在像素渲染中是不可用的.
(2)在基于发散的雾化中,D3D使用从观察点(视点)到顶点的有效距离(实际距离)进行雾化计算,D3D会随着两点间距离的增大而增加雾化效果,而并非是增加场景中顶点的深度,因此避免了旋转赝象.如果当前设备支持基于发散的雾化,则当调用GetDeviceCaps时,它可对D3DCAPS9成员RasterCaps中的D3DPRASTERCAPS_FOGRANGE进行设置.
(3)使用基于发散的雾化,需将渲染状态D3DRS_RANGEFOGNABLE设为TRUE.D3D在变换和光照过程中对基于发散的雾化进行计算,不使用D3D变换和光照引擎的应用程序必须执行它们自己的顶点雾化计算.在这种情况下,镜面组件中的Alpha组件为每个顶点提供基于发散的雾化因子.
参考文献
[1] 普建涛.实时计算机图形学[M],北京:北京大学出版社,2004.
[2] 唐荣锡,汪嘉业,彭群生.计算机图形学教程[M].北京:科学出版社,1994.
[3] 陈 卡.DirectX93D图形程序设计[M].上海:上海科学技术出版社,2003.
[4] David F. Rogers著,石教英,彭群生译.计算机图形学的算法基础[M].北京:机械工业出版社,2002.
[5] Edward Angel.交互式计算机图形学——自顶向下方法与OpenGL应用(第三版 影印版)[M].北京:高等教育出版社,2003.
[6] Richard S. Wright, Jr. Michael Sweet著,潇湘工作室译.OpenGL超级宝典(第二版)[M].北京:人民邮电出版社,2001.
[7] 杨 钦,徐永安,翟红英.计算机图形学[M].北京:清华大学出版社,2005.
[8] 清源计算机工作室.MATLAB6.0高级应用——图形图像处理[M].北京:机械工业出版社,2001.