纹理


纹理是增强计算机生成的三维图像的真实感的有力工具。Microsoft® Direct3D®支持广泛的纹理特性,并使开发人员可以很方便地使用高级纹理技术。

本节讲述如何使用纹理。

以下主题将更详细地介绍另外的纹理功能。

要提高性能,可以考虑使用动态纹理。动态纹理在每一帧都可以被锁定,写入及解锁。更多信息请参阅使用动态纹理


纹理的基本概念


早期计算机生成的三维图像看起来往往像是发亮的塑料,虽然这在当时也是比较先进的,但是它们缺乏各种纹路——如磨损、裂痕、指纹和污渍等,而这些纹路会增加三维物体的真实感。近年来,纹理已经在开发人员中得到普及并作为增强计算机生成的三维图像的真实感的工具。

词语“纹理”在日常使用中表示物体的光滑度或粗糙度,但是在计算机图形学中,纹理指的是一张表示物体表面细节的位图。

因为Direct3D中所有纹理都是位图,所以可以把任何位图贴到Direct3D图元的表面。例如,应用程序可以创建物体并使它们的表面看起来有木纹的样式。可以把草、泥土和岩石等纹理贴在构成山的图元的表面,这样就能得到看起来很真实的山坡。应用程序也可以用纹理创建其它的效果,如:路边的路标,悬崖边的岩层,或是地面上的大理石。

另外,Direct3D支持更高级的纹理技术,如纹理混合(包含或不含透明度)和光照贴图。更多信息请参阅纹理混合用纹理实现光照贴图

如果应用程序创建一个HAL设备或软件设备,那么可以使用8、16、24或是32位纹理。

以下主题包含了更多的信息。


纹理寻址模式


Microsoft® Direct3D®应用程序可以把纹理坐标值赋给任何图元的任何顶点。更多细节,请参阅纹理坐标。一般来说,应用程序赋给顶点的u、v纹理坐标值在0.0到1.0范围内,闭区间。但是,通过把纹理坐标值赋为此范围外的值,应用程序可以创建某些特殊纹理效果。

通过设置纹理寻址模式,应用程序可以控制当纹理坐标位于范围[0.0, 1.0]外时希望Direct3D执行何种操作。例如,应用程序可以设置寻址模式,使纹理平铺于图元表面。下面的主题包含了更多的细节。

Direct3D使应用程序可以进行纹理环绕,很重要的一点是要注意把纹理寻址模式设为D3DTADDRESS_WRAP与进行纹理环绕并不相同。把纹理寻址模式设为D3DTADDRESS_WRAP会使源纹理的多个复本被贴到当前图元的表面,而启用纹理环绕则会改变系统对贴有纹理的多边形进行光栅化的方式。更多细节,请参阅纹理环绕

启用纹理环绕实际上使位于[0.0, 1.0]范围之外的纹理坐标无效,在这种情况下,对无效的纹理坐标进行光栅化操作将导致未定义的结果。当启用纹理环绕时,不会使用纹理寻址模式,同时应用程序应该注意不要给出小于0.0或大于1.0的纹理坐标。

设置寻址模式

应用程序可以通过调用IDirect3DDevice9::SetSamplerState方法设置每个纹理层的纹理寻址模式,只需把纹理层的标识作为第一个参数,并把第二个参数设置为D3DSAMP_ADDRESSU,D3DSAMP_ADDRESSV或D3DSAMP_ADDRESSW,就可以分别改变u,v或w寻址模式。IDirect3DDevice9::SetSamplerState方法的第三个参数决定要设置的模式,这是一个D3DTEXTUREADDRESS枚举类型值。要取得某一纹理层当前的纹理寻址模式,只需调用IDirect3DDevice9::GetSamplerState方法,把第二个参数设置为D3DTEXTURESTAGESTATETYPE枚举类型值,并把第三个参数设置为用于保存返回的寻址模式的变量的地址。

设备限制

虽然系统允许纹理坐标位于闭区间0.0到1.0之外,但硬件限制通常会决定纹理坐标究竟可以超出该范围多少。当应用程序取得设备能力时,渲染设备通过D3DCAPS9结构的MaxTextureRepeat成员表明这个限制,这个成员的值描述了设备允许的纹理坐标的全部范围。例如,若这个值为128,则输入的纹理坐标必须保持在从-128.0到+128.0之间,若输入顶点的纹理坐标位于这个范围外,则纹理坐标是无效的。对自动生成的纹理坐标和由纹理坐标变换得到的纹理坐标也有同样的限制。

MaxTextureRepeat的解释同时还受D3DPTEXTURECAPS_TEXREPEATNOTSCALEDBYSIZE能力位的影响。若设置了这个能力位,则D3DCAPS9结构的MaxTextureRepeat成员的值正如前面描述的一样。但是,若没有设置该能力位,则纹理循环的限制还取决于纹理坐标寻址的那个纹理的大小。在这种情况下,MaxTextureRepeat必须除以当前mipmap中最大纹理的大小。例如,若纹理大小为32,而MaxTextureRepeat的值为512,则实际有效的纹理坐标的范围是512/32 = 16,因此传给该设备的纹理坐标必须在范围-16.0到+16.0之间。

以下主题包含了更多有关纹理寻址的信息:        


环绕纹理寻址模式


环绕纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_WRAP成员表示,它会使Microsoft® Direct3D®在纹理坐标的整数边界重复使用该纹理。例如,设想应用程序创建了一个方的图元并把纹理坐标指定为(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把纹理寻址模式设置为D3DTADDRESS_WRAP会使纹理在u和v方向都重复三次。

这种纹理寻址模式的效果与镜像纹理寻址模式有些相似,却又明显不同。更多信息,请参阅镜像纹理寻址模式


镜像纹理寻址模式


镜像纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_MIRROR成员表示,它会使Microsoft® Direct3D®在纹理坐标的整数边界先对纹理进行镜像然后再重复使用。例如,设想应用程序创建了一个方的图元并把纹理坐标指定为(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把纹理寻址模式设置为D3DTADDRESS_MIRROR会使纹理在u和v方向都重复三次,每一行和每一列的纹理都是相邻行和列的纹理的镜像。

这种纹理寻址模式的效果与环绕纹理寻址模式有些相似,却又明显不同。更多信息,请参阅环绕纹理寻址模式


截取纹理寻址模式


截取纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_CLAMP成员表示,它会使Microsoft® Direct3D®把纹理坐标截取到[0.0, 1.0]范围内,也就是说,这种模式只应用纹理一次,然后就重复使用纹理边缘处像素的颜色。例如,设想应用程序创建了一个方的图元并把纹理坐标指定为(0.0,0.0), (0.0,3.0), (3.0,3.0)和(3.0,0.0),把纹理寻址模式设置为D3DTADDRESS_CLAMP会使纹理只被应用一次,列的顶端和行的末端处像素的颜色被相应地延伸至图元的顶端和右边。

下图描绘了截取寻址模式。


边框颜色纹理寻址模式


边框颜色纹理寻址模式由D3DTEXTUREADDRESS枚举类型的D3DTADDRESS_BORDER成员表示,该寻址模式会使Microsoft® Direct3D®对于位于[0.0, 1.0]范围之外的纹理坐标使用一个被称为边框颜色的指定颜色。

下图描绘了边框颜色纹理寻址模式,这里应用程序指定红色为纹理的边框颜色。

应用程序可以通过调用IDirect3DDevice9::SetSamplerState方法设置边框颜色。在调用时要把第一个参数设为想要设置的纹理层的标识,把第二个参数设为D3DSAMP_BORDERCOLOR纹理层状态值,并把第三个参数设为以RGBA形式表示的新的边框颜色。


无效纹理区域


通过给纹理指定无效区域,应用程序可以对需要复制纹理的哪些子集进行优化,只有那些被标记为无效的区域才会被IDirect3DDevice9::UpdateTexture方法更新。当创建纹理时,整个纹理被标记为无效的。只有以下几种操作可以改变纹理的无效状态。

  • 给一个纹理添加一个无效区域。
  • 锁定纹理中的一些区域。此操作会把被锁定的区域添加到无效区域中,如果应用程序明确知道哪些是真正的无效区域,那么也可以关闭对无效区域的自动更新。
  • 将纹理作为目标表面进行更新的话会把整个纹理标记为无效的。
  • 对纹理调用IDirect3DDevice9::UpdateTexture方法会清除该纹理的所有无效区域。
  • 为了得到设备上下文(device context)而调用IDirect3DDevice9::GetDC

对于mipmap纹理而言,无效区域被设在最高一级的纹理上,为了最小化对mipmap纹理中每一级的纹理更新所需复制的字节数,IDirect3DDevice9::UpdateTexture方法可以扩展无效区域并沿mipmap链更新子纹理。注意子级中无效区域的纹理坐标被向上舍入,也就是说,它们的小数部分被向上取整到纹理中最近的像素。

因为每种类型的纹理具有不同类型的无效区域,所以每种类型的纹理都有相应的方法表示无效区域。二维纹理使用矩形,立体纹理使用立方体。

  • IDirect3DCubeTexture9::AddDirtyRect
  • IDirect3DTexture9::AddDirtyRect
  • IDirect3DVolumeTexture9::AddDirtyBox

把以上方法的pDirtyRectpDirtyBox参数设置为NULL会扩大无效区域并使之覆盖整个纹理。

每种锁定方法都有D3DLOCK_NO_DIRTY_UPDATE标志,使用这个标志可以防止对纹理无效区域的改变。更多信息,请参阅锁定资源

如果在锁定操作时可以得到已改变区域的完整集合,那么应用程序应该使用D3DLOCK_NO_DIRTY_UPDATE标志。注意,对纹理一个的子级的锁定或复制操作(也就是说,未对纹理的最高一级进行锁定或复制操作)不会更新该纹理的无效区域。当应用程序锁定了纹理的子级而没有锁定纹理的最高一级时,它同样有责任对无效区域进行更新。


纹理调色板


Microsoft DirectX® 9.0中的Microsoft® Direct3D®通过一组与IDirect3DDevice9对象相关联的256色调色板支持调色板纹理(paletted texture)。通过调用IDirect3DDevice9::SetCurrentTexturePalette方法可以设置当前调色板。当前调色板用于对所有已激活的纹理层中的所有调色板纹理进行颜色转换。IDirect3DDevice9::SetPaletteEntries方法可以更新调色板中的全部256个颜色项。每个颜色项都是一个用D3DFMT_A8R8G8B8格式表示的PALETTEENTRY结构,默认值为0xFFFFFFFF。

IDirect3DDevice9的调色板包含了一个阿尔法通道。若设备设置了D3DPTEXTURECAPS_ALPHAPALETTE能力位,则表示该设备支持调色板阿尔法,并可以使用该阿尔法通道。当纹理格式不含阿尔法通道时,就使用调色板阿尔法通道。若设备不支持调色板阿尔法,同时纹理格式也不含阿尔法通道,则使用0xFF作为阿尔法值。

系统中最多可以有65,536个调色板。因为调色板占用的内存资源与应用程序引用到的最大的调色板编号成正比,所以最好使用从零开始且连续的编号。


纹理坐标


大多数纹理,如位图,都是一个存放颜色值的二维数组,但立方体环境贴图除外,具体细节请参阅立方体环境贴图。数组中的每个颜色值被称为texel。每个texel在纹理中有唯一的地址,可以认为这个地址是行和列的编号,它们分别被标记为u和v。

纹理坐标位于纹理空间中,也就是说,它们相对于纹理中的位置(0,0)点。当把纹理贴到三维空间中图元的表面时,纹理的texel必须先被映射到对象坐标系,然后再变换到屏幕坐标系,或像素的位置。

Texel映射到屏幕空间

Microsoft® Direct3D®直接把纹理中的texel映射到屏幕空间,这样就省略了中间步骤并极大地提高了效率。这个映射的过程实际上是一个反向映射,也就是说,系统根据每个像素在屏幕空间中的位置计算该像素在纹理空间中相应的texel的位置,然后对位于该点或该点附近的纹理颜色进行取样。取样的过程被称为纹理过滤。更多信息请参阅纹理过滤

纹理中每个texel的位置可以用它的texel坐标表示。但是为了把texel贴到图元表面,Direct3D需要所有的纹理中的texel具有相同的地址范围,所以Direct3D使用了一种通用的寻址方法。在这种寻址方法中,所有texel的地址都在闭区间0.0到1.0内。Direct3D用u,v值表示纹理坐标,这和用x,y坐标表示二维笛卡尔坐标系非常相似。从技术上讲,系统事实上可以处理0.0到1.0范围外的纹理坐标,系统根据应用程序设置的纹理寻址模式来进行此类处理。更多信息,请参阅纹理寻址模式

采用这种方法的结果是相同的纹理地址在不同的纹理中会映射到不同的texel坐标。在下图中,正在使用的纹理地址是(0.5,1.0)。但是,因为纹理的大小不同,所以该纹理地址映射到不同的texel。左边纹理的大小为5x5,纹理地址(0.5,1.0)映射到texel (2,4)。右边纹理的大小为7x7,纹理地址(0.5,1.0)映射到texel (3,6)。

下图描述了一个经简化的texel映射过程。这个示例显然非常简单,要了解更多细节信息,请参阅直接把Texel映射到像素

本例中,图的左边显示了一个被理想化为正方形色块的像素。像素四角的地址被映射到对象空间中的三维图元,因为三维场景中图元的形状及观察角度的不同,像素的形状常常会被扭曲。像素四角所对应图元表面的区域然后被映射到纹理空间,这个映射过程会再次扭曲像素的形状。像素的最终颜色根据像素映射的区域所覆盖的texel计算得到。通过设置纹理过滤方法,应用程序可以控制让Direct3D使用何种方法计算像素颜色。更多信息,请参阅纹理过滤

应用程序可以直接给顶点指定纹理坐标,这使应用程序可以控制把纹理的哪些部分贴到图元上。例如,设想应用程序创建了一个与下面的纹理大小完全相同的图元,本例中,如果应用程序希望把整张纹理贴到整面墙上,那么应用程序给图元的顶点指定的纹理坐标就应该是(0.0,0.0), (1.0,0.0), (1.0,1.0), 和 (0.0,1.0)。

如果应用程序决定把墙的高度减半,应用程序仍可以把整张贴图贴到稍小的墙上,但这会挤压纹理并使之扭曲,或者应用程序也可以重新指定纹理坐标,使Direct3D使用纹理的下半部分。

如果应用程序为了把纹理贴到稍小的墙上而决定挤压或拉伸纹理,那么应用程序使用的纹理过滤方法会影响最终图像的质量。更多信息,请参阅纹理过滤

如果应用程序决定重新指定纹理坐标并使Direct3D使用纹理的下半部分,那么在本例中应用程序给图元的顶点指定的纹理坐标应该是(0.0,0.5), (1.0,0.5), (1.0,1.0), 和 (0.0,1.0),这样Direct3D就会把纹理的下半部分贴到墙上。

顶点的纹理坐标有可能大于1.0,如果应用程序给顶点指定的纹理坐标不在闭区间0.0到1.0范围内,那么应用程序还应该设置纹理寻址模式。更多信息,请参阅纹理寻址模式

纹理坐标和纹理层

纹理坐标通过纹理层与纹理联系在一起。纹理通过SetTexture(stageIndex, pTextre) 被设定到某一纹理层。请参阅IDirect3DDevice9::SetTexture

一个弹性顶点格式码最多可以定义八组纹理坐标,纹理坐标数据由用户在顶点数据中提供,数据通过索引值0到7来引用。最多可以有八个纹理混合层,一张纹理通过SetTexture(stageIndex, pTexture)与某一纹理层联系在一起。

完成以上操作后,任意一组纹理坐标可以被任意一纹理层使用。每一组纹理坐标通过SetTextureStageState(stageIndex, D3DTSS_TEXCOORDINDEX, textureCoordinateIndex)与某一纹理层联系在一起。请参阅IDirect3DDevice9::SetTextureStageState。通过这种方法,可以设置纹理混合层使它们使用任意一张纹理和任意一组纹理坐标。多个纹理层可以使用同一张纹理,或同一组纹理坐标。

以下主题包含了更多的信息。


直接把Texel映射到像素


应用程序经常需要把纹理贴到几何体上并使texel直接映射到屏幕上的像素。例如,以一个需要在纹理中显示文本的应用程序为例,为了使纹理中的文本能清晰地显示,应用程序需要以某种方式确保映射到几何体上的texel不受纹理过滤的影响。如果无法保证这一点,那么得到的图像通常会是模糊的,如果纹理过滤方法为最近点取样,那么可能会产生粗糙边缘。

为了统一像素和纹理取样,并同时支持图像和纹理过滤,Microsoft® Direct3D®的像素和纹理取样规则经过了精心定义,但这也使得把纹理中的texel直接映射到屏幕上的像素成为一个相当有意义却又艰难的挑战。要战胜这个挑战,需要透彻地理解Direct3D如何把用浮点数表示的纹理坐标映射到光栅化器使用的整数像素坐标。

为了把用浮点数表示的纹理坐标映射到texel地址,Direct3D执行下面的计算。

在这些公式中,TxTy为水平/垂直方向的输出texel坐标,uv为顶点提供的水平/垂直方向的纹理坐标。MxMy元素表示当前mipmap级水平/垂直方向的texel的数量。本节剩余部分将主要讨论在水平方向上从texel到像素的映射,垂直方向上的映射与水平方向完全相同。

把纹理坐标0.0和1.0代入以上公式,会使纹理坐标0.0映射到本次纹理迭代的第一个texel和上次纹理迭代的最后一个texel的中间,纹理坐标1.0会被映射到本次纹理迭代的最后一个texel和下次纹理迭代的第一个texel的中间。对于一个宽度为4的迭代纹理,在mipmap的第0级,下图显示了系统把坐标0.0和1.0映射到哪里。

理解了这种映射方式,应用程序只需给几何体的屏幕空间坐标加上一个偏移量,就可以强制系统把每个texel映射到相应的像素。例如,要绘制一个四边形并使前面的纹理中的每个texel一一映射到屏幕上唯一的像素,应用程序必须使几何体坐标覆盖所有像素,并使每个texel的中心正好对应每个像素的中心,这样得到的结果就是应用程序常要追求的一对一映射。

为了把宽度为4的纹理映射到像素坐标0到3,可以用两个三角形绘制一个四边形,三角形在屏幕空间的坐标为-0.5到3.5,纹理坐标为0.0到1.0。以屏幕空间坐标为0.0的像素为例,因为0.0与第一个顶点,位于屏幕空间-0.5,相距半个像素,而总的宽度为4.0,迭代后的纹理坐标为0.125(译注:每个像素的宽度为0.25=1/4,半个像素的宽度为0.125),然后用纹理的大小,此处为4,对纹理坐标进行缩放,得到的结果坐标为0.5。再减去偏移量0.5即得到纹理地址为0.0,该地址完全对应于贴图中的第一个texel。

再概括一下,纹理坐标覆盖纹理贴图的两边,并平均分布在它们之间。下图显示了这种映射,纹理的宽度为4。

系统以相同的方法归一化像素坐标和纹理坐标,因此,如果顶点与要渲染的像素重叠,且顶点使用的纹理坐标为0.0和1.0,那么像素和顶点就可以对齐。如果它们的大小相同且排列整齐,那texel和像素就可以完全对应,如下图所示。


纹理坐标的格式


Microsoft® Direct3D®中的纹理坐标可以包含一个、两个、三个或四个用浮点数表示的元素,用来寻址不同大小的纹理。一维纹理——纹理表面的大小为1xn个texel——通过一个纹理坐标寻址。最常见的情况是二维纹理,通过两个被称为uv的纹理坐标寻址。Direct3D支持两种三维纹理,立方体环境贴图立体贴图。立方体环境贴图不是真正三维的,但使用一个三元素向量寻址。更多细节,请参阅立方体环境贴图

顶点格式中所述,应用程序把纹理坐标编码在顶点格式中。顶点格式可以包含多组纹理坐标。可以用FVF码D3DFVF_TEX0到D3DFVF_TEX8描述不包含纹理坐标或最多可包含八组纹理坐标的顶点格式。

每组纹理坐标可以有一到四个元素。D3DFVF_TEXTUREFORMAT1到D3DFVF_TEXTUREFORMAT4标志用来描述一组纹理坐标中元素的个数,但这些标志不能直接使用,D3DFVF_TEXCOORDSIZEn系列宏使用这些标志创建位掩码,用来在顶点格式中描述某组纹理坐标中元素的个数。这些宏带一个参数,表示要设置元素个数的那组纹理坐标的索引值。以下示例代码显示了如何使用这些宏。

// 这个顶点格式包含两组纹理坐标。第一组(索引为0)包含2个元素,

// 第二组包含一个元素。顶点格式描述应该是:

//     dwFVF = D3DFVF_XYZ  | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2 |

//             D3DFVF_TEXCOORDSIZE2(0) | D3DFVF_TEXCOORDSIZE1(1);

//

typedef struct CVF

{

    D3DVECTOR position;

    D3DVECTOR normal;

    D3DCOLOR  diffuse;

    float     u, v;   // 第一组纹理坐标,二维

    float     t;      // 第二组纹理坐标,一维

} CustomVertexFormat;

注意  除了立方体贴图和立体贴图外,光栅化器无法使用两个以上的元素寻址纹理。应用程序最多可以提供三个元素给一组纹理坐标,但只有当纹理是立方体贴图或立体贴图,或使用了D3DTTFF_PROJECTED纹理变换标志时多余的元素才会被用到。D3DTTFF_PROJECTED标志会使光栅化器把前两个元素除以第三个(或第n个)元素。更多信息,请参阅纹理坐标变换


纹理坐标的处理


下图显示了纹理坐标从数据源,经过处理然后到达光栅化器的流程。

系统可以从两个数据源得到纹理坐标。对于某一层纹理,应用程序可以使用包含在顶点格式(D3DFVF_TEX1到D3DFVF_TEX8)中的纹理坐标,也可以使用Microsoft® Direct3D®自动生成的纹理坐标。对于后一种情况,请参阅自动生成的纹理坐标。如果当前纹理层的D3DTSS_TEXTURETRANSFORMFLAGS纹理层状态为D3DTTFF_DISABLE(默认值),那么系统不会对输入的纹理坐标进行变换。如果D3DTSS_TEXTURETRANSFORMFLAGS为任何其它值,那么系统会用该纹理层的变换矩阵对该输入纹理坐标进行变换。

D3DTEXTURETRANSFORMFLAGS枚举类型定义了D3DTSS_TEXTURETRANSFORMFLAGS纹理层状态的有效值。除了D3DTTFF_DISABLE标志不进行纹理坐标变换外,该枚举类型值用于设定系统传送至光栅化器的输出坐标的数量。D3DTTFF_COUNT1到D3DTTFF_COUNT4标志告诉系统把输出纹理坐标中的一个、两个、三个或四个元素传送至光栅化器。

D3DTTFF_PROJECTED标志有些特别:它告诉系统纹理坐标将用于经过投影的纹理。把D3DTTFF_PROJECTED标志与D3DTEXTURETRANSFORMFLAGS的其它成员一起使用可以告诉系统在光栅化操作前把所有元素除以最后一个元素。例如,当显式使用三元素纹理坐标,或纹理变换产生三元素纹理坐标时,应用程序可以同时使用D3DTTFF_COUNT3和D3DTTFF_PROJECTED标志,这会使光栅化器把前两个元素除以最后一个元素,并得到寻址二维纹理所需的二维纹理坐标。

注意  除了立方体贴图和立体贴图外,光栅化器无法使用多于两个元素的纹理坐标。如果应用程序提供的元素比寻址当前纹理层所需的多,那么多余的元素会被忽略。当把二维纹理坐标用于一维纹理时也是这样。

以下主题包含了更多的信息。


自动生成的纹理坐标


系统可以使用经过变换的摄像机空间中的位置或顶点的法向量作为纹理坐标,也可以计算用于寻址立方体贴图的三元素向量。与应用程序在顶点数据中明确给出的纹理坐标一样,应用程序可以使用自动生成的纹理坐标作为纹理变换的输入。

通过无需在顶点格式中显式地给出纹理坐标,自动生成的纹理坐标可以显著降低几何数据所需的带宽。在许多情况下,系统生成的纹理坐标可以和纹理变换一起使用以生成特效。当然这只是一种特殊用途,大多数情况下应用程序还是要用显式给出的纹理坐标。

设定自动生成的纹理坐标

C++应用程序用D3DTSS_TEXCOORDINDEX纹理层状态(来自D3DTEXTURESTAGESTATETYPE)控制系统如何产生纹理坐标。

一般来说,这个状态告诉系统使用编码在顶点格式中的某组特定的纹理坐标。当应用程序给这个状态指定的值包含D3DTSS_TCI_CAMERASPACENORMAL,D3DTSS_TCI_CAMERASPACEPOSITION,或D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR标志时,系统执行的操作会完全不同。如果使用了这些标志中的任意一个,那么纹理层会忽略在顶点格式中的给出的纹理坐标,并优先使用系统生成的坐标。下表列出了每个标志的意义。

  • D3DTSS_TCI_CAMERASPACENORMAL

使用变换到摄像机空间的顶点法向作为输入纹理坐标。

  • D3DTSS_TCI_CAMERASPACEPOSITION

使用变换到摄像机空间的顶点位置作为输入纹理坐标。

  • D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR

使用变换到摄像机空间的反射向量作为输入纹理坐标。反射向量根据输入顶点位置和法向量计算得到。

前面的这些标志是互斥的。如果应用程序指定了其中一个标志,那么应用程序还可以指定一个索引值,系统用这个索引值决定纹理环绕模式。

以下示例代码显示了如何在C++应用程序中使用这些标志。

/*

 * 在本例中,d3dDevice变量为指向IDirect3DDevice9接口的有效指针。

 *

 * 在当前纹理层使用顶点位置(摄像机空间)作为输入纹理坐标,

 * 纹理环绕模式在D3DRENDERSTATE_WRAP1渲染状态中设置。

 */

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX,

                                   D3DTSS_TCI_CAMERASPACEPOSITION | 1 );

自动生成的纹理坐标在作为纹理坐标变换的输入,或使应用程序无需计算用于寻址立方体环境贴图的三元素向量时最为有用。

球形贴图使用一张预计算的(在建模时)纹理贴图,该贴图包含了一个反光的球体反射的整个环境。Microsoft® Direct3D®有一个纹理坐标自动生成特性,使用了D3DTSS_TCI_CAMERASPACENORMAL渲染状态,该特性会取得变换到摄像机空间的顶点法向,并对它进行纹理变换生成纹理坐标。更多信息请参阅Sphere Map示例

相关主题


纹理坐标变换


Microsoft® Direct3D®设备可以用一个4x4矩阵对顶点的纹理坐标进行变换。系统使用相同的方法对纹理坐标和几何体进行变换。任何变换(缩放、旋转、投影、shear或这些变换的组合)可以用一个4x4矩阵完成。

注意  Direct3D不改变经过变换和光照处理的顶点,因此,使用经过变换和光照处理的顶点的应用程序无法让Direct3D变换顶点的纹理坐标。

支持硬件变换和光照的设备(TnLHAL设备)也会对纹理坐标变换进行硬件加速。如果设备不支持硬件变换和光照,那么Direct3D会使用几何流水线中与平台相关的优化进行纹理坐标变换。

纹理坐标变换非常有用,它在生成特效的同时避免了对几何体纹理坐标的直接修改。应用程序可以使用简单的平移或旋转矩阵给物体表面的纹理生成动画效果,也可以对Direct3D自动生成的纹理坐标进行变换,这样可以简化并可能加速如投影纹理和动态光照贴图等高级特效。另外,在多层纹理中,应用程序也可以用纹理坐标变换重复使用某一组纹理坐标并将之用于多种用途。

设置及取得纹理坐标变换的信息

和应用程序用于变换几何体的矩阵一样,应用程序可以通过IDirect3DDevice9::SetTransformIDirect3DDevice9::GetTransform方法设置和取得纹理坐标变换的信息。在调用这些方法时,用D3DTRANSFORMSTATETYPE枚举类型的成员D3DTS_TEXTURE0到D3DTS_TEXTURE7标识纹理层0到7。

以下示例代码设置了一个矩阵,对纹理层0的纹理坐标进行变换。

// 本例中,假设d3dDevice变量为指向IDirect3DDevice9接口的有效指针。

 

D3DMATRIX matTrans = D3DXMatrixIdentity( NULL );

 

// 为希望的变换设置矩阵。

d3dDevice->SetTransform( D3DTS_TEXTURE0, &matTrans );

启用纹理坐标变换

D3DTSS_TEXTURETRANSFORMFLAGS纹理层状态控制对纹理坐标的变换。这个纹理层状态的值由D3DTEXTURETRANSFORMFLAGS枚举类型定义。

D3DTSS_TEXTURETRANSFORMFLAGS为D3DTTFF_DISABLE(默认值)时,纹理坐标变换被禁用。假设纹理层0的纹理坐标变换已启用,以下代码将之禁用。

// 本例中,假设d3dDevice变量为指向IDirect3DDevice9接口的有效指针。

 

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_DISABLE );

D3DTEXTURETRANSFORMFLAGS定义的其它值用于启用纹理坐标变换,并控制把结果纹理坐标中的几个元素传送到光栅化器。以下为示例代码。

// 本例中,假设d3dDevice变量为指向IDirect3DDevice9接口的有效指针。

 

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_COUNT2 );

D3DTTFF_COUNT2值告诉系统对纹理层0进行变换,然后把得到的纹理坐标的前两个元素传送给光栅化器。

D3DTTFF_PROJECTED纹理变换标志表示用于投影纹理的坐标。当这个标志被设置时,光栅化器会把传入的元素除以最后一个元素。以下为示例代码。

// 本例中,假设d3dDevice变量为指向IDirect3DDevice9接口的有效指针。

 

d3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS,

                                 D3DTTFF_COUNT3 | D3DTTFF_PROJECTED );

本例告诉系统传送三个纹理坐标元素到光栅化器。光栅化器把前两个元素除以第三个元素,得到寻址纹理所需的二维纹理坐标。


特效


本主题包含了可以用纹理坐标处理实现的特效。

给建模表面的纹理生成动画效果(通过变换或旋转)

  • 在顶点格式中定义一组二维纹理坐标。

·                // 使用单纹理,二维纹理坐标。这个位掩码会根据需要

·                // 被扩展为包含位置,法向和颜色信息。

DWORD dwFVFTex = D3FVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0);

  • 设定光栅化器,使之使用二维纹理坐标。

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

  • 定义并设置合适的纹理坐标变换矩阵

// M为要设置的D3DMATRIX,用于在U和V方向对纹理坐标进行变换。

//      1   0  0  0

//      0   1  0  0

//      du dv  1  0 (du和dv每帧都会改变)

//      0   0  0  1

 

D3DMATRIX M = D3DXMatrixIdentity(); // 在d3dutil.h中声明

M._31 = du;

M._32 = dv;

把纹理坐标作为建模在摄像机空间中的位置的线性函数创建

  • D3DTSS_TCI_CAMERASPACEPOSITION标志告诉系统使用顶点在摄像机空间中的位置作为纹理变换的输入。

·                // 为了节省带宽,输入顶点没有纹理坐标。三个纹理坐标用顶点在

·                // 摄像机空间中的位置(x, y, z)产生。

SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);

  • 告诉光栅化器使用二维纹理坐标。

·                // 使用了两个输出纹理坐标。

SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

  • 定义并设置生成线性函数的矩阵。

// 把纹理坐标作为线性函数创建,这样的话:

//      u = Ux*x + Uy*y + Uz*z + Uw

//      v = Vx*x + Vy*y + Vz*z + Vw