实时阴影绘制技术研究

C++博客 首页 新随笔 联系 聚合 管理
  48 Posts :: 20 Stories :: 57 Comments :: 0 Trackbacks
这两天看纹理映射看的头大,我认为这是《Direct3D游戏入门教程》这本书写的不好的一个地方,解释的不清楚,或者说认为读者水平太高。看了第一遍没有什么感觉,又看了一遍,还是感觉不太清楚的样子。反正先写下来吧,将来有了更多的理解再进行补充。
-------------------
1,要点
首先我觉得D3D纹理映射的选项太多,给人一种眼花缭乱的感觉,而且刚看的时候并不容易分辩不同选项之间的差别,不知道如何使用这些选项,或者说不知道这些选项对应以前经验中的哪些效果。这些选现包括:
  • Dark mapping
  • Animated Dark mapping
  • Blending with Material Diffuse Color
  • Darkmap with Material Diffuse Color
  • Glow mapping
  • Detail mapping
  • Modulate mapping
  • Blending with Frame Buffer
  • Trillinear Filtering
  • Clamp Addressing Mode
  • Mirror Once
  • Border Color.
使用的主要API就是:
  • HRESULT SetSampleState(
                 DWORD Sampler,//纹理阶段号
                 D3DSAMPLERSTATETYPE type,//采样器状态类型
                 DWORD value)//采样器状态值
,用于指定纹理的过滤方式,贴夹方式(tiling/clamping)和MIPLOD方式等等。
  • HRESULT SetTextureStageState(
                     DWORD Stage,//纹理阶段号
                     D3DSAMPLERSTATETYPE type,//采样器状态类型
                     DWORD value)//采样器状态值

用于为使用固定功能的多纹理单元提供附加的操作功能。包括:不同的纹理坐标,颜色操作,alpha操作,凹凸映射/环境映射。
--------------------------------------------
2,纹理坐标
对于一个顶点,最多允许使用八组纹理坐标(D3DFVF_TEX1到D3DFVF_TEX8),可以利用D3DTSS_TEXCOORDINDEX来选择某组特定的纹理坐标:

m_pd3dDevice->SetTexture(0,m_pWallTexture);
m_pd3dDevice->SetTextureStateState(0,D3DTSS_TEXCOORDINDEX,0);
m_pd3dDevice->SetTexture(1,m_pWallTexture);
m_pd3dDevice->SetTextureStateState(1,D3DTSS_TEXCOORDINDEX,1);
上边第一张纹理使用了顶点的第一组纹理坐标,第二张纹理使用了顶点的第二组纹理坐标。当然也可以让第二张纹理也使用第一组纹理坐标,这要是情况而定。

--------------------------------------------
3,四种纹理寻址模式

纹理寻址方式实在是我又查询了其他一些网站之后才知道的事情,因为以前用openGL的时候对纹理映射方式一点都没有接触过,因此这部分看起来就的确显得有点难度了。

一般来说,分配的U、V纹理坐标值都在0.0到1.0范围内(包括它们)。但是,如果我们分配了超出这个范围的纹理坐标,可能会得到一些特别的纹理效果。通过设置纹理寻址模式,我们就可以在纹理坐标超出范围时进行控制,纹理寻址模式就是用来干这个的。

 包装(Wrapping)纹理寻址模式

  “Wrapping”纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_WRAP成员来确定,它使Direct3D在每一个整数结点(integer junction)对纹理进行重复。假设我们要创建一个正方形图元,并将纹理坐标声明为(0.0,0.0)、(0.0,3.0)、(3.0,3.0)和(3.0,0.0)。这时,如果我们设置了纹理寻址模式,就可以使纹理在U、V方向都重复三次。,如下图所示:

pic76.gif (64086 bytes)

  这种纹理寻址模式的效果与“镜像”模式比较相似,但在本质上是不同的。

镜像纹理寻址模式

  “镜像”纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_MIRROR成员来确定,它使Direct3D在每个整数边界处(integer boundary)对纹理进行镜像处理。想在我们创建一个正方形图元,为坐标为(0.0,0.0)、(0.0,3.0)、(3.0,3.0)和(3.0,0.0)。我们设置镜像纹理寻址模式,纹理在U、V方向都重复了三次,并且每一行、每一列都与相邻的行和列成镜像关系。如下图所示:

pic77.gif (15582 bytes)

钳位纹理寻址模式

  “钳位”纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_CLAMP成员确定,它使Direct3D将纹理坐标钳制在[0.0, 1.0]范围内。也就是说,它只使用一次纹理,然后将边缘像素的颜色抹去。我们创建一个正方形图元,纹理地址分配为(0.0,0.0)、(0.0,3.0)、(3.0,3.0)和(3.0,0.0)。这时,设置钳位纹理寻址模式,纹理将只使用一次,并且最顶一行和最后一列上的像素颜色会一直延伸到图元的最顶端和最右段,如下图所示:

pic78.gif (5443 bytes)

 

边界颜色纹理寻址模式

  “边缘颜色”纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_BORDER成员确定,它使Direct3D可以在纹理坐标超过范围的地方使用一个任意的颜色,也就是边界颜色。

  下图中展示了一个使用了纹理的图元,它使用了红色的边界色:

pic79.gif (5657 bytes)

一次镜像寻址模式

这个模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_MIRRORONCE成员确定,是从DirectX8才引入的新的纹理寻址模式,类似于镜像模式于夹持模式的组合。纹理再-1.0到1.0之间做镜像,而在该范围之外做夹持。

-----------------------------------------
4,纹理包装(Wrapping)
简单来说,纹理Wrapping就是要改变Direct3D光栅使用纹理坐标对有纹理的多边形进行光栅操作的基本方式。我们对一个多边形进行光栅操作时,系统在每一个多边形顶点的纹理坐标之间进行内插运算,这样来决定在多边形的每个像素上所使用的纹理像素。通常,系统将纹理看作一个二维平面,在这个平面内A、B两点间的连线上进行内插。如果点A的U、V坐标为(0.8, 0.3),点B为(0.1,.9),那么进行内插的连线就如下图所示:

pic85.gif (2608 bytes)
  注意,上图中A、B两点的最短连线穿过了纹理的中间部分。U、V纹理Wrapping的使用会影响Direct3D在U、V方向上对纹理坐标间最短连线的选取。现在我们假定0.0与1.0重合,那么通过定义,纹理Wrapping就会导致光栅在纹理坐标设置之间来选择最短距离。我们可以认为一个方向上的纹理Wrapping就是让系统认为将一个纹理包裹在了一个圆筒上,就象下图中那样:

pic86.gif (3890 bytes)

   上图中我们在U方向上进行了Wrapping,它影响了系统对纹理坐标进行的插值操作。我们使用同样的两个点A和B,可以看到,它们之间最短的连线不再通过纹理的中间部分;它现在穿越了0.0和1.0所在的交界线。沿V方向的Wrapping与它相似,只不过纹理所包裹的圆筒横躺在地上。U、V方向上同时进行Wrapping比较复杂,这时我们可以将纹理想象成一个园环面或者是面包圈的形状。
   再具体说明一下,就是说如果按照U方向来包装就取U方向的最小距离线段,在该段上做插值;如果如果按照V方向来包装就取V方向的最小距离线段,然后做插值。那么设置了按U&V方式可以将纹理想象成一个环形。
   需要注意的是,在启用纹理包装之后将使[0,1]之外的纹理坐标成为无效值,所以纹理包装不能于各种纹理寻址模式共用。
----------------------------------------------

5,纹理过滤和抗锯齿(反走样Anti-Aliasing)

纹理过滤和反走样解决的是同一类问题,就是屏幕锯齿的问题,不同的是文理过滤解决的是纹理映射过程中的锯齿,而反走样解决的是几何体边沿的锯齿。

  • MipMap:是由一系列纹理组成的分辨率分级的特殊纹理形式。

一个mipmap就是一系列的纹理,每一幅纹理都与前一幅是相同的图样,但是分辨率都要比前一幅有所降低。mipmap中的每一幅或者每一级图象的高和宽都比前一级小二分之一。Mipmap并不一定必须是正方形。
高分辨率的mipmap图象用于接近观察者的物体。当物体逐渐远离观察者时,使用低分辨率的图象。Mipmap可以提高场景渲染的质量,但是它的内存消耗却很大。
  Direct3D将mipmap描绘成一系列相互联系的表面。高分辨率的纹理位于开始处,并与下一级纹理相互联系。以此类推,纹理相互联系,逐渐排列到分辨率最小的一级。
 下面这套插图显示了这样的一个例子。这套纹理是一个三维场景中一个集装箱的标签。当我们创建了一个mipmap时,分辨率最高的一幅纹理就是这一套纹理的第一个。这套mipmap中的每一个纹理宽高都是前一个纹理宽高的二分之一。这样,最大分辨率的纹理是256x256,接下来的纹理就是128x128,最后一个纹理就是64x64。
我们有一个能看到这个标签的最大距离。如果观察者从远处向标签走近,那么场景中首先会显示最小的一幅纹理,它的大小是64x64的。

pic84.gif (19673 bytes)pic83.gif (8944 bytes)pic82.gif (3304 bytes)

当观察者走进标签时,我们就使用更高分辨率一幅纹理;当观察者走到允许的最近距离时,我们使用分辨率最高的那幅纹理.

  这是方法能够模拟纹理的透视效果并能够减少处理时的计算量。与将一幅纹理用于不同的分辨率相比,这种方法更加快速。
  Direct3D能够访问mipmap中与我们想要输出的分辨率最接近的那个纹理设置,并将像素映射到它的纹理像素空间中。如果最终图象的分辨率在mipmap纹理的分辨率的中间,那么Direct3D会对两幅纹理中的纹理像素进行检查,并将它们的颜色值进行融合。
  提供给CreateTexture()正确的参数就可以自动创建MipMap纹理。
  • 纹理过滤方法:最近点采样,线性纹理过滤,各项异性过滤,混合过滤

   过滤是指通过给定的UV坐标从纹理贴图中获取图素的一种方法。一张64*64的纹理映射到400*400象素的多边形就会出现因放大而造成的锯齿,这称为放大问题;一张64*64的纹理映射到10*10象素的多边形的时候,一个屏幕象素对应多个纹理图素,就会因交替占有象素而出现抖动。那么为了解决这个问题,就要采用纹理过滤方法。在默认的显卡设置中,纹理过滤的方法是由驱动程序控制的。

   最近点采样是最简单的过滤方法,顾名思义,就是采用离象素最近的图素作为颜色。将纹理坐标对齐到最接近的整数,再将那个位于整数坐标的纹理图素作为最终的颜色。这种方法在处理图素边界的时候会发生一些错误。

   线性纹理过滤在Direct3D中采用的是双线性纹理过滤,它计算相对于采样点距离最近的4个图素的平均值。采用如下形式进行设置:
m_pd3Device->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
m_pd3Device->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);

   三线性过滤:选择两张最接近的mipmap,将它们双线性过滤为两张理想大小的mipmap,然后根据理想的mip级组合这两张过滤厚的mipmap中的对应象素。采用如下形式进行设置:
m_pd3Device->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_LINEAR);
m_pd3Device->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_LINEAR);
m_pd3Device->SetSamplerState(0,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

各向异性过滤:双线性过滤和三线性过滤的缺点是他们都使用正方形采样区域来采样图素,如果纹理是侧着朝向视点的化则会发生一种称之为“各向异性”的失真效果。那么各向异性过滤则会把因为透视投影拉伸的几何体大小映射回纹理空间中,使得纹理在投射时候被伸张,因此来获得更佳的深度细节和精确显示。使用各向异性过滤的代码如下:
m_pd3Device->SetSamplerState(0,D3DSAMP_MINFILTER,D3DTEXF_ANISOTROPIC);
m_pd3Device->SetSamplerState(0,D3DSAMP_MAGFILTER,D3DTEXF_ANISOTROPIC);
m_pd3Device->SetSamplerState(0,D3DSAMP_MAXD3DTEXF_ANISOTROOPY,16);

  • 反走样:现在反走样已经是DirectX中的一项默认特征。

全屏反走样(FSAA):采用全屏多采样对每个象素进行对此采样,进行混合后进行输出,通过这种方式来调整图像中每条斜线周围的亮度来隐藏骑上的锯齿效果,它沿着这些边沿产生局部模糊的效果。一种更为高级的多采样成为“可屏蔽多采样(maskable multisampling)”。

  • alpha混合

alpha混合不知道怎么搞到这一章来介绍,可能是为了利用这一个没有程序的篇章把该介绍的基本知识介绍完吧。我也姑且写到这里。

alpha混合的原理很简单,就是利用混合因子把要绘制到帧缓存中的颜色和当前帧缓存中的颜色进行一个叠加,因此可以实现多次渲染,从而实现许多特效。对应的代码像下面这种形式:
m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ZERO);
m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_SRCCOLOR);

原载:http://blog.sina.com.cn/u/40d00f17010001qe

posted on 2006-01-08 20:12 苦行僧 阅读(1497) 评论(0)  编辑 收藏 引用 所属分类: directX

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理