上一次已经讲了DXUT的基本工作模式,汝在DirectX SDK里面把EmptyProject找出来,然后把工程建立好,把include和lib等也配置,可能有的不知道怎么配置,咱这里还是说一下吧


若汝使用的是Visual Sutdio,那么 工具->选项->项目和解决方案->VC++目录。右边在“包含文件”里加上DirectX SDK目录底下的include目录,“库文件”里加上DirectX SDK目录底下的lib/xXX目录,若汝使用的是32位机,就选x86,64位机选择x64。若是其他IDE,在系统环境变量里配置就好。


现在汝按下F5,就会出现一个640*480的黑色的窗口。这个就是DXUT底层完全准备好了,就等汝干事儿了。

接下来咱们要干嘛?很容易想到的就是画图。于是给汝介绍一下在Direct3D中处理2D图像的核心内容——Sprite

Sprite是Direct3D中处理2D图像的十分便利的一个武器。它的用处就是绘制某张指定的纹理,并且可以加上简单的颜色化渲染。

HRESULT Draw(
  LPDIRECT3DTEXTURE9 pTexture,
  CONST RECT * pSrcRect,
  CONST D3DXVECTOR3 * pCenter,
  CONST D3DXVECTOR3 * pPosition,
  D3DCOLOR Color
);

Sprite::Draw函数的原型就是这样。不过这个函数必须在Sprite::Begin()和Sprite::End()这两个函数之间才能使用。这个Begin和End便是加在OnFrameRender()这个回调函数里面。那么下来我们细说这些参数。
pTexture 这个要传入需要绘制的纹理对象的指针
pSrcRect 这个传入需要绘制纹理pTexture的哪一部分,就是一个矩形限定框
pCenter 以pCenter为中心绘制纹理
pPosition 以pPosition为左上角为起始点绘制
Color 用Color颜色来渲染此次绘制

利用这个函数咱就可以创造一个世界了~有图就有真相。那么咱是如何创造这个世界呢?
咱可以将这个函数封装一下,让咱可以非常方便的使用它。
平常绘制图片,因为不存在3D,所以咱需要的也就这几个
texture 需要绘制的纹理
srcRect 绘制纹理的srcRect
darwRect 将纹理的srcRect部分拉伸绘制在屏幕的drawRect部分
color 用color颜色来渲染此次绘制

于是咱就可以计划咱的简单的图形引擎(虽然很简陋)了。总结出下列的封装后的接口,咱们将它们封装进咱们的图像引擎,也就是GraphEngine:
class GraphEngine
{
    
public
        
//-------------------------绘制接口----------------------------------------
        void Draw(string whichone, int dx, int dy);                       //在(dx,dy)处绘制路径名为whichone的整个纹理
        void Draw(string whichone, RECT drawRect);                       //在drawRect内绘制整个纹理
        void Draw(string whichone, RECT srcRect, int dx, int dy);      //在(dx,dy)处绘制纹理的srcRect部分
        void Draw(string whichone, RECT srcRect, RECT drawRect);       //在drawRect内绘制纹理的srcRect部分
        
//---------------------这四个就是前面的接口加上一个渲染的颜色------------------------
        void Draw(string whichone, int dx, int dy, D3DCOLOR color);        
        
void Draw(string whichone, RECT drawRect, D3DCOLOR color);        
        
void Draw(string whichone, RECT srcRect, int dx, int dy, D3DCOLOR color);       
        
void Draw(string whichone, RECT srcRect, RECT drawRect, D3DCOLOR color);                
            //------------------------------------------------------------------------------------
}
;

具体实现咱们先放一放。规划一个程序千万不要直接从底层开始想。虽然我们这个是面向对象编程,但是“自顶向下,逐步求精”依然对我们很有帮助。

以上的那些接口如果实现了,那么我们就可以真真正正地开始绘图了。

大概汝也发现了,需要画的是一个纹理的指针,可是我这里传入的是一个路径名。为什么呢?这是因为每次绘制的时候,汝要是每次都以“纹理的指针”方式代表一个纹理,那么可以想见不是很好管理。于是我们就引入纹理池的概念。纹理池正如其名,是用来管理纹理的一个池子。Direct3D中有专门的东西来管理,可是咱这里用不到里面的复杂的东西,只用手动模拟一个便可。咱的代码里是直接用数组存的,也对应存了那纹理的路径名。查找一个纹理就直接for过去,是O(n)级别的。要是汝追求效率的话可以写棵平衡树,用map也可以。

汝说汝没看见代码,不信?好吧,咱可是贤狼赫罗啊!给汝解析Draw(string whichone,RECT srcRect,RECT drawRect,D3DCOLOR color);这个函数吧,解析了汝明白了后,其他的也就不在话下。

///-------------------------------------
///在矩形drawRect内伸缩绘制纹理的srcRect部分,并用制定颜色渲染
///-------------------------------------

void GraphEngine::Draw(string whichone,RECT srcRect,RECT drawRect, D3DCOLOR color)
{
    D3DXMATRIX ptransform,ptransform2,ptransform3,ptransform4;
    pSprite
->GetTransform(&ptransform);
    
float fx = (float)(drawRect.right-drawRect.left)/(float)(srcRect.right-srcRect.left);
    
float fy = (float)(drawRect.bottom-drawRect.top)/(float)(srcRect.bottom-srcRect.top);
    D3DXMatrixTranslation(
&ptransform2,drawRect.left/fx,drawRect.top/fy,0.0f);
    D3DXMatrixScaling(
&ptransform3, fx, fy, 1.0f);
    D3DXMatrixMultiply(
&ptransform4,&ptransform2,&ptransform3);
    D3DXMatrixMultiply(
&ptransform4,&ptransform4,&ptransform);
    pSprite
->SetTransform(&ptransform4);
    pSprite
->Draw(TexturePool[findTexture(whichone)],&srcRect,NULL,NULL,color);
    pSprite
->SetTransform(&ptransform);
}

D3DXMATRIX 申明了几个变换矩阵(什么是变换矩阵?一会儿咱会解释,汝先继续看)。在Direct3D中,坐标系以矩阵形式存储,并且可以通过矩阵变换来变换坐标系。
我们要吧图画在drawRect部分里,于是我们首先把坐标系原点变换到drawRect矩形的左上角。因为后面还会伸缩坐标系,所以不能直接移动过去,而要以drawRect和srcRect的比例移动过去。
pSprite->GetTransform(&ptransform);这句将原本的坐标系存储下来,好一会儿还原回去。fx记录了X轴方向的比例,fy记录了Y轴方向的比例。
D3DXMatrixTranslation(&ptransform2,drawRect.left/fx,drawRect.top/fy,0.0f);这句将ptransform2这个矩阵改变成记录了“在X轴方向平移drawRect.left/fx,在Y轴方向平移drawRect.top/fy,在Z轴方向平移0(因为我们不涉及3D)”的变换信息的矩阵。
D3DXMatrixScaling(&ptransform3,fx,fy,1.0f);这句将ptransform3这个矩阵改变成记录了“X轴拉伸比例为fx,Y轴拉伸比例为fy,Z轴拉伸比例为1.0(因为我们不涉及3D)”的变换信息的矩阵。
D3DXMatrixMultiply(&ptransform4,&ptransform2,&ptransform3);这句便是矩阵乘法了,将ptransform2*ptransform3的结果存入ptransform4中。
接下来的一句就是将目前的和原始的变换矩阵相乘。
pSprite->SetTransform(&ptransform4);这句将ptransform4变换矩阵设置成坐标系。
pSprite->Draw(TexturePool[findTexture(whichone)],&srcRect,NULL,NULL,color);Draw函数的原型已经介绍过了,其中的pCenter参数传入NULL,因为pCenter大多用到的地方是在3D里,咱不用关心。而pPosition传入NULL是因为咱们前面已经变换过了坐标系,只用直接在原点绘制出来就好了。findTexture(whichone)是一个需要自己写的函数,具体就是传入文件名路径,然后返回值是这个文件在纹理池中的索引。若此纹理不存在于纹理池中便重新加载一张纹理然后返回索引。
最后一句将坐标系还原。

于是这个就解释完了。那么留下的问题就是所谓的变换矩阵。矩阵就不解释了。变换矩阵就是可以通过矩阵乘法来将你的坐标变换至目标坐标,这样利用矩阵乘法十分方便快捷,效率很高。
3D里的矩阵变换是由一个1行4列矩阵(点(x0,x1,x2)),以及一个4行4行矩阵(变换矩阵)的矩阵乘法组成,是这样的(这里矩阵只以3*3为例,意在表现矩阵乘法):


其中(x0,x1,x2)是原始点,(y0,y1,y2)是最终得出的目标点。变换信息存储于矩阵中,由矩阵乘法得出结果。而实际根据变换不同需要的变换矩阵也不同。下面介绍一下3D中常用的变换矩阵(请注意矩阵表示方法,上面那图是从上到下,下面的是从左往右):
1、平移变换

这个变换需要4乘4的变换矩阵,如上图所示,其得出的结果便是(x,y,z)平移(Tx,Ty,Tz)得到的坐标。
过程名:D3DXMatrixTranslation

2、伸缩变换

结果是(x,y,z)在X轴方向伸缩比例Sx,Y轴方向伸缩比例Sy,Z轴方向伸缩比例Sz得到的坐标。
过程名:D3DXMatrixScaling

3、旋转变换

旋转变换分为3种:绕X轴,绕Y轴,绕Z轴。图示分别如下:
X轴:
Y轴:
Z轴:
结果是(x,y,z)绕X轴或Y轴或Z轴旋转角度θ(弧度制)
过程名:D3DXMatrixRotationX D3DXMatrixRotationY D3DXMatrixRotationZ

咱使用中只要忽略Z轴,便可达到想达到的变换效果。

再说一下所谓的渲染颜色
D3DCOLOR是由4个部分组成的:A(alpha,透明值)以及RGB(Red,Green&Blue),
这个值指定了纹理中A、R、G、B的表现程度
下面我们用半透明的灰色来遮住一张图看看效果:
原图:


用color (255,50,50,50)绘制了以后的效果:

也就是所有颜色的“表现力”从255变成50了

若用(255,0,255,255)绘制了以后,红色部分就无法表现出来了:


若是ALPHA值低一点,会变得透明。

这些所有的了解了后,你就可以画图了!注意,因为Direct3D中存储的纹理长宽必须是2的方幂的。因为这样对硬件加速方面很有帮助(咱不需要了解)。咱把到目前为止的代码全部打包发出来一下,代码里也有注释的,汝研究一下便可以领会透彻。

代码下载地址(工程文件属于Visual Sutio 2008,若汝的VS版本过低,请网上百度一下VS工程转换器):
/Files/CK985/Direct3D_2D.rar

里面的工程咱都是调制出来的,编译运行没问题,会在800*600的窗口里画出上面那张原图。代码里面除了今天讲的绘制纹理方面,也有文字的绘制,还有一些其他的东西,都是为了使之运行更完美。汝可对比一下空的EmptyProject和咱写的。

那今天就到这里了。有不懂的可以回帖,也可以单独联系我QQ670188680。
下篇:Direct3D中的简单2D绘制(下)——文字的绘制以及为了GAL GAME而完善的框架。