二、 Direct3D
Direct3D类对象可以说是DirectX下最重要的同时也是最复杂的对象集合。基本说来,Direct3D可以分为立即模式( Immediate Mode)和保留模式(Retained Mode)。其中保留模式是一种高级3D API编程接口,它是为需要快速开发Direct 3D应用的程序员所准备的。而立即模式是一种低级3D API编程接口,它为需要开发高性能游戏或者多媒体应用的程序员提供了在较低级别上同图形加速硬件打交道的与设备无关的接口。Direct3D的保留模式是建立在立即模式基础之上的,如下图所示:
由上面的图可以看到。Direct3D的立即模式同图形加速硬件之间的结合比较紧密,性能比较高,适合于开发三维游戏。而Direct3D的保留模式具有层次性,可移植性比较的好,可以用于开发商业的三维应用程序(例如设备展示等)。
要建立一个Direct3D应用,首先要建立Direct3D7对象,利用DirectDraw7对象的GetDirect3D方法可以获得一个Direct3D7对象。利用Direct3D7对象可以建立Direct3D对象以及设置三维环境。
利用Direct3D7对象的CreateDevice方法可以建立一个Direct3DDevive7对象,你可以将一个Direct3DDevice7对象想象为一个电影场景,你可以向场景中布置演员(3D对象)、给每个演员安排服装(纹理设置)、设置灯光(光照效果)、设置摄影机(设置视角)。
下面通过一个具体的程序来说明Direct3D立即模式的基本原理
首先建立一个新的工程并保存,为了给三维对象加上纹理,我们需要在工程目录下建立三个位图文件,文件名分别是t1.bmp、t2.bmp、t3.bmp,位图的尺寸要设置为128*128或者256*256像素,将DirectX7 说明库加入到工程文件中。然后在Form1中加入以下代码:
Option Explicit Const pi As Single = 3.141592 Const NUM_CUBE_VERTICES As Integer = 4 * 6 Dim g_vCube(NUM_CUBE_VERTICES) As D3DVERTEX '定义三个材质表面 Dim TextureSurface1 As DirectDrawSurface7 Dim TextureSurface2 As DirectDrawSurface7 Dim TextureSurface3 As DirectDrawSurface7 Dim g_dx As New DirectX7 Dim g_dd As DirectDraw7 Dim g_ddsd As DDSURFACEDESC2 Dim MainBuffer As DirectDrawSurface7 Dim BackBuffer As DirectDrawSurface7 Dim Direct3DDevice As Direct3DDevice7 Dim g_rcDest As RECT,g_rcSrc As RECT Dim ViewPortRect(0) As D3DRECT Dim bIsRunning As Boolean Dim bRoAn As Boolean Dim CNT As Single Dim iViewSize As Integer Private Sub Form_KeyDown(KeyCode As Integer,Shift As Integer) '根据不同的击键值来决定角度的变化 Select Case KeyCode Case vbKeyUp CNT = CNT + 6 bRoAn = True Case vbKeyDown CNT = CNT - 6 bRoAn = True Case vbKeyLeft CNT = CNT + 6 bRoAn = False Case vbKeyRight CNT = CNT - 6 bRoAn = False Case vbKeySubtract If iViewSize < 12 Then iViewSize = iViewSize + 1 End If Case vbKeyAdd If iViewSize > 4 Then iViewSize = iViewSize - 1 End If End Select End Sub Private Sub Form_Load() Dim j As Long InitDDraw InitD3D InitDeviceObjects Me.Show bIsRunning = True Do While bIsRunning = True RenderScene FrameMove (CNT / 360),bRoAn g_dx.GetWindowRect Me.hWnd,g_rcDest '将后台绘图平面的内容复制到前台 j = MainBuffer.Blt(g_rcDest,BackBuffer,g_rcSrc,DDBLT_WAIT) If j <> DD_OK Then MsgBox "无法将后台绘图平面的内容拷贝到前台,错误代码:" & Hex(j) End End If DoEvents Loop End Sub Private Sub FrameMove(stepVal As Single,bType As Boolean) Dim matView As D3DMATRIX Dim matTemp As D3DMATRIX '建立线形矩阵 g_dx.IdentityMatrix matView ' matView.rc11 = Cos(0.5)' matView.rc12 = Sin(0.5)' matView.rc21 = Sin(-0.5)' matView.rc22 = Cos(0.5)' matView.rc33 = 1' matView.rc43 = iviewsize'你可以尝试将下面5句注释掉而使用上面5句进行视矩阵变换,看有什么效果matView.rc11 = 1matView.rc22 = Cos(-0.5)matView.rc23 = Sin(-0.5)matView.rc32 = -Sin(-0.5)matView.rc33 = Cos(-0.5)matView.rc43 = iViewSize'对视矩阵进行角度变换Direct3DDevice.SetTransform D3DTRANSFORMSTATE_VIEW,matViewDim matWorld As D3DMATRIXg_dx.IdentityMatrix matWorldIf bType Theng_dx.RotateXMatrix matWorld,stepValElseg_dx.RotateYMatrix matWorld,stepValEnd IfDirect3DDevice.SetTransform D3DTRANSFORMSTATE_WORLD,matWorldEnd Sub'RenderScene函数执行场景重绘和渲染Private Sub RenderScene()Dim i As Integer'将整个视界背景设置为蓝色,并清除Z缓冲Direct3DDevice.Clear 1,ViewPortRect(),D3DCLEAR_TARGET,&HFF,1,0'开始绘制场景Direct3DDevice.BeginScene'将TextureSurface1设置为Direct3DDevice的纹理平面Direct3DDevice.SetTexture 0,TextureSurface1'使用TextureSurface1作为纹理绘制g_vCube(0)到g_vCube(3)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,D3DFVF_VERTEX,g_vCube(0),_4,D3DDP_DEFAULT)'使用TextureSurface1作为纹理绘制g_vCube(4)到g_vCube(7)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,g_vCube(4),D3DDP_DEFAULT)'将TextureSurface2设置为Direct3DDevice的纹理平面Direct3DDevice.SetTexture 0,TextureSurface2'使用TextureSurface2作为纹理绘制g_vCube(8)到g_vCube(11)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,g_vCube(8),D3DDP_DEFAULT)'使用TextureSurface2作为纹理绘制g_vCube(12)到g_vCube(15)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,g_vCube(12),D3DDP_DEFAULT)'将TextureSurface3设置为Direct3DDevice的纹理平面Direct3DDevice.SetTexture 0,TextureSurface3'使用TextureSurface3作为纹理绘制g_vCube(16)到g_vCube(19)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,g_vCube(16),D3DDP_DEFAULT)'使用TextureSurface3作为纹理绘制g_vCube(20)到g_vCube(23)顶点之间的平面,Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP,g_vCube(20),D3DDP_DEFAULT)'结束绘制场景Direct3DDevice.EndSceneEnd SubPrivate Sub Form_Unload(Cancel As Integer)bIsRunning = FalseEnd Sub'InitDDraw函数初始化DirectDraw对象,包括建立主绘图平面以及后台绘图平面Private Sub InitDDraw()'建立DirectDraw对象Set g_dd = g_dx.DirectDrawCreate("")'设定DirectDraw对象的协作模式g_dd.SetCooperativeLevel Me.hWnd,DDSCL_NORMAL'预先定义主绘图平面的属性g_ddsd.lFlags = DDSD_CAPSg_ddsd.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE'建立主绘图平面Set MainBuffer = g_dd.CreateSurface(g_ddsd)g_ddsd.lFlags = DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_CAPSg_ddsd.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_3DDEVICEg_dx.GetWindowRect Me.hWnd,g_rcDestg_ddsd.lWidth = g_rcDest.Right - g_rcDest.Leftg_ddsd.lHeight = g_rcDest.Bottom - g_rcDest.Top'建立后台绘图平面Set BackBuffer = g_dd.CreateSurface(g_ddsd)'将后台绘图平面的尺寸保存到g_rcSrc中With g_rcSrc.Left = 0: .Top = 0.Bottom = g_ddsd.lHeight.Right = g_ddsd.lWidthEnd WithDim pcClipper As DirectDrawClipperSet pcClipper = g_dd.CreateClipper(0)pcClipper.SetHWnd Me.hWndMainBuffer.SetClipper pcClipperEnd Sub'InitD3D函数初始化Direct3D对象,包括3D设备、光源、视角以及材质Sub InitD3D()Dim d3d As Direct3D7Dim ddsd As DDSURFACEDESC2'从DirectDraw对象中获得Direct3D对象Set d3d = g_dd.GetDirect3D'获得DirectDraw对象的显示颜色深度,如果小于16位色,则程序出错退出g_dd.GetDisplayMode ddsdIf ddsd.ddpfPixelFormat.lRGBBitCount <= 8 ThenMsgBox "本程序不支持颜色位数小于16bit的显示模式,程序将退出"EndEnd IfOn Error Resume Next'首先尝试建立硬件3维设备对象(HAL)Set Direct3DDevice = d3d.CreateDevice("IID_IDirect3DHALDevice",BackBuffer)'如果Direct3DDevice为Nothing说明显示卡不支持硬件Direct3D设备'尝试建立RGB3维设备。If Direct3DDevice Is Nothing ThenSet Direct3DDevice = d3d.CreateDevice("IID_IDirect3DRGBDevice",BackBuffer)End If'定义视角区域Dim VPDesc As D3DVIEWPORT7VPDesc.lWidth = g_rcDest.Right - g_rcDest.LeftVPDesc.lHeight = g_rcDest.Bottom - g_rcDest.TopVPDesc.minz = 0#VPDesc.maxz = 1#'设置Direct3DDevice对象的视角Direct3DDevice.SetViewport VPDesc'保存对视角的设置With ViewPortRect(0).X1 = 0: .Y1 = 0.X2 = VPDesc.lWidth.Y2 = VPDesc.lHeightEnd WithiViewSize = 4End Sub'InitDeviceObjects函数建立三维物体Private Sub InitDeviceObjects()'建立立方体的顶点数据CreateCube g_vCube'通过位图文件建立三个纹理表面Set TextureSurface1 = CreateTextureSurface("t1.bmp")Set TextureSurface2 = CreateTextureSurface("t2.bmp")Set TextureSurface3 = CreateTextureSurface("t3.bmp")'使用泛光源以及白色的普通材质Dim mtrl As D3DMATERIAL7'定义材质对光源的的反射属性,你可以尝试改变它们的值看一下材质'的反射效果mtrl.diffuse.r = 1#: mtrl.diffuse.g = 0#: mtrl.diffuse.b = 1#mtrl.Ambient.r = 1#: mtrl.Ambient.g = 1#: mtrl.Ambient.b = 1#: mtrl.Ambient.a = 1mtrl.emissive.r = 1#: mtrl.emissive.g = 0#: mtrl.emissive.b = 1#mtrl.emissive.r = 1#: mtrl.specular.g = 1#: mtrl.specular.b = 1#'将材质的清晰度设置为10mtrl.power = 10Direct3DDevice.SetMaterial mtrl'设置Direct3DDevice的光源为泛光源,你可以尝试对SetRenderState函数的'第一个参数使用不同的值,看看光源的效果。Direct3DDevice.SetRenderState D3DRENDERSTATE_AMBIENT,_g_dx.CreateColorRGBA(1#,1#,0#,1#)Dim matProj As D3DMATRIXg_dx.IdentityMatrix matProjCall g_dx.ProjectionMatrix(matProj,1000,pi / 4#)Direct3DDevice.SetTransform D3DTRANSFORMSTATE_PROJECTION,matProjEnd Sub'CreateCube函数建立立方体的顶点数据Private Sub CreateCube(vertices() As D3DVERTEX)'一个立方体有6个面,每面有是一个正方形,有4个顶点,下面共定义了'这6个面的24个顶点g_dx.CreateD3DVertex -1,-1,vertices(0)g_dx.CreateD3DVertex 1,vertices(1)g_dx.CreateD3DVertex -1,vertices(2)g_dx.CreateD3DVertex 1,vertices(3)g_dx.CreateD3DVertex -1,vertices(4)g_dx.CreateD3DVertex -1,vertices(5)g_dx.CreateD3DVertex 1,vertices(6)g_dx.CreateD3DVertex 1,vertices(7)g_dx.CreateD3DVertex -1,vertices(8)g_dx.CreateD3DVertex 1,vertices(9)g_dx.CreateD3DVertex -1,vertices(10)g_dx.CreateD3DVertex 1,vertices(11)g_dx.CreateD3DVertex -1,vertices(12)g_dx.CreateD3DVertex -1,vertices(13)g_dx.CreateD3DVertex 1,vertices(14)g_dx.CreateD3DVertex 1,vertices(15)g_dx.CreateD3DVertex 1,vertices(16)g_dx.CreateD3DVertex 1,vertices(17)g_dx.CreateD3DVertex 1,vertices(18)g_dx.CreateD3DVertex 1,vertices(19)g_dx.CreateD3DVertex -1,vertices(20)g_dx.CreateD3DVertex -1,vertices(21)g_dx.CreateD3DVertex -1,vertices(22)g_dx.CreateD3DVertex -1,vertices(23)End SubPublic Function CreateTextureSurface(sFile As String) As DirectDrawSurface7Dim ddsTexture As DirectDrawSurface7Dim i As LongDim bIsFound As BooleanDim ddsd As DDSURFACEDESC2'定义纹理平面的属性ddsd.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_PIXELFORMAT _Or DDSD_TEXTURESTAGEDim TextureEnum As Direct3DEnumPixelFormats'获得当前Direct3DDevice支持的所有纹理类型Set TextureEnum = Direct3DDevice.GetTextureFormatsEnum()'便历所有纹理类型,找到符合需要的类型For i = 1 To TextureEnum.GetCount()bIsFound = TrueCall TextureEnum.GetItem(i,ddsd.ddpfPixelFormat)With ddsd.ddpfPixelFormat'跳过不常使用的格式If .lFlags And (DDPF_LUMINANCE Or DDPF_BUMPLUMINANCE Or DDPF_BUMPDUDV) ThenbIsFound = FalseEnd If'跳过FourCC格式If .lFourCC <> 0 Then bIsFound = False'跳过Alpha模式纹理If .lFlags And DDPF_ALPHAPIXELS Then bIsFound = False'只使用16位颜色三维纹理,跳过其它的颜色设定If .lRGBBitCount <> 16 Then bIsFound = FalseEnd WithIf bIsFound Then Exit ForNext iIf Not bIsFound ThenMsgBox "你的图形卡不支持16位颜色绘图平面"EndEnd Ifddsd.ddsCaps.lCaps = DDSCAPS_TEXTUREddsd.ddsCaps.lCaps2 = DDSCAPS2_TEXTUREMANAGEddsd.lTextureStage = 0sFile = App.Path + "/" + sFile'建立一个新的纹理绘图平面Set ddsTexture = g_dd.CreateSurfaceFromFile(sFile,ddsd)'返回建立的纹理绘图平面Set CreateTextureSurface = ddsTextureEnd Function |