Chinaunix首页 | 论坛 | 博客
  • 博客访问: 400193
  • 博文数量: 158
  • 博客积分: 1227
  • 博客等级: 少尉
  • 技术积分: 946
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-20 16:19
文章分类
文章存档

2016年(1)

2015年(1)

2012年(107)

2011年(49)

分类:

2012-03-23 11:26:22

这篇文章很不错,作者分析问题时用的方法很好。

文中MFCTex程序可安装《Advanced Animation with DirectX》随书光盘上的directX sdk取得。9.0c

original address



3D地形多层纹理混合加阴影渲染方法

修正: 
我 重新观察了一下Torque引擎Demo的地形,发现在他在一个方块内最多用了3张贴图。这3张贴图就需要3个Texture Stage,再加上2个提 供Alpha值的Stage,总共就是5个Texture Stage。这对显卡要求太高,至少我的显卡只支持4个Texture Stage。那么就必 须使用MultiPass了,多遍渲染。这篇文章说的把Alpha信息存储在顶点Diffuse里面的方法,还是值得考虑的。因为如果是用Alpha图的 话,Alpha图总会被拉伸得很厉害,使得地形上不同贴图之间仍然不能自然过渡。除非你的地形有更大的区域概念,对一个区域使用一张Alpha图,这可能 会解决Alpha图被过度拉伸的问题。不然,你只使用全局唯一的一张Alpha图的话,将很难收到理想的效果。 
由 定点的Diffuse提供Alpha有一个好处,那就是D3D会做插值运算,这样就不会让某一点的Alpha扩展成斑块,因为它扩展出来的新值会被D3D 插值运算的。那么要实现Torque那样的最多支持3张纹理混合,同一个地形就需要2份Diffuse。可以考虑使用多流的技术,让Diffuse数据作 为一个单独的流。

 


原文

地形多层纹理混合加阴影渲染方法

地 形由于十分庞大,需要大量顶点,所以往往占用很多内存空间,那么就应该在地形贴图上想办法节约空间。很多游戏的地形,虽然看上去不同地点的纹理好像互不相 同,地表纹理十分丰富,但其实真正用的贴图是很少的,之所以还能产生地表纹理变化多端的视觉效果,是因为使用了Alpha混合和阴影,从而产生一种错觉。 2.5D的网络游戏《奇迹》(MU)、全3D引擎Torque(多人联网FPS游戏《部落2》即使用此引擎)、全3D的滑雪游戏《极限滑 雪》(Supreme Snowboarding),都是这么做的。(参见文章末尾的截图) 

我对此设想了一个解决方案。需要用到顶点【重剑注:不明白顶点格式的看这里Vertex Formats的 Diffuse颜色、3个TextureStage。其实所谓的多层纹理,具体到一个三角形上的时候,很多游戏只用了2层纹理来做Alpha混合(因为也 许玩家的显卡只支持2层MultiTexture混合),然后再一层光照纹理。前2层纹理要做Alpha混合,Alpha来自哪里?若使用Alpha贴 图,就必然再增加一层TextureStage,而我选择使用顶点的Diffuse就可以省掉这一层TextureStage:由Diffuse的 Alpha通道提供Alpha值供前两层TextureStage混合使用。下面是在D3D中的设置: 

// TextureStage 0 
pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );  
pd3dDevice
->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 
pd3dDevice
->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_DISABLE ); 
// TextureStage 1 
pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_BLENDDIFFUSEALPHA ); 
pd3dDevice
->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 
pd3dDevice
->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT ); 
pd3dDevice
->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE ); 


在 TextureStage 0,什么也没做,把第一层纹理原封不动的留给TextureStage 1;在TextureStage 1,COLOROP 使用D3DTOP_BLENDDIFFUSEALPHA,也就是使用来自顶点Diffuse的Alpha值作为混合因子混合第2层纹理和 TextureStage 0原封不动留下来的第1层纹理。混合公式如下(来自DX帮助文档): 
Result = Arg1 * Alpha + Arg2 * (1 – Alpha) 
Arg1是来自第2层纹理的颜色,Arg2是来自TextureStage 0原封不动留下来的第1层纹理的颜色。并且,这里的Alpha值是D3D插值运算后得到的,因此混合效果很平滑。 
你不相信2层纹理混合就能得到丰富的地表视觉效果吗?你应该相信,因为虽然Arg1和Arg2总是恒定不变的来自2个贴图,因此对三角形上的同一点来说,这两个是常量,但Alpha值却是个变量,Alpha值取不同的值就会产生不同的Result。 
假 设与Arg1相对应的贴图是一个草地的贴图,与Arg2相对应的贴图是一个沙地的贴图,在一片地形区域内,仅使用这两张贴图,但是由于各顶点的 Diffuse的Alpha值不同,你会看到最终渲染出来的这片地形区域,草地的分布并不均匀,有些地方草的样子多一些,有些地方沙的样子多一些。这种效 果看上去已经很自然了。 
当然并不限制你在一个地形场 景上使用更多的地表纹理,但是对一个三角形来说,2张纹理已经足够,其它纹理请用在其它三角形上吧,这可以产生更丰富的地貌。比如你想让另一片地形区域是 雪地和岩石的风格,那么在那片区域上就使用1张雪地贴图、1张岩石贴图。现在这个关卡里面共有4张贴图了,但同一个三角形仍然只使用2张贴图(这是硬件的 限制啊!!)。 


现在来说阴影。前面已经使用了顶点Diffuse的Alpha通道,现在他的RGB三个分量也要派上用场了。在D3D中设置如下: 

// TextureStage 2 
pd3dDevice->SetTextureStageState( 2, D3DTSS_COLOROP,   D3DTOP_MODULATE ); 
pd3dDevice
->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_DIFFUSE ); 
pd3dDevice
->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT ); 
pd3dDevice
->SetTextureStageState( 2, D3DTSS_ALPHAOP,   D3DTOP_DISABLE ); 


这是第3个TextureStage,COLOROP为相乘,Arg1设为Diffuse,是来自经过D3D插值运算的顶点Diffuse颜色,Arg2是Current就是TextureStage 1产生的结果。混合公式如下: 
Result = Arg1 * Arg2 

颜色虽是RGB三个字节(或者其它颜色格式),但D3D会将它们拆成单独的3个RGB通道值(0-1之间的浮点数)。相乘运算可以贴上彩色的光影效果。 
有 一个问题是,人们当初选择使用光照图就是为了能够提高光影的精确度,因为顶点光照只作用于顶点,虽然渲染API会在顶点之间做插值,但这也只在顶点很密集 的时候才会有很精确的光照,如果定点很稀疏甚至不能产生正确的光照效果。这里使用顶点Diffuse,会不会也得不到令人满意的光照效果呢? 
我 想过使用一整张光照图铺在整个地形上,但我忽然想起这与基于heightmap的地形生成技术面临一个同样的问题,精度。使用heightmap,为了减 小游戏体积,这个heightmap不可能很大,不可能每一个高度点只对应一个顶点,而是多个顶点被映射到同一个高度点上去了。一整张光照图也面临同样的 问题,也许多个顶点使用光照图上的同一个像素呢?如果两个顶点用的是光照图上的同一个像素,那顶点之间的空间也肯定是用这个像素点了,这样是不会提高精度 的。把光照图的尺寸增大?那是选择512*512呢,还是1024*1024? 
而 实际上,LOD地形的顶点总是很密集的。至少在靠近照相机的区域是这样的,往往比室内建筑的顶点还要密集。虽然还没密集到可以让D3D的聚光灯产生满意效 果的地步,但已足够使用顶点光照模拟地形因高低起伏而产生的阴影效果。何况业界普遍认为地形渲染的精度是低于室内渲染一个档次的(新游戏FarCry是个 例外)。 
更妙的是,顶点光照信息可以存储在高度图里,我们把高度点从一个unsigned char改为一个多字节的数据结构: 

struct HeightPoint 

  unsigned 
char height;  // 用于计算定点的Y坐标值,用于渲染地形高度起伏 
  unsigned char alpha;  // 定点diffuse的alpha,用于多层纹理混合 
  unsigned char red;  // 顶点diffuse的red,用于光照 
  unsigned char green;;  // 顶点diffuse的green,用于光照 
  unsigned char blue;  // 顶点diffuse的blue,用于光照 
}; 


这样高度图信息不仅包含高度信息,也包含多层纹理混合信息、光照信息。当动态创建地形的时候,在读取高度数据的同时也能得到与该顶点对应的diffuse值,非常方便。 
下面是用DX自带的MFCTex程序验证理论。3个TextureStage正是使用上面所说的设置方法。 

  
图1 


env2.bmp是一个砖墙的贴图,caust11.tga是一个水面波纹的贴图,env3.bmp没有被使用。Diffuse颜色是0x77ff0000,是纯红色,但alpha值为119。 
Stage0 把env2.bmp的图像数据原封不动的保留给Stage1。Stage1的COLOROP使用D3DTOP_BLENDDIFFUSEALPHA的方 法,以diffuse的alpha值(119 => 0.53)为混合因子混合caust11.tga和env2.bmp,公式 是:ResultOfStage1 = caust11.tga * 0.53 + env2.bmp * (1 – 0.53),得到的视觉结果是我们 既能看到砖墙又能看到水面波纹,他们被半透明的混合起来了。Stage2使用相乘,把diffuse的RGB三分量与 ResultOfStage1(Current就是ResultOfStage1)上对应点的RGB三分量乘起来,由于diffuse的RGB是 ff0000纯红色,完全没有green和blue分量,所以这次得到的视觉结果是整个墙壁偏红色。这样我们就产生了墙壁上被投上了水面折射的波纹,而整 个墙壁又被一片红光照亮的效果。 
下面来看看Torque引擎、《极限滑雪》的地形贴图渲染效果。 
首 先是Torque。Torque引擎的Demo所使用的纹理文件都没有打包,所以我很容易找到这些文理,用Photoshop给它们画上标记,再到游戏中 看他们是如何被混合、以及怎样贴到地面上去。这个Demo只用了3张地形纹理:1个grass.jpg,1个sand.jpg,1个 patchy.jpg。每张纹理我先用红色粗线条勾出它的4条边,再在左上角和右下角用写上它的文件名,我还给它们写上中文译名,grass.jpg是 “草”,sand.jpg是“沙”,patchy.jpg是“稀疏的草地”。来看截图: 
  
图2 

每 张地形纹理都是256*256的,从空中这个角度看,每张纹理都被映射到地形上一个相当大的方形区域,所以镜头贴近地面的时候,会变得很模糊,但 Torque会在这时贴上细节纹理(参见后面的截图),一个叫detail1.png的图片文件(还是只有1张,但效果很好),这是另一个技术了,在此不 表。图中,凡是黄色区域都来自sand.jpg。我们看那个左边有“稀疏的草地”的方形区域,这个区域用的是patchy.jpg和sand.jpg两张 纹理混合的,“地”字就刚好位于混合边界,所以模糊了。混合因子不是顶点diffuse的alpha吗?所以这个区域里面,要有更多的顶点,没问 题,Torque使用四叉树来创建地形,这个方形区域里面被细分为更小的方形区域,而且不是平衡的(LOD技术),所以有足够的顶点,同时看上去混合边界 是扭曲的。这个Demo可以切换为线框渲染模式,我好好观察了那些顶点的分布,确实跟Alpha混合的分布相一致。 

再来一张截图: 
  
图3 

从这个截图里面看出,阴影的存在确实起到了丰富视觉效果的作用。看左边那个方形区域,几乎全是沙地,但有阴影的存在,效果就好多了。你可能觉得它的阴影很细致,但别忘了这是一个全3D的FPS游戏,我是在很高的空中俯拍的。 
另 一个游戏《极限滑雪》,这是欧洲某个国家制作的商业游戏。虽是商业游戏,它的纹理文件也没有打包,赤裸裸的jpg、tga,所以我把这些纹理都替换为只有 一个颜色的图片,再勾勒出图片的四条边,打上文件名。由于是个滑雪游戏,所以场景中最常见的颜色就是白色了,所以大范围内只使用同一张纹理用得理直气壮。 
  
图4 

当然要加上阴影,不然真的会灼伤玩家的眼睛呢。有了阴影,效果好一些了。再加上彩色的光影,还蛮令人着迷的,看图: 
  
图5 

游戏中的山头和公路都需要别的纹理来混合。 
  
图6 

看图6的阴影,尤其是左边的,有较为明显的梯级,右边的呈矩形,应该是那棵树的影子,但却很涣散,很不对劲,跟顶点光照的效果十分相像,可能是场景编辑器把计算出来的影子信息存储到顶点的diffuse中去了。 
这个游戏的雪地纹理只有2张,另一张出现的频率较小。

附:Torque的细节纹理 
  

阅读(473) | 评论(0) | 转发(0) |
0

上一篇:OpenGL入门学习(五)

下一篇:第01课

给主人留下些什么吧!~~