二、 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 = 1 matView.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, matView Dim matWorld As D3DMATRIX g_dx.IdentityMatrix matWorld If bType Then g_dx.RotateXMatrix matWorld, stepVal Else g_dx.RotateYMatrix matWorld, stepVal End If Direct3DDevice.SetTransform D3DTRANSFORMSTATE_WORLD, matWorld End 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, D3DFVF_VERTEX, g_vCube(4), _ 4, D3DDP_DEFAULT) '将TextureSurface2设置为Direct3DDevice的纹理平面 Direct3DDevice.SetTexture 0, TextureSurface2 '使用TextureSurface2作为纹理绘制g_vCube(8)到g_vCube(11)顶点之间的平面, Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, g_vCube(8), _ 4, D3DDP_DEFAULT) '使用TextureSurface2作为纹理绘制g_vCube(12)到g_vCube(15)顶点之间的平面, Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, g_vCube(12), _ 4, D3DDP_DEFAULT) '将TextureSurface3设置为Direct3DDevice的纹理平面 Direct3DDevice.SetTexture 0, TextureSurface3 '使用TextureSurface3作为纹理绘制g_vCube(16)到g_vCube(19)顶点之间的平面, Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, g_vCube(16), _ 4, D3DDP_DEFAULT) '使用TextureSurface3作为纹理绘制g_vCube(20)到g_vCube(23)顶点之间的平面, Call Direct3DDevice.DrawPrimitive(D3DPT_TRIANGLESTRIP, D3DFVF_VERTEX, g_vCube(20), _ 4, D3DDP_DEFAULT) '结束绘制场景 Direct3DDevice.EndScene End Sub Private Sub Form_Unload(Cancel As Integer) bIsRunning = False End 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_CAPS g_ddsd.ddsCaps.lCaps = DDSCAPS_PRIMARYSURFACE '建立主绘图平面 Set MainBuffer = g_dd.CreateSurface(g_ddsd) g_ddsd.lFlags = DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_CAPS g_ddsd.ddsCaps.lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_3DDEVICE g_dx.GetWindowRect Me.hWnd, g_rcDest g_ddsd.lWidth = g_rcDest.Right - g_rcDest.Left g_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.lWidth End With Dim pcClipper As DirectDrawClipper Set pcClipper = g_dd.CreateClipper(0) pcClipper.SetHWnd Me.hWnd MainBuffer.SetClipper pcClipper End Sub 'InitD3D函数初始化Direct3D对象,包括3D设备、光源、视角以及材质 Sub InitD3D() Dim d3d As Direct3D7 Dim ddsd As DDSURFACEDESC2 '从DirectDraw对象中获得Direct3D对象 Set d3d = g_dd.GetDirect3D '获得DirectDraw对象的显示颜色深度,如果小于16位色,则程序出错退出 g_dd.GetDisplayMode ddsd If ddsd.ddpfPixelFormat.lRGBBitCount <= 8 Then MsgBox "本程序不支持颜色位数小于16bit的显示模式,程序将退出" End End If On Error Resume Next '首先尝试建立硬件3维设备对象(HAL) Set Direct3DDevice = d3d.CreateDevice("IID_IDirect3DHALDevice", BackBuffer) '如果Direct3DDevice为Nothing说明显示卡不支持硬件Direct3D设备 '尝试建立RGB3维设备。 If Direct3DDevice Is Nothing Then Set Direct3DDevice = d3d.CreateDevice("IID_IDirect3DRGBDevice", BackBuffer) End If '定义视角区域 Dim VPDesc As D3DVIEWPORT7 VPDesc.lWidth = g_rcDest.Right - g_rcDest.Left VPDesc.lHeight = g_rcDest.Bottom - g_rcDest.Top VPDesc.minz = 0# VPDesc.maxz = 1# '设置Direct3DDevice对象的视角 Direct3DDevice.SetViewport VPDesc '保存对视角的设置 With ViewPortRect(0) .X1 = 0: .Y1 = 0 .X2 = VPDesc.lWidth .Y2 = VPDesc.lHeight End With iViewSize = 4 End 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 = 1 mtrl.emissive.r = 1#: mtrl.emissive.g = 0#: mtrl.emissive.b = 1# mtrl.emissive.r = 1#: mtrl.specular.g = 1#: mtrl.specular.b = 1# '将材质的清晰度设置为10 mtrl.power = 10 Direct3DDevice.SetMaterial mtrl '设置Direct3DDevice的光源为泛光源,你可以尝试对SetRenderState函数的 '第一个参数使用不同的值,看看光源的效果。 Direct3DDevice.SetRenderState D3DRENDERSTATE_AMBIENT, _ g_dx.CreateColorRGBA(1#, 1#, 0#, 1#) Dim matProj As D3DMATRIX g_dx.IdentityMatrix matProj Call g_dx.ProjectionMatrix(matProj, 1, 1000, pi / 4#) Direct3DDevice.SetTransform D3DTRANSFORMSTATE_PROJECTION, matProj End Sub 'CreateCube函数建立立方体的顶点数据 Private Sub CreateCube(vertices() As D3DVERTEX) '一个立方体有6个面,每面有是一个正方形,有4个顶点,下面共定义了 '这6个面的24个顶点 g_dx.CreateD3DVertex -1, 1, -1, 0, 0, -1, 0, 0, vertices(0) g_dx.CreateD3DVertex 1, 1, -1, 0, 0, -1, 1, 0, vertices(1) g_dx.CreateD3DVertex -1, -1, -1, 0, 0, -1, 0, 1, vertices(2) g_dx.CreateD3DVertex 1, -1, -1, 0, 0, -1, 1, 1, vertices(3) g_dx.CreateD3DVertex -1, 1, 1, 0, 0, 1, 1, 0, vertices(4) g_dx.CreateD3DVertex -1, -1, 1, 0, 0, 1, 1, 1, vertices(5) g_dx.CreateD3DVertex 1, 1, 1, 0, 0, 1, 0, 0, vertices(6) g_dx.CreateD3DVertex 1, -1, 1, 0, 0, 1, 0, 1, vertices(7) g_dx.CreateD3DVertex -1, 1, 1, 0, 1, 0, 0, 0, vertices(8) g_dx.CreateD3DVertex 1, 1, 1, 0, 1, 0, 1, 0, vertices(9) g_dx.CreateD3DVertex -1, 1, -1, 0, 1, 0, 0, 1, vertices(10) g_dx.CreateD3DVertex 1, 1, -1, 0, 1, 0, 1, 1, vertices(11) g_dx.CreateD3DVertex -1, -1, 1, 0, -1, 0, 0, 0, vertices(12) g_dx.CreateD3DVertex -1, -1, -1, 0, -1, 0, 0, 1, vertices(13) g_dx.CreateD3DVertex 1, -1, 1, 0, -1, 0, 1, 0, vertices(14) g_dx.CreateD3DVertex 1, -1, -1, 0, -1, 0, 1, 1, vertices(15) g_dx.CreateD3DVertex 1, 1, -1, 1, 0, 0, 0, 0, vertices(16) g_dx.CreateD3DVertex 1, 1, 1, 1, 0, 0, 1, 0, vertices(17) g_dx.CreateD3DVertex 1, -1, -1, 1, 0, 0, 0, 1, vertices(18) g_dx.CreateD3DVertex 1, -1, 1, 1, 0, 0, 1, 1, vertices(19) g_dx.CreateD3DVertex -1, 1, -1, -1, 0, 0, 1, 0, vertices(20) g_dx.CreateD3DVertex -1, -1, -1, -1, 0, 0, 1, 1, vertices(21) g_dx.CreateD3DVertex -1, 1, 1, -1, 0, 0, 0, 0, vertices(22) g_dx.CreateD3DVertex -1, -1, 1, -1, 0, 0, 0, 1, vertices(23) End Sub Public Function CreateTextureSurface(sFile As String) As DirectDrawSurface7 Dim ddsTexture As DirectDrawSurface7 Dim i As Long Dim bIsFound As Boolean Dim ddsd As DDSURFACEDESC2 '定义纹理平面的属性 ddsd.lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_PIXELFORMAT _ Or DDSD_TEXTURESTAGE Dim TextureEnum As Direct3DEnumPixelFormats '获得当前Direct3DDevice支持的所有纹理类型 Set TextureEnum = Direct3DDevice.GetTextureFormatsEnum() '便历所有纹理类型,找到符合需要的类型 For i = 1 To TextureEnum.GetCount() bIsFound = True Call TextureEnum.GetItem(i, ddsd.ddpfPixelFormat) With ddsd.ddpfPixelFormat '跳过不常使用的格式 If .lFlags And (DDPF_LUMINANCE Or DDPF_BUMPLUMINANCE Or DDPF_BUMPDUDV) Then bIsFound = False End If '跳过FourCC格式 If .lFourCC <> 0 Then bIsFound = False '跳过Alpha模式纹理 If .lFlags And DDPF_ALPHAPIXELS Then bIsFound = False '只使用16位颜色三维纹理,跳过其它的颜色设定 If .lRGBBitCount <> 16 Then bIsFound = False End With If bIsFound Then Exit For Next i If Not bIsFound Then MsgBox "你的图形卡不支持16位颜色绘图平面" End End If ddsd.ddsCaps.lCaps = DDSCAPS_TEXTURE ddsd.ddsCaps.lCaps2 = DDSCAPS2_TEXTUREMANAGE ddsd.lTextureStage = 0 sFile = App.Path + "/" + sFile '建立一个新的纹理绘图平面 Set ddsTexture = g_dd.CreateSurfaceFromFile(sFile, ddsd) '返回建立的纹理绘图平面 Set CreateTextureSurface = ddsTexture End Function |
运行程序,在窗口中会出现一个带贴图的立方体,按动上下左右方向键就可以向不同的方向滚动立方体,按动+、-键可以缩放立方体。