DIRECTX GRAPHICS C/C++教程(一)

类别:VC语言 点击:0 评论:0 推荐:
DIRECTX GRAPHICS C/C++教程

译自(c)Microsoft DirectX SDK Documentation 译者:A.R.T ,FrontFree.net

本文不是为商业目的而作;文中任何例程代码均属Microsoft版权所有。

  这一部分的教程将展示如何在应用程序中使用Microsoft Direct3D和Direct3DX。我们把所要完成的任务分为若干步骤,在某些情况下,为了清楚起见,某些步骤又被分为若干子步骤。

我们所提供的教程如下:

教程1:建立一个设备 教程2:渲染顶点 教程3:矩阵的使用 教程4:创建和使用光源 教程5:使用贴图 教程6:使用网格

注意:此教程的所有例子代码均使用c++格式书写。

 

教程1:建立一个设备

  为了能够使用Microsoft Direct3D,你首先应该建立一个应用程序窗口,然后建立和初始化Direct3D对象。你可以使用这些对象的COM接口,这些接口提供对对象的操纵,并且能够创建渲染场景所需的其他对象。此教程所基于的例程Create Device通过建立一个Direct3D设备和渲染一个简单的蓝色屏幕说明了如何完成这些工作。

此教程通过如下步骤初始化Direct3D、渲染一个场景并最终关闭程序。

步骤1:创建一个窗口 步骤2:初始化Direct3D 步骤3:响应系统信息 步骤4:渲染和显示场景 步骤5:关闭

 

步骤1:建立窗口

  所有Microsoft Windows程序开始运行时所要做的第一件事就是建立一个用以展现给用户的窗口。下列源代码展示了在主函数开始部分的对窗口的初始化。

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )

  { // Register the window class.

    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,

      GetModuleHandle(NULL), NULL, NULL, NULL, NULL,

      "D3D Tutorial", NULL };

    RegisterClassEx( &wc );

    // Create the application’s window.

    HWND hWnd = CreateWindow( "D3D Tutorial", "D3D Tutorial 01: CreateDevice",

      WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,

      GetDesktopWindow(), NULL, wc.hInstance, NULL );

  以上的代码是标准的Windows编程中的步骤。对于它们实现的功能,这里不再赘述。

  当程序的窗口准备完毕,你就可以开始设置和调整必要的Direct3D对象,这将在步骤2:初始化Direct3D中提到。

步骤2:初始化Direct3D

  例程CreateDevice中展示了当建立Windows窗口之后,在被WinMain函数所调用的InitD3D函数里对Direct3d的初始化工作。当你已经建立了一个程序窗口,你就可以开始初始化用于渲染场景的Direct3D对象了。初始化的过程包括建立一个Direct3D对象、设置参数、并最终建立Direct3D设备。

  当建立了一个Direct3D对象后,可以使用IDirect3d8::CreateDevice方法建立Direct3D设备,还可以使用Direct3D对象列举设备和各种格式等等。下列程序片断使用Direct3DCreate8()建立一个Direct3D对象。

if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) ) return E_FAIL;

  传给Direct3DCreate8的唯一一个参数永远是D3D_SDK_VERSION。

  下一步是查询当前的显示模式。所用到的函数是IDirect3D8::GetAdapterDisplayMode。用法如下:

D3DDISPLAYMODE d3ddm;

if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) ) return E_FAIL;

  在建立Direct3D设备时将用到D3DDISPLAYMODE结构的Format成员。为了在窗口模式下运行,Format成员将被用于建立一个后备缓冲,这个缓冲符合适配器的当前显示模式的设置。

  使用不同的参数填充D3DPRESENT_PARAMETERS结构,你可以指定你的3D程序将如何运转。例程中,设置Windowed成员为TRUE,SwapEffect成员为D3DSWAPEFFECT_DISCARD,而BackBufferFormat成员为d3ddm.Format.

D3DPRESENT_PARAMETERS d3dpp;

ZeroMemory( &d3dpp, sizeof(d3dpp) );

d3dpp.Windowed = TRUE;

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

d3dpp.BackBufferFormat = d3ddm.Format;

  最后的一步就是使用IDirect3D8::CreateDevice 方法来创建Direct3D 设备了,代码如下:

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &g_pd3dDevice ) ) )

  上面的程序使用D3DADAPTER_DEFAULT 建立设备,该设备使用缺省的适配器。而在大多数情况下,系统只有一个适配器,除非安装了多个图形硬件卡。如果你想优先使用硬件加速,那么使用D3DDEVTYPE_HAL 填充DeviceType 参数。 这个例子使用D3DCREATE_SOFTWARE_VERTEXPROCESSING 填充下一个参数,它表明使用软件处理顶点。注意,如果你使用D3DCREATE_HARDWARE_VERTEXPROCESSING 表明使用硬件处理顶点,你将会从支持硬件顶点处理的显卡那里得到快的多的速度。

  既然已经完成Direct3D的初始化, 下一步是确定你的程序中有一个处理系统消息的机制,这将在步骤3:响应系统信息中介绍。

步骤3:响应系统信息

  现在,你已经为渲染场景做好了准备。大多数情况下,Windows程序在他们的消息循环中处理系统消息,而当消息队列中没有任何消息时,则渲染场景。然而,CreateDevice例程是在等到一条WM_PAINT 消息之后才渲染场景。

// The message loop.

MSG msg;

while( GetMessage( &msg, NULL, 0, 0 ) )

{

  TranslateMessage( &msg );

  DispatchMessage( &msg );

}

  每次循环中,DispatchMessage 调用MsgProc来响应消息队列中的消息。当从队列中取出WM_PAINT 时,程序调用自定义函数Render重绘屏幕。然后Microsoft Win32函数 ValidateRect 被调用来使窗口中的整个用户区生效。

响应消息的函数代码如下:

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )

{

  switch( msg )

  {

    case WM_DESTROY:

    PostQuitMessage( 0 );

    return 0;

    case WM_PAINT:

    Render();

    ValidateRect( hWnd, NULL );

    return 0;

  }

  return DefWindowProc( hWnd, msg, wParam, lParam );

}

  下一步就是渲染与显示场景了。这将在步骤4:渲染和显示场景中作详细描述。

步骤4:渲染和显示场景

  为了渲染和显示场景,这一步的例程使用蓝色清除后备缓冲,传送后备缓冲到前端缓冲,并把前端缓冲的内容显示在屏幕上。

  要清除背景,使用IDirect3DDevice8::Clear 方法。

  // Clear the back buffer to a blue color

g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );

  前两个参数通知Direct3D所要清除的矩形数组的大小和地址。矩形数组描述了在渲染目标表面上将要被清除的区域。

  大部分情况下,你可以使用一个覆盖整个渲染目标表面的矩形作为要清除的矩形。把第一、二个参数分别置为0和NULL即可达到此目的。第三个参数决定了这个函数的行为,可以指定一个标志来决定清除的是渲染目标表面,深度缓冲区,复制的表面,还是以上三种的任何组合。教程没有使用深度缓冲,所以唯一的标志是D3DCLEAR_TARGET 。最后三个参数影响清除所使用的实际值,分别对应目标渲染表面, 深度缓冲和复制缓冲。例程中设置渲染目标的清除值为蓝色(D3DCOLOR_XRGB(0,0,255))。后两个参数被忽略,因为前面没有使用相应的标志 。

  设备创建完成之后,CreateDevice例程通知Direct3D即将开始渲染。渲染完成后发出结束信号,代码如下。

// Begin the scene.

g_pd3dDevice->BeginScene();

// Rendering of scene objects happens here.

// End the scene.

g_pd3dDevice->EndScene();

  在渲染开始和结束时,IDirect3DDevice8::BeginScene 和IDirect3DDevice8::EndScene方法分别向系统发送开始和结束渲染信号。你只能在这两个函数之间调用完成渲染工作的方法。即使渲染失败,你也应该在下一次调用BeginScene之前调用EndScene。

  渲染完毕后,请注意一定不要忘记使用IDirect3DDevice8::Present方法显示渲染结果,否则不会有任何输出。

g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

  前两个参数为源矩形和目的矩形。通过设置为NULL,实现将整个后备缓冲的内容般移到前端缓冲。第三个参数表明了显示的目标窗口,因为这里使用了NULL,3DPRESENT_PARAMETERS 的hWndDeviceWindow 成员将被作为默认窗口使用。第四个参数是“脏矩形”参数,大多数情况下被设置为NULL。

  最后一步是程序的关闭和退出,将在步骤5:关闭中介绍。

步骤5:关闭

  你的程序必须能够被关闭。关闭一个Microsoft DirectX程序绝不仅仅意味着毁坏程序窗口,还必须释放所有你的程序中用到的DirectX对象,并且使指向它们的指针实效。例程中当收到WM_DESTROY消息时调用自定义的Cleanup方法来实现这些操作。

VOID Cleanup()

{

  if( g_pd3dDevice != NULL)

    g_pd3dDevice->Release();

  if( g_pD3D != NULL)

    g_pD3D->Release();

}

  上面的函数使用 IUnknown::Release方法释放每一个用到的DirectX对象。因为例程遵循COM规则,绝大部分对象的统计值参数会被设为0,而且这些对象会被从内存中释放.

  除了关闭之外,当程序运行时存在一些新的可能——例如当用户改变了桌面的属性或色深——你可能需要破坏原有的Microsoft Direct3D对象并创建新的。因此一个好的方法是,保持你的cleanup 代码仅出现在一个地方 ,这样便于调用。

  此教程介绍了如何建立一个设备。教程2:渲染顶点展示了怎样使用顶点绘制几何图形。

教程2:渲染顶点

  Microsoft Direct3D程序时使用顶点来绘制几何图形。每一个3D程序都包括一个以上这样的几何图形。Vertices例程创建一个最简单的形状——一个三角形,并且把它渲染到屏幕。

这个教程展示了如何通过以下步骤创建一个三角形:

步骤1:定义顶点格式 步骤2:设置顶点缓冲 步骤3:渲染到显示

步骤1:定义顶点格式

  顶点例程使用3个顶点渲染一个2D的三角形。这里将介绍顶点缓冲的概念。顶点缓冲是Microsoft Direct3D中用来存储和渲染顶点的对象。通过指定不同的顶点结构和与之符合的顶点格式,顶点可以被以多种方式定义。例程中的顶点格式如下列源代码所示。

struct CUSTOMVERTEX

{

  FLOAT x, y, z, rhw; // The transformed position for the vertex.

  DWORD color; // The vertex color.

};

  上面的结构指明了自定义顶点样式的格式。下一步是定义FVF即自定义顶点格式,它将用于表示顶点缓冲中各顶点的内容。下面的代码定义了与上述自定义顶点格式相符合的FVF。

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE)

  可用的顶点格式标志(Flexible vertex format flags)描述自定义顶点格式所用到的顶点样式。上面的例子代码使用D3DFVF_XYZRHW 和D3DFVF_DIFFUSE 标志,这将表明上面定义顶点样式的顶点缓冲由顶点位置变化信息和颜色信息组成。

  既然自定义顶点格式和FVF已经确定,下一步就是使用顶点来填充顶点缓冲,这将在步骤2:设置顶点缓冲中介绍。

注意:例程中的顶点是已经变换好了位置的,也就是说,它们已经在2D窗口坐标中。这意味着原点位于屏幕左上角,x轴向右,y轴向下。而且对这些顶点没有使用光源。

步骤2:设置顶点缓冲

  既然已经定义了顶点格式,现在是初始化顶点的时候了。顶点例程使用自定义函数InitVB来完成此项工作。下列代码初始化三个自定义顶点的值

CUSTOMVERTEX g_Vertices[] =

{

  { 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color

  { 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, },

  { 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, },

};

  上面的代码使用一个三角形的三个顶点和它们的颜色信息填充三个自定义顶点结构对象。每一个顶点坐标的前两个值代表在二维窗口坐标系中的坐标,第三个值是深度,第四个值是颜色。

  下一步,就是调用IDirect3DDevice8::CreateVertexBuffer来建立一个顶点缓冲,代码如下:

if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3*sizeof(CUSTOMVERTEX),

0 /* Usage */, D3DFVF_CUSTOMVERTEX,

D3DPOOL_DEFAULT, &g_pVB ) ) )

return E_FAIL;

  前两个参数通知Direct3D顶点缓冲的大小和用途。下面两个参数指明新的缓冲的向量格式和内存位置产生方式。这里的向量格式是前面定义的D3DFVF_CUSTOMVERTEX。而D3DPOOL_DEFAULT 标志告诉Direct3D在最恰当的内存位置中申请缓存的空间。最后一个参数是即将建立的顶点缓冲对象的地址。

  建立完顶点缓冲后,它将被用自定义的顶点值填充。

VOID* pVertices;

if( FAILED( g_pVB->Lock( 0, sizeof(g_Vertices), (BYTE**)&pVertices, 0 ) ) )

return E_FAIL;

memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );

g_pVB->Unlock();

  顶点缓冲首先被IDirect3DVertexBuffer8::Lock锁定,第一个参数是需要锁定的数据的偏移量,为字节型。第二个参数是需锁定的顶点数据的大小,为字节型。第三个参数是一个字节型指针,将指向被锁定的顶点数据。第四个参数告诉函数如何锁定数据。

随后,使用memcpy函数将顶点值复制到顶点缓冲中。在顶点值被置入缓冲之后,调用IDirect3DVertexBuffer8::Unlock方法解锁顶点缓冲。之所以采用这种锁定——解锁机制是因为顶点缓冲有可能在设备内存区域中。

  既然顶点缓冲已由顶点值填充,下一步就是渲染和显示了。

步骤3:渲染到显示

  既然顶点缓冲已经被顶点值填充,现在是渲染和显示它的时候了。这项工作开始于清除后备缓冲为蓝色和调用BeginScene函数。

g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0L ); g_pd3dDevice->BeginScene();

  渲染顶点缓冲中的顶点数据需要几个步骤。首先,你需要设置源数据流。在这个例子中,使用0号数据流。数据流的源是由IDirect3DDevice8::SetStreamSource决定的。

g_pd3dDevice->SetStreamSource( 0, g_pVB, sizeof(CUSTOMVERTEX) );

  第一个参数告诉Direct3D数据流的源。第二个参数是将被“绑定”到数据源上的顶点缓冲。第三个参数是每一个顶点数据的大小,以字节为单位。

  下一步是通过调用IDirect3DDevice8::SetVertexShader,使Direct3D知道使用何种光影模式来渲染顶点,通常顶点光影模式就是FVF,这将使Direct3D知道它正在处理什么格式的顶点。下面的代码设置了顶点光影模式。

g_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );

  唯一的一个参数值控制着顶点光影模式。这个值可以通过IDirect3DDevice8::CreateVertexShader函数得到,也可以使用FVF。

  要得到更多关于顶点光影格式的信息,请参阅SDK中关于Vertex shaders的介绍。

  下一步是使用IDirect3DDevice8::DrawPrimitive方法来渲染顶点:

g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );

  第一个参数告诉Direct3D绘制何种图元(即图形的基本单位)。在这个例子中,使用D3DPT_TRIANGLELIST 标志表示一个三角形列表。下一个参数是三角形顶点列表中第一个将被绘制的顶点的序号。第三个参数表明要绘制的图元的个数,因为这个例子中只绘制一个三角形,所以这里是1。

  要得到更多关于3D图元的信息,请参阅SDK中关于3-D Primitives的介绍。

  最后两步,是结束场景绘制和把后备缓冲中的图像显示到前台。代码如下:

g_pd3dDevice->EndScene();

g_pd3dDevice->Present( NULL, NULL, NULL, NULL );

  当后备缓冲被显示到前台后,窗口用户区会显示一个在蓝色背景下的三角形。

  此教程展示了如何使用顶点来绘制几何图形,在教程3:矩阵的使用中将介绍矩阵的概念和用法。

教程3:矩阵的使用

  这部分教程介绍矩阵的概念和用法。在上面顶点的例程中渲染2-D的顶点来组成一个三角形。然而,在下面的这个教程中,你将在3-D空间中对顶点进行转换。另外,你还将用到矩阵来设置摄像机和视口。在3-D转换这一部分中,有关于转换矩阵用法的详细介绍。

  在矩阵例程完成渲染图形工作之前,首先调用自定义函数SetupMatrices 来建立和设置用于渲染3-D三角形的矩阵。在3-D场景中最有代表性的变换共有三种。下面的步骤分别用于建立和设置每一种变换。

步骤一:定义世界变换矩阵 步骤二:定义视变换矩阵 步骤三:定义投影变换矩阵

  要得到更多关于矩阵和变换的信息,请参阅SDK中关于矩阵的介绍。

  以上这些变换矩阵的建立顺序并不影响3-D场景中对象的布局。然而,Direct3D以以下顺序对场景应用矩阵:(1)世界变换矩阵,(2)视变换矩阵,(3)投影变换矩阵。

步骤一:定义世界变换矩阵

  世界变换矩阵定义3-D空间中模型的平移、缩放和旋转。

  以下代码对一个三角形作绕y轴的旋转,并为Direct3D设备(device)设置当前的世界变换矩阵。

D3DXMATRIX matWorld;

D3DXMatrixRotationY( &matWorld, timeGetTime()/150.0f );

g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );

  绕y轴旋转一个三角形的第一步,是调用D3DXMatrixRotationY方法。此方法的第一个参数是一个指向D3DXMATRIX结构对象的指针。这个对象将用于接受旋转操作的结果。第二个参数是要旋转的角度,以弧度为单位。

  下一步是调用 IDirect3DDevice8::SetTransform 以设置Direct3D 设备的世界旋转矩阵。Settransform的第一个参数表明设置为哪一种变换。这里的例子使用D3DTS_WORLD 宏来指定设置的是世界变换矩阵。第二个参数是一个指针,该指针指向一个矩阵,这个矩阵将要被设置为当前变换的矩阵。

  为场景定义了世界变换矩阵之后,你就可以准备定义视变换矩阵了。请再一次注意,变换定义的次序并不是严格的。然而,Direct3D以以下顺序对场景应用矩阵:(1)世界变换矩阵,(2)视变换矩阵,(3)投影变换矩阵。

  对视变换矩阵的定义,将在步骤二:定义视变换矩阵中介绍。

步骤二:定义视变换矩阵

  视变换矩阵定义了视域的位置和旋转。视矩阵相当于场景中的摄像机。

  下列代码建立视变换矩阵,并且为Direct3D设备对象设置当前的视变换。

D3DXMATRIX matView;

D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-5.0f ), &D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), &D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) );

g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

  第一步,是通过调用D3DXMatrixLookAtLH函数定义视变换矩阵。第一个参数是一个指向D3DXMATRIX结构对象的指针,该对象用于接受操作的结果。第二、三、四个参数定义了“眼点”、“朝向点”和“上”方向。在这里,眼点被设置在沿z轴负方向5个单位长度、y轴正方向3个单位长度(0,3,-5)的地方,朝向点被设为原点(即摄像机位置为(0,3,-5),镜头指向原点),而上方向被定义为y轴正方向。

  下一步是调用IDirect3DDevice8::SetTransform来为Direct3D设备对象设置视变换。Settransform的第一个参数表明设置为哪一种变换。这里的例子使用D3DTS_VIEW 宏来指定设置的是视变换矩阵。第二个参数是一个指针,该指针指向一个矩阵,这个矩阵将要被设置为当前变换的矩阵。

  当完成了定义视变换的工作后,你就可以准备定义投影变换矩阵了。

步骤三:定义投影变换矩阵

  投影变换矩阵定义了如何将几何图形从3-D空间投影到2-D视口。

  下面的代码建立投影变换矩阵,然后为Direct3D设备对象设置当前的投影变换。

D3DXMATRIX matProj;

D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );

g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );

  第一步,调用D3DXMatrixPerspectiveFovLH来设置投影矩阵。第一个参数是一个指向D3DXMATRIX结构对象的指针,这个对象用于接受操作结果。第二个参数定义视角,视角将影响物体远近与它们投影到视口以后的大小之间的关系。一个典型的视角值是1/4pi,在例子中使用的就是这个值。第三个参数是视口的长宽比,例子使用典型的1:1比例。第四和第五个参数分别定义了视口的近距离和远距离边界平面,由这些平面决定在什么距离范围之外的图形将不被渲染。例程中视口的近距离边界为1,而远距离边界为100。

  下一步,是调用IDirect3DDevice8::SetTransform来把变换应用到Direct3D设备对象。SetTransform的第一个参数通知Direct3D将被设置的变换的类型。这部分例程中使用D3DTS_PROJECTION 标志指定透视变换将被设置。 第二个参数是一个指向矩阵对象的指针,该矩阵将被用作当前变换的矩阵。

  本教程已经向你介绍了如何使用矩阵。教程4:建立和使用光源将展示怎样为你的场景添加光源。

教程4:建立和使用光源

  Direct3D 光源为3-D物体添加更多的真实感。当使用了光源后,所有的几何物体都将被赋予光照效果,这种效果是基于所使用的光源的位置和种类的。本教程中的例程将会以光源和材质为主题。

本教程通过以下的步骤建立材质和光源。

步骤一:初始化场景几何图形 步骤二:设置材质和光源

本文地址:http://com.8s8s.com/it/it4.htm