DirectX入门知识点(3)

Direct3D使用一个纹理坐标系统,它是由用水平方向的u轴和竖直方向v轴构成。由u,v坐标决定纹理上的元素,它被叫做texel。注意v轴是向下的。


  在Direct3D中,你能够设置八个纹理(正确的来说是八层纹理),它们能够组合起来创建更多细节的图象。这又被叫做多重纹理。
即IDevice3DDevice9::SetTexture的第一个参数可以指定被设置的纹理(层)。
为了销毁一个纹理,我们可以设置第二个参数为NULL。


  纹理被映射到屏幕中的三角形上。通常纹理三角形和屏幕三角形是不一样大的。当纹理三角形比屏幕三角形小时,纹理三角形会被适当放大。当纹理三角形比屏幕三角形大时,纹理三角形会被适当缩小。这两种情况,变形都将会出现。过滤(Filtering)是一种Direct3D用它来帮助这些变形变的平滑的技术。
Direct3D提供了三种不同的过滤器;每种都提供了一个不同的品质级别。越好的品质越慢,因此你必须在品质与速度之间取得一个平衡。纹理过滤器是用IDirect3DDevice9::SetSamplerState方法来设置的:
  D3DSAMP_MAGFILTER
  D3DSAMP_MINFILTER
  D3DSAMP_MIPFILTER
(一般来说使用默认数值就好)


  我们规定纹理坐标必须指定在[0,1]之间。从技术上来说这是不正确的;他们能够超出这个范围。纹理坐标也可以在[0,1]的范围之外,它通过Direct3D的寻址模式来定义。这里有四种寻址模式:环绕纹理寻址模式、边框颜色纹理寻址模式、截取纹理寻址模式、镜像纹理寻址模式
// set wrap address mode
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
// set border color address mode
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff);
// set clamp address mode
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
// set mirror address mode
Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR);
Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR);


  当使用混合(通常是指Alpha透明度混合)时,下面的规则将被遵循:
规则:首先不使用混合绘制物体。然后根据物体离摄象机的距离使用混合对物体拣选;这是非常有效的处理,假如物体是在视图坐标中,那么你能够利用z分量简单地拣选。最后使用从后到前的顺序混合绘制物体。
混合默认是被关闭的;你能够通过设置D3DRS_ALPHABLENDENABLE渲染状态为true来开启它:
Device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
源(通常被计算的像素,它是利用在后缓存中的像素来被混合,D3DRS_SRCBLEND)和目的混合要素(在后缓存中的像素,D3DRS_DESTBLEND)的默认值分别是D3DBLEND_SRCALPHA和D3DBLEND_INVSRCALPHA。


  在D3D中,默认情况下,设置一个有Alpha通道的纹理的时候,Alpha值是从在Alpha通道中获得的。假如没有Alpha通道,那么Alpha值是通过顶点颜色获得。
然而,你能够通过下面的渲染状态来指定使用哪一个资源:
// compute alpha from diffuse colors during shading
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
// take alpha from alpha channel
Device->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);


  Direct3D最多支持8层纹理,也就是说,在一个三维物体的表面可以同时拥有1~8张不同的纹理贴图。Direct3D能够在一个渲染过程中把这些纹理颜色依次混合,渲染到同一个物体的表面。每一个纹理层对应0~7的索引序号,多层纹理映射能够模拟更为真实的三维世界。例如,要显示具有周围景物倒影的光滑大理石地板,可以把大理石地板贴图设置为纹理层0,把具有周围景物倒影的贴图设置为纹理层1,然后通过设置Direct3D多层纹理混合操作,把纹理层0和纹理层1相混合,这时绘制出的三维物体就同时具有大理石地板和景物倒影的纹理颜色。利用Direct3D多达8层的纹理混合,可以在图形显示系统中显示丰富多彩的图像。
  这8层纹理是逐层混合然后输出的,也就是说,最多需要8个阶段完成纹理映射,而且每一阶段都是独立进行的,针对每一阶段都需要设置相应的颜色和alpha混合方法。所以一个纹理层就相当于一个纹理阶段(Texture Stage),多层纹理映射有时也称为多阶段纹理混合。其中,是否应用纹理层0~7,即是否进行0~7纹理阶段的操作,可由应用程序指定,但它们的选择必须是顺序的。也就是说,在没有使用第n层的情况下,第n+1层不能使用。
  默认情况下,第一个纹理阶段操作(阶段0)的默认操作是D3DTOP_MODULATE,其他纹理操作阶段的默认操作是D3DTOP_DISABLE。即默认情况下Direct3D使用单层纹理映射绘制图形,除纹理层0外,纹理层1~7都是禁用的。
  为了将多层纹理映射到物体表面,需要为每层纹理指定使用的纹理坐标,各层纹理可以使用相同的纹理坐标,也可以使用不同的纹理坐标。纹理坐标包含在顶点数据中,如果需要使用不同的纹理坐标,那么在顶点数据中就需要包括多组纹理坐标,然后通过索引为每层纹理指定使用哪组纹理坐标:
如:
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);
truct sCustomVertex
{
    float x, y, z;
    DWORD color;
    float u0, v0;
    float u1, v1;
};
#define D3DFVF_CUSTOM_VERTEX    (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX2)
// 只需要声明D3DFVF_TEX2就可以代表FVF中有两组纹理坐标了
sCustomVertex vertices[] =     
{
    { -3.0f, -3.0f,  0.0f,  0xffffffff, 0.0f, 1.0f, 0.0f, 1.0f},
    { -3.0f,  3.0f,  0.0f,  0xffffffff, 0.0f, 0.0f, 0.0f, 0.0f},
    {  3.0f, -3.0f,  0.0f,  0xffffffff, 1.0f, 1.0f, 1.0f, 1.0f},
    {  3.0f,  3.0f,  0.0f,  0xffffffff, 1.0f, 0.0f, 1.0f, 0.0f}
};
这里指定的两组纹理坐标值完全相同,你可以改变其中任何一组坐标,使两层纹理使用不同的纹理坐标。
设置纹理层混合方法的代码如下:
// set color blend operation and texture coordinate index for texture stage 0
pd3dDevice->SetTexture(0, g_texture_0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0); // 会自动查找到u0和v0
pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
// set color blend operation and texture coordinate index for texture stage 1
pd3dDevice->SetTexture(1, g_texture_1);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_ADD);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1); // 会自动查找到u1和v1
pd3dDevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
pd3dDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
这里先将物体的漫反射颜色和纹理层0的纹理颜色相乘,得到的结果再与纹理层1的纹理颜色相加后输出。


  纹理映射本质上就是从纹理中获取颜色值,然后应用到物体的表面,多层纹理映射本质上就是混合多层纹理的颜色,然后应用到物体表面。为了处理上的方便,Direct3D将颜色的RGB通道和alpha通道分别进行处理,(这恐怕就是有D3DTSS_COLOROP和D3DTSS_ALPHAOP区别的原因吧)具体的操作方法通过纹理阶段状态进行设置。
设置纹理颜色混合操作的代码大致如下:
// i表示纹理阶段序号
pd3dDevice->SetTextureStageState(i, D3DTSS_COLORARG1, arg1);
pd3dDevice->SetTextureStageState(i, D3DTSS_COLORARG2, arg2);
pd3dDevice->SetTextureStageState(i, D3DTSS_COLOROP, op);
一般的,用D3DTSS_COLORARG1指定当前纹理层的颜色,用D3DTSS_COLORARG2指定已经过颜色混合处理后的前面所有纹理层的颜色,用D3DTSS_COLOROP指定混合方式。Direct3D使用下面的方式进行纹理混合:
Colorstage = D3DTSS_COLOROP(D3DTSS_COLORARG1, D3DTSS_COLORARG2)


  Direct3D的坐标变换和光照流水线中,光照效果是基于所谓的"逐顶点(per-vertex)"方式计算的,也就是说,参与实际数计算的是三角形的每个顶点,而不是针对每个像素进行。有时这会造成一些较为明显的视觉错误,例如,有一个很大的三角形,其表面近处有一个光源,当光源靠近该三角形的一个顶点时,就会看到这个三角形的受光效果;当光源向三角形的重心靠近时,三角形的受光效果便会逐渐消失。最坏的情况是,当光源位于三角形的中央时,整个三角形只受非常少的光照,而在三角形的中央会有一个亮点。由此可见,如果顶点未受光照,则无法计算出正确的三角形面的颜色。为了解决这个问题,可以采用基于像素的光照计算,但是基于像素的光照计算其计算量比较大,通常采用纹理贴图的方式模拟基于逐像素光照效果,其中纹理贴图的内容正式所期望的类型光源照射在一张漆黑表面上的结果。
  通过纹理映射来模拟逐像素光照效果,通常是将第一层纹理设置为物体原来的表面纹理,将第二层纹理设置为光照纹理,然后将两张纹理的颜色相乘,所以有时将两张纹理的颜色相乘称为光照映射(light mapping)。由于这种技术经常被用于使一张纹理变暗,有时也称为黑暗映射(dark mapping)。示例代码如下:
pd3dDevice->SetTexture(0, g_base_texture);
pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
pd3dDevice->SetTexture(1, g_dark_texture);
pd3dDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 0);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_CURRENT);
pd3dDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_MODULATE);


  Direct3D在渲染一个场景时,它可以结合几种来源的颜色信息:顶点、当前材质、纹理贴图、先前写入渲染目标的颜色信息,然后将其中的一些颜色混合起来。同时也可以使用Alpha来指定Direct3D该以怎样的权重混合这些颜色,Alpha信息可以存储在顶点中、材质中、纹理贴图中。Alpha值为0表示完全透明,Alpha值为1表示不透明,其余0~1之间的值表示不同程度的透明。
如果要从一张纹理中获取Alpha值,应将D3DTA_TEXTURE作为Alpha参数。
如果要使用来自顶点中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_COLOR1(这也是默认状态)。
如果要使用来自材质中的Alpha值,应将D3DTA_DIFFUSE作为Alpha参数,并确保渲染状态D3DRS_DIFFUSEMATERIALSOURCE被设置为D3DMCS_MATERIAL。
如果未用SetRenderState()设置D3DRS_DIFFUSEMATERIALSOURCE参数,则从默认来源(即顶点)获取漫反射颜色。


  在Direct3D程序中,不仅可以在模型载入阶段或渲染阶段指定物体的纹理坐标,还可以通过Direct3D渲染引擎自动生成纹理坐标,用于诸如环境映射等特殊的视觉效果。与手动设置纹理坐标相比,纹理坐标自动生成在Direct3D坐标 变换和光照流水线中完成,执行速度更快。
Direct3D系统可以使用经过变换的摄像机空间顶点位置坐标、法线信息来生成纹理坐标。如果使用纹理坐标自动生成,那么在顶点中就可以不用包含纹理坐标数据,从而可以降低图形渲染时的数据传输量。纹理坐标自动生成主要用于产生一些特殊效果,在大多数情况下还是手工为每个顶点指定纹理坐标。
通过调用SetTextureStageState()并将第二个参数设置为D3DTSS_TEXCOORDINDEX来控制Direct3D系统如何自动生成纹理坐标。


  Direct3D提供了对生成的纹理坐标进行坐标变换的功能,与顶点坐标变换相类似,可以指定一个4x4的纹理坐标变换矩阵,把它与生成的纹理坐标相乘,然后将变换之后的纹理坐标输出至Direct3D渲染流水线。使用纹理坐标变换可以对纹理坐标进行诸如平移、旋转和缩放等三维变换。纹理坐标变换对于生成一些特殊效果是非常有用的,它不用直接修改顶点的纹理坐标。例如可以通过一个简单的平移矩阵对纹理坐标进行变换,从而使物体表面上的纹理不断变换位置,产生动画效果。纹理坐标自动生成在三维图形程序中最广泛的应用是环境映射。
  可通过函数IDirect3DDevice9::SetTransform()来设置4x4的纹理坐标变换矩阵,它以D3DTS_TEXTURE0~ D3DTS_TEXTURE7作为第一个参数,表示设置纹理层0~7的纹理矩阵。


  三维场景中的物体不仅受光照影响,而且受周围环境的影响,可以映射出周围环境的图像,环境映射就是模拟物体光滑表面映射周围环境的一种技术。实际上,就是将一幅包含物体周围环境场景的纹理贴到物体表面上,这样就可以在一定程度上模拟出物体对周围环境的映射,而无须使用像光线跟踪算法这种复杂的计算技术。
posted @ 2009-09-14 21:25  芈希有  阅读(1240)  评论(0编辑  收藏  举报