首页 > 代码库 > DirectX 9.0c游戏开发手记之RPG编程自学日志之15: Drawing with DirectX Graphics (用DirectX图形绘图)(第8-9节)
DirectX 9.0c游戏开发手记之RPG编程自学日志之15: Drawing with DirectX Graphics (用DirectX图形绘图)(第8-9节)
本文由哈利_蜘蛛侠原创,转载请注明出处!有问题请联系2024958085@qq.com
这一次我们继续来讲述Jim Adams 老哥的RPG编程书籍第二版第二章的第8节:Using Fonts(使用字体),和第9节:Billboards (广告牌)。这两节的内容都不多,所以就放在一期里面讲了。
原文翻译:
===============================================================================
2.8 Using Fonts (使用字体)
DirectX 9的一个缺陷就是它不支持字体。DirectX的老一些的版本(在DX 8之前)可以操控Windows的字体绘制函数。而在DX 9里面,你必须手动地绘制一个字体到一个纹理表面上,然后将字体的每个字母作为一个小的带纹理映射的多边形进行绘制。
管理一个包含有字体的纹理对于仅仅是绘制文本来说有点太费劲了,但是多亏了D3DX,你可以利用一个特殊的对象ID3DXFont,它可以为你处理这些纹理映射字体。ID3DXFont对象包含有7个函数,但是我们在这里只对其中的3个感兴趣。
这3个函数是:(注意:下面的Begin 函数和End 函数现在已经没有了。另外DrawText 函数也与现在的版本不一样了。这充分说明了我之前提到过的“即使同样是DirectX 9也有很多区别”这一点。详情请见这一节后面我的补充文字。)
● ID3DXFont::Begin。这个函数代表一个文本绘制操作的开始。
● ID3DXFont::End。这个函数代表一个文本绘制操作的结束。
● ID3DXFont::DrawText。这个函数绘制文本。
注意
===============================================================================
你也许会觉得有用的第四个函数是ID3DXFont::OnResetDevice。当你失去对显示设备的控制的时候(例如当用户切换到另外一个应用程序的时候),你会丢失资源,例如你的字体对象。只要你失去了资源(被错误代码所指出——查阅DX SDK文档来获取更多信息),你就得调用OnResetDevice 函数来恢复这些资源。
===============================================================================
在你走远之前,你先来看看如何创建一个字体吧!
2.8.1 Creating theFont (创建字体)
为了使用ID3DXFont对象,你必须首先使用D3DXCreateFontIndirect 函数来将它初始化:
HRESULT D3DXCreateFontIndirect( IDirect3DDevice9*pDevice, // Device to associate font to CONSTLOGFONT *pLogFont, // Structuredefining font ID3DXFont **ppFont); // Pointer to createdfont object
当心
===============================================================================
因为ID3DXFont 是一个COM对象,你始终要记得在用完它之后对其进行释放。
===============================================================================
你向这个函数提供之前初始化的设备对象以及指向你正在初始化的ID3DXFont对象的指针,但是你认为pLogFont 参数是干什么的呢?
正如你所见到的,pLogFont 指向一个LOGFONT(它代表logical font)结构体,该结构体看上去像这个样子:
typedef struct tagLOGFONT { LONG lfHeight; LONG lfWidth; LONG lfEscapement; LONG lfOrientation; LONG lfWeight; BYTE lfItalic; BYTE lfUnderline; BYTE lfStrikeOut; BYTE lfCharSet; BYTE lfOutPrecision; BYTE lfClipPrecision; BYTE lfQuality; BYTE lfPitchAndFamily; TCHAR lfFaceName[LF_FACESIZE]; } LOGFONT;
哎哟喂,好多信息啊!但是你可以安全地跳过对LOGFONT 结构体中大多数成员的设置,将它们设为我给出的默认值。(大哥,你并没有给出默认值啊!)你唯一需要改变的就是lfHeight, lfWeight, lfItalic, lfUnderline 以及lfFaceName 这些成员。
从最简单的成员开始,你可以将 lfItaic 和 lfUnderline 设为0或1来分别设定或清除对斜体或下划线的使用。你用lfWeight 来设定你在绘制时使用的粗体等级;你将它设为0来获得正常字体,或者设为700来获得粗体字。lfHeight代表字体的点尺寸(poing size)。lfHeight 的值有一点奇特,因为它本质上并不取一个直接的尺寸。相反,你必须给它一个代表它以像素计量的高度的负值。例如,对于一个有16像素高的字体,你使用-16的值。(但是对于现在的ID3DXFont对象而言,不需要再添加负号了。)
最后的是lfFaceName,它是你想使用的字体的名字。它可以是Times New Roman,Courier New,或者任何其他安装在你系统中的字体。你只需要将名字复制进lfFaceName 成员中。下面是一个使用具有16 的点尺寸的Times New Roman 字体例子:
// g_pD3DDevice = pre-initialized deviceobject // hWnd = handle to parent window ID3DXFont *pD3DFont; LOGFONT lf; // Clear out the font structure ZeroMemory(&lf, sizeof(LOGFONT)); // Set the font name and height strcpy(lf.lfFaceName, “Times New Roman”); lfHeight = -16; // Create the font object if(FAILED(D3DXCreateFontIndirect(g_pD3DDevice, &lf,&pD3DFont))) { //Error occurred }
2.8.2 Drawing with Fonts (用字体进行绘制)
一旦你的ID3DXFont对象被初始化后,你可以使用ID3DXFont::DrawText 函数来开始绘制文本了:(现在版本的DrawText函数原型与之不同,详情参见SDK文档。)
HRESULT ID3DXFont::DrawText( LPCSTR pString, // String to print INT Count, // -1 LPRECT pRect, // Area to draw text in DWORD Format, // 0 D3DCOLOR Color); // Color to use to draw with
使用DrawText 函数时唯一需要注意的是pRect 参数,它是指向一个包含了用于绘制文本的区域的RECT 结构体的指针。你可以将这个区域设为屏幕的尺寸,或者如果你想要在一个特定的区域中包含文本的话,那么就使用屏幕的坐标。RECT 结构体看起来像这个样子:
typedef struct tagRECT { LONG left; // Left coordinate LONG top; // Top coordinate LONG right; // Right coordinate LONG bottom; // Bottom coordinate } RECT;
DrawText 函数最后的参数是Color,它确定了绘制文本所使用的颜色。使用方便的D3DCOLOR_RGBA 或D3DCOLOR_COLORVALUE 宏来定义绘制文本的颜色。
哦,对了,还有一件事。你必须将你对DrawText 函数的调用夹在ID3DXFont::Begin 和ID3DXFont::End 函数之间。这两个函数告诉Direct3D 你准备渲染文本了。
下面的例子假设你已经初始化了字体对象,并准备好绘制文本:
// g_pD3DDevice = pre-initialized deviceobject // pD3DXFont = pre-initialized font object // Setup the RECT structure with drawablearea RECT rect = { 0, 0, 200, 100 }; // Begin the drawing code block if(SUCCEEDED(g_pD3DDevice->BeginScene())){ // Begin font rendering pD3DXFont->Begin(); // Draw some text pD3DXFont->DrawText(“I can draw with text!”, -1, &rect,0, D3DCOLOR_RGBA(255,255,255,255); // End font rendering pD3DXFont->End(); // End the scene g_pD3DDevice->EndScene(); }
===============================================================================
作者写了一个Font 程序来演示ID3DXFont 对象的使用。不过由于我前面的红字部分说的那些话,你是没法直接对其进行编译的。所以我就对其进行了相应的变动。这应该是目前为止我做出来的第一个本质性的改动了。由于这里没有用到顶点,所以那些跟顶点有关的例如顶点声明和绘制图元的部分就没有了;另外由于这里的变动实在是太大了,所以为了让大家看得更明白,我就没有用之前更新过的shell框架,而是在作者的代码基础上进行变动而得到。
为了更好玩,我弄了两个ID3DXFont对象,一个用来显示英文,一个用来显示中文。效果如下图所示:
有这么几个值得注意的地方:
1、 现在的DirectX 9.0c 版本里的ID3DXFont 对象没有Begin和End 函数了。
2、 现在的DirectX 9.0c 版本里的ID3DXFont 对象的DrawText 函数的参数列表与文中不同了,多了第一个参数,具体意义可以查看DirectX SDK 文档;对于我们的目的而言,将其设为0即可。
3、 关于字体的名字。首先字体可以是系统安装的任何字体,也就是C:\Windows\Font 文件夹下的任何字体。至于字体的名字,右击字体文件,在“详细信息”中找到“标题”一栏,那就是你所要用的字体的名字。
下面是代码的下载地址:
Font代码下载地址
下面继续翻译第9节:
===============================================================================
2.9 Billboards (广告牌)
广告牌技术(billboarding)是一种很酷的技术,它允许2-D物体以三维的形式出现。例如,一个如一棵树般复杂的物体可以根据一个建模程序的侧视图渲染出来,然后将它在一个矩形的多边形上以纹理的形式绘制出来。这个矩形多边形总是面朝观察点,所以不管从什么角度观察这个多边形,看上去似乎这个树纹理总是从它被渲染的那一边被观察到的(如图2.21所示)。
许多程序员使用广告牌技术来制作游戏,因为他们可以很容易实现它。使用广告牌技术的一个完美的例子可以在N64上的Paper Mario(《纸片马里奥》)游戏中看到。所有的游戏角色都是用2-D绘制成的,然后被纹理映射到多边形上。这个游戏还增添了一点花样,就是允许你在角色转身的时候看见广告牌的多边形,这样就给了图形一种很滑稽的风格。
广告牌技术是通过使用一个将多边形与视角对齐的世界矩阵而起作用的。因为你已经知道了视角(或者可以获得一个视角变换矩阵),你只需要使用相反的观察角来构造一个矩阵就行了。你不需要改变多边形的位置,因为你只关心其角度。
构造广告牌世界矩阵(你可以将之运用到一个网格模型或多边形上)的第一种方法是使用你已经知道的视角的相反值。例如,假设一个具有顶点的顶点缓存已经建立起来了。观察点的角度被储存在XRot, YRot 和ZRot 中,而广告牌物体的坐标是XCoord, YCoord 和ZCoord。下面告诉你如何建立用于渲染广告牌顶点缓存的矩阵:
// g_pD3DDevice = pre-initialized deviceobject D3DXMATRIX matBillboard; D3DXMATRIX matBBXRot, matBBYRot, matBBZRot; D3DXMATRIX matBBTrans; // Construct the billboard matrix // Use the opposite angles of the viewpointto align to view D3DXMatrixRotationX(&matBBXRot, -XRot); D3DXMatrixRotationY(&matBBYRot, -YRot); D3DXMatrixRotationZ(&matBBZRot, -ZRot); // Use the billboard object coordinates toposition D3DXMatrixTranslation(&matBBTrans,XCoord, YCoord, ZCoord); // Combine the matrices D3DXMatrixIdentity(&matBillboard); D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBTrans); D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBZRot); D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBYRot); D3DXMatrixMultiply(&matBillboard,&matBillboard, &matBBXRot); // Set the matrix g_pD3DDevice->SetTransform(D3DTS_WORLD,&matBillboard); // Continue to draw the vertex buffer,which is aligned // to face the viewport, but at the propercoordinates.
在最后一行代码之后,世界变换矩阵已经建立起来了,并且可以用来渲染广告牌物体了。
创建一个广告牌世界矩阵的第二种方法是从Direct3D 获得当前的视角变换矩阵并将其转置(取逆)。这个转置后的矩阵会正确地让所有的物体面朝观察点的。你只需要运用网格模型的平移矩阵来将其正确地摆放到你的世界中。下面告诉你如何从视角变换矩阵来构造广告牌矩阵并用之来绘制广告牌物体:(原书这里的代码有一点小错误,下面已经改正。)
// g_pD3DDevice = pre-initialized deviceobject D3DXMATRIX matTrans, matWorld, matTranspose; // Get the current Direct3D view matrix g_pD3DDevice->GetTransform(D3DTS_VIEW,&matTranspose); // Get the billboard’s rotation matrix. D3DXMatrixTranspose(&matTranspose,&matTranspose); // Create the mesh’s translation matrix D3DXMatrixTranslation(&matTrans,XCoord, YCoord, ZCoord); // Multiply them together to form worldtransformation matrix D3DXMatrixMultiply(&matWorld,&matTranspose, &matTrans); // Set the world transformation matrix g_pD3DDevice->SetTransform(D3DTS_WORLD,&matWorld); // Continue to draw the vertex buffer,which is aligned // to face the viewport, but at the proper coordinates.
广告牌是一种强大的技术;事实上,它是其他一些特殊效果——例如粒子——的基础。
===============================================================================
为了演示广告牌技术,作者写了一个Billboard 程序,效果还不错。然后我又将它改写了一下。这一次的改写的幅度也挺大的,因为用到的shader 方法已经产生了本质性的影响;这是用“龙书”第二版第12章习题3的方法写的。
下面是程序运行时的截图:
下面是代码下载地址:
Billboard代码下载地址
DirectX 9.0c游戏开发手记之RPG编程自学日志之15: Drawing with DirectX Graphics (用DirectX图形绘图)(第8-9节)