分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow
也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!
本系列文章由zhmxy555(毛星云)编写,转载请注明出处。
文章链接: http://blog.csdn.net/zhmxy555/article/details/8567848
作者:毛星云(浅墨) 邮箱: happylifemxy@163.com
本篇文章里,我们首先对Direct3D之中固定功能流水线中的纹理映射相关的知识进行了详尽的剖析,然后我们将DirectInput这套游戏输入控制领域一手遮天的API用一个C++类进行了抽象和封装,这样之后我们用起来会显得非常便捷。希望在学习抽象和封装这套API的时候,大家能领悟到那些只能意会不可言传的宝贵编程思想。
文章末尾依旧是提供文章配套详细注释的demo源代码的欣赏,并在文章末尾给出了源代码的下载地址。
一、纹理映射知识回顾
因为这是纹理映射技术讲解的下半部分文章,且中间隔了一次在讲DirectInput,所以我们有必要把纹理映射知识再回顾一下。
首先我们来概括回顾一下《【Visual C++】游戏开发笔记四十一 浅墨DirectX教程之九 为三维世界添彩:纹理映射技术(一)》中讲过的知识点。
纹理是所有游戏中永恒的核心部分,无论是2D还是3D游戏。一言以蔽之,纹理映射是一种将2D图像映射到3D物体上的技术,可以让贴上纹理的表面展现出化腐朽为神奇的逼真显示效果。比如一块用砖块砌成的墙,更合适的做法是用简单的平面形状表面贴上砖块的纹理图像,而不是傻傻地用大量复杂的几何图形来模拟砖块坑洼的表面形态。纹理在计算机图形学中存在的意义就是模拟出复杂的表面图形和形状,而这种近乎逼真的模拟效果是单单用几何图形无法比拟的。通过这幅图可以非常容易地理解纹理映射的概念:
接着我们来复习一下《【Visual C++】游戏开发笔记四十一 浅墨DirectX教程之九 为三维世界添彩:纹理映射技术(一)》中讲到的核心知识点,使用纹理的四步曲:
纹理映射使用四步曲,简明扼要20个字:
顶点的定义,顶点的访问,纹理的创建,纹理的启用。
四步曲之一、顶点的定义。
FVF顶点格式的定义,最多八层纹理
四步曲之二、顶点的访问。
访问顶点缓存内容,纹理坐标的填充
四步曲之三、纹理的创建。
一个对象:IDIRECT3DTEXTURE9接口对象,
一个方法:D3DXCreateTextureFromFile方法
四步曲之四、纹理的启用。
一个方法:SetTexture方法
复习完前置知识,下面我们就来看看本篇文章中的故事会有怎样的美好。
二、四大纹理过滤方式精讲
我们知道,当Direct3D渲染一个图元或者三维图形时,必须将它们通过坐标变换映射到二维屏幕之上。而当我们使用纹理来进行辅助渲染的时候,Direct3D就必须使用该纹理为二维图像上的每个像素进行着色。这里的每个像素都包含一个来自纹理的颜色值,而从纹理中为每个像素获取颜色的过程,就是所谓的纹理过滤(texture filtering)了。
大多数情况下,屏幕显示的图形大小与纹理贴图大小是不同的。换句话说的话,这个纹理将被映射到一个比它大或者小的图元的图像上。这样纹理常常会被放大或者缩小。对纹理的放大会造成许多像素被映射到同一个纹理元素上,这样图形渲染的结果就会有色块的感觉。缩小一个纹理意味着一个像素被映射到许多纹理元素上,图形看上去会闪烁或者失真抑或是有锯齿。而为了解决这些问题,可以将相关纹理元素的颜色融合到一个像素之上,而如何将多个纹理元素的颜色融合到一个像素就取决于纹理过滤的方式了。
Direct3D中,有四种纹理过滤方式,他们分别是:
1.最近点采样过滤(nearst-pointsampling)
2.线性纹理过滤(lineartexture filtering)
3.各项异性过滤(anisotropictexture filtering)
4.多级渐进过滤(texturefiltering with mipmaps)
这纹理过滤的“四大天王”各有优缺点。比如,线性过滤生成的图像较糙,但是计算量小。多级渐进纹理过滤的效果通常最好,特别是和各向异性过滤联合使用的时候效果更佳。但是它占有的资源最多,计算量也最大。
设置纹理过滤方式通常都是用IDirect3DDevice9::SetSamplerState函数,这个函数用于设置纹理的各种采样属性,下面我们来讲下一这个函数:
HRESULT SetSamplerState( [in] DWORD Sampler, [in] D3DSAMPLERSTATETYPE Type, [in] DWORD Value);
■ 第一个参数,DWORD类型的Sampler,指定为哪一层纹理设置采样状态,在Direct3D中支持最多8层纹理,所以这个值取值显然就是0~7了。如果只使用单层纹理进行渲染的话,这个值设为0就行了。
■ 第二个参数,D3DSAMPLERSTATETYPE类型的Type,用来指定对哪种纹理采样属性来进行操作。从参数类型来看我们就知道需要在枚举体D3DSAMPLERSTATETYPE中进行取值,下面我们就来看看这个枚举体的定义:
typedef enum D3DSAMPLERSTATETYPE { D3DSAMP_ADDRESSU = 1, D3DSAMP_ADDRESSV = 2, D3DSAMP_ADDRESSW = 3, D3DSAMP_BORDERCOLOR = 4, D3DSAMP_MAGFILTER = 5, D3DSAMP_MINFILTER = 6, D3DSAMP_MIPFILTER = 7, D3DSAMP_MIPMAPLODBIAS = 8, D3DSAMP_MAXMIPLEVEL = 9, D3DSAMP_MAXANISOTROPY = 10, D3DSAMP_SRGBTEXTURE = 11, D3DSAMP_ELEMENTINDEX = 12, D3DSAMP_DMAPOFFSET = 13, D3DSAMP_FORCE_DWORD =0x7fffffff} D3DSAMPLERSTATETYPE,*LPD3DSAMPLERSTATETYPE;
这个枚举体中的D3DSAMP_MAGFILTER、 D3DSAMP_MINFILTER 、 D3DSAMP_MIPFILTER、D3DSAMP_MIPMAPLODBIAS、 D3DSAMP_MAXMIPLEVEL、D3DSAMP_MAXANISOTROPY用于控制纹理过滤的方式,具体用法我们下面会讲到的。
■ 第三个参数,DWORD类型的Value,这个参数和第二个参数Type联系很紧密,就是对第二个参数指定的属性进行值的设置的。
Ⅰ、最近点采样过滤
最近采样点过滤可谓是四种纹理过滤方式中速度最快但是效果最差的过滤方式了。Direct3D计算得到的纹理过滤元素通常都是一个浮点值,当使用最近点采样时,Direct3D会复制与这个浮点值地址最接近的整数地址的纹理元素的颜色。
设置最近点采样的具体方法其实很简单,就是调用我们上面提到的IDirect3DDevice9::SetSamplerState函数,通过第二个参数Type的不同取值,分别设置纹理过滤的放大过滤器和缩小过滤器就可以了。其中,将第一个参数设置为与我们相关联的纹理层的序号(0~7之间取值)。第二个参数Type取D3DSAMP_MAGFILTER表示设置放大过滤器,取D3DSAMP_MINFILTER表示设置缩小过滤器。而对应于第二个参数的取值,第三个参数就直接设为表示最近点采样的枚举常量D3DTEXT_POINT就行了。
下面就是一个调用实例,把纹理层2的过滤方式设置为最近点采样:
//最近点采样过滤 g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER, D3DTEXF_POINT); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER, D3DTEXF_POINT);
对于使用最近点采样过滤后图像质量相关的常识,我们提一下:如果纹理大小和屏幕图元的实际大小相近,那么采用最近点采样过滤对图像质量的影响不大。但是,如果纹理大小和屏幕图元的实际大小相差太多,则图像的精度就会受到影响,从而图像质量就会差强人意,出现闪烁或者失真的现象。
Ⅱ、线性纹理过滤
线性纹理过滤是目前使用最广泛的纹理过滤方式。与最近点采用方式相比,能有效地提高图像的显示质量,且对系统性能的影响不大。线性纹理过滤取得与计算得到的纹理元素的浮点地址最接近的上下左右4个纹理元素,对这四个纹理元素进行加权平均,从而得到最终显示得颜色值。因为是在单一纹理层上的线性过滤,而且是从x,y方向上的线性过滤,所以我们也称通常的线性纹理过滤为双线性纹理过滤。
关于使用方法,与上面刚讲到的最近点采样方法当然类似,就是用SetSamplerState函数,第二个参数还是D3DSAMP_MAGFILTER与D3DSAMP_MINFILTER,有不同的地方就是第三个参数,改为线性纹理过滤专属的D3DTEXF_LINEAR就可以了。下面依旧是一个调用实例,将第0层纹理的放大和缩小过滤器设为线性纹理过滤:
//线性纹理过滤g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);g_pd3dDevice->SetSamplerState(0,D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
目前市面上的大多数显卡都为线性纹理过滤进行了针对性优化,所以使用这种纹理过滤方式能获得比较好的图形质量,而且也不吃资源。线性纹理过滤是目前使用最广泛的纹理过滤方式。
Ⅲ. 各向异性纹理过滤
首先需要知道一个常识,在很多时候,三维物体的表面不可能是完全平面的,当三维物体的表面和投影平面不平行时,它在屏幕上的投影会有拉长和扭曲的现象,这种现象称为各向异性(Anisotropic)。当一个各项异性的图元映射到纹理元素上时,它的形状就会发生扭曲。而Direct3D会根据屏幕像素反向转换到纹理元素的延长度,来决定各项异性的程度。
关于使用方法,依然是用SetSamplerState,与最近点采样过滤和线性纹理过滤不同的地方,除了第三个参数设为我们各向异性纹理过滤的专属D3DTEXF_ANISOTROPIC外,另外还需专门设置一下最大各项异性程度值。还是用这个货SetSamplerState,第一个参数取需要进行设置的纹理层数,第二个参数设为D3DSAMP_MAXANISOTROPY,第三个参数设为大于一的任意值就行了。
下面是一个实例,对第1层纹理,设置最大各项异性值为3的各向异性纹理过滤方式:
//各向异性过滤 // g_pd3dDevice->SetSamplerState(1,D3DSAMP_MAXANISOTROPY, 3); // g_pd3dDevice->SetSamplerState(1,D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC); // g_pd3dDevice->SetSamplerState(1,D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
Ⅳ、多级渐进纹理过滤
首先需要知道的是,这个多级渐进纹理过滤通常不是独当一面的纹理过滤方式,他需要找个伴,也就是需要和我们上面讲到的四大纹理过滤方式其他三种之一结合使用。
多级渐进纹理就是由一组分辨率逐渐降低的纹理序列组成的,每一级纹理的宽度和高度都是上一级纹理的宽度和高度的一半。需要注意,纹理的宽度和高度并不一定要相等。也就是说,这些图形并不一定要是正方形。
Direct3D在纹理映射时,自动选择一幅与物体大小最接近的纹理进行渲染。当物体离投影面很远时,Direct3D会选择一张尺寸较小、分辨率较低的纹理进行渲染:当物体离投影面较近时,Direct3D则会选择一张尺寸较大,分辨率较高的纹理进行渲染。就像这样:
上面这四张图从左到右分别是512x512,256x256,128x128,64x64分辨率的纹理链,为了排版美观,浅墨在这里对图片整体进行了适当的缩小。
好了,下面继续讲。
Direct3D将多级渐进纹理描绘成一系列相互联系的表面,当创建一个渐进纹理时,分辨率最高的纹理位于开始处,并与下一级纹理相互联系,直到分辨率最低的一级纹理。
Direct3D能够估计出多级渐进纹理链中哪幅纹理的分辨率最接近想要的输出结果,然后能将像素映射到纹理空间。而且当最终显示的图形大小介于任意两级纹理图形之间时,Direct3D能智能地取得两级纹理的相应元素进行混合后显示出来。
多级渐进纹理过滤能够有效地提高图形渲染速度,当物体离投影平面较远的时候,Direct3D会选择一张尺寸较小的纹理进行渲染,而无需经过复杂的诸如各项异性纹理过滤。并且由于这时纹理需要的显存比不使用多级渐进纹理要小,因此还能有效地减小纹理载入显存的时间。
对于多级渐进纹理的生成,使用我们之前讲创建纹理的时候提到过的一个方法,就是D3DXCreateTextureFromFileEx方法:
HRESULT D3DXCreateTextureFromFileEx( _In_ LPDIRECT3DDEVICE9pDevice, //D3D设备接口对象 _In_ LPCTSTR pSrcFile, //纹理贴图的文件地址 _In_ UINT Width, //纹理宽度,为0的话表示使用贴图的宽度 _In_ UINT Height, //纹理高度,为0的话表示使用贴图的高度 _In_ UINT MipLevels, //生成的渐进纹理的级数 _In_ DWORD Usage, //用法的标志,通常设为0 _In_ D3DFORMAT Format, //纹理贴图的格式 _In_ D3DPOOL Pool, //保存纹理的方式 _In_ DWORD Filter, //纹理过滤方式 _In_ DWORD MipFilter, //生成纹理序列的过滤方式 _In_ D3DCOLOR ColorKey, //替换Alpha值得颜色值 _Inout_ D3DXIMAGE_INFO *pSrcInfo, //通常设为NULL就可以了 _Out_ PALETTEENTRY *pPalette, //调色板的地址,通常设为NULL即可 _Out_ LPDIRECT3DTEXTURE9*ppTexture //纹理接口对象);
然后是这个函数的一个调用实例:
D3DXCreateTextureFromFileEx(g_pd3dDevice,L"pal5q.jpg", 0, 0, 6, 0, D3DFMT_X8R8G8B8, D3DPOOL_MANAGED,D3DX_DEFAULT, D3DX_DEFAULT, 0xFF000000, 0, 0, &g_pMipTexture);
下面讲一下多级渐进的使用方法。我们在这小节开头一开始就提到过,多级渐进纹理过滤通常不是独当一面的纹理过滤方式,他需要找个伴,也就是最好和我们上面讲到的四大纹理过滤方式其他三种之一结合使用。首先对于多级渐近纹理过滤,还是用SetSamplerState函数,第一个参数依旧是设置纹理层数的序号,第二个参数设为D3DSAMP_MIPFILTER,第三个参数设置为在相邻纹理级之间的过滤方式,取枚举类型D3DTEXTUREFILTERTYPE的任意值。比如下面这个段代码就把相邻纹理级之间的过滤方式设置为线性过滤。
// g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
如果把上面第三个参数设为D3DTEXF_NONE的话,就表示一直使用最高一级的纹理。而设为D3DTEXF_POINT则Direct3D就会只使用与图元大小最匹配的一级纹理;而设为D3DTEXF_LINEAR的话,Direct3D就会与图元的大小最匹配的两级纹理以线性方式混合。
另外,我们还需要设置一下实际渲染时纹理过滤的最大级数,需要将第二个参数设为D3DSAMP_MAXMIPLEVEL,而第三个参数设为实际渲染时纹理过滤的最大级数。下面这句代码就是设置了纹理层5的最大多级纹理过滤级数为8:
// g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAXMIPLEVEL, 8);
另外,还可以通过IDirect3DDevice9:: SetSamplerState的第二个参数设为D3DSAMP_MIPLODBIAS,来设置多级纹理映射级数的偏移值。
好了,关于线性纹理过滤相关的讲了这么多,我们来串联一下,给一个综合起来的实例,线性纹理过滤配合多级渐进纹理过滤的实例:
//首先开线性纹理过滤 g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);//然后设置多级渐进纹理过滤 g_pd3dDevice->SetSamplerState(0,D3DSAMP_MIPFILTER, D3DTEXF_LINEAR); g_pd3dDevice->SetSamplerState(0,D3DSAMP_MAXMIPLEVEL, 15);