vb.net使用DirectX入门知识

有关DirectX图形(DirectX 8的图形组件)的用法,由3部分组成的迷你系列中的第一部分。在这3篇文章中,我将介绍成为该领域的合格程序员所需的所有知识。虽然本系列文章不会涵盖所有方面的知识(这将需要3个以上的部分),但最终您将可以做大多数事情,而您做不到的任何事情都应该可以自己解决,或者阅读其他教程并轻松理解它们。

有人可能会说您不能在Visual Basic中编写适当的3D游戏。我不是要争论这个问题,但是我现在要告诉您-完全有可能使用纯Visual Basic以全3D编写中度到高级的游戏-也许不是下一个Quake / Half-Life,但这不是。并不意味着你不能做任何游戏。

为了使用DirectX进行编程,您需要做一些事情:
Visual Basic语言的常识。尽管将解释复杂的事情,但我将假定您可以编写一个相当复杂的程序。
Visual Basic 5或更高版本的副本。Visual Basic的早期版本不支持组件对象模型(COM),因此将无法使用DirectX 8。
DirectX 8的副本。运行时(从Microsoft网站或杂志CD中获得的运行时)完全可以接受。如果您认真学习和使用DirectX,则获取SDK(软件开发套件)将是巨大的优势。
这些文章将在Visual Basic中发布,但实际的DirectX 8接口几乎与C / C ++中使用的接口相同(除了明显的语言差异),因此,如果可以在C / C ++中使用DirectX 8,则觉得这很容易。

入门

DirectX图形都以Direct3D的名称命名,这是从现在开始使用的术语(简称),但是名称可以互换。当Direct3D变得困难时,它的确会变得非常困难。但是幸运的是,基础知识非常简单,一个基本应用程序可以在100左右的代码行中建立。这样就可以了:

首先,我们需要将DirectX 8附加到我们的VB程序中-这样它就知道如何使用它。打开VB并创建一个新的“ Standard EXE”项目。单个表单应添加到项目视图。项目菜单,然后单击引用以显示库对话框。您应该在窗口中间看到一长串对象和库,所有对象和库都在左侧带有一个小复选框。向下滚动,直到看到一个名为“ Visual Basic类型库的DirectX 8”的条目,选中该复选框,然后单击“确定”。

现在,我们已将我们的项目引用到DirectX 8运行时库中。这就是我们要在Visual Basic中使用DirectX 8功能所要做的全部。请记住,最终用户必须在其计算机上安装DirectX 8,才能使您的应用程序开始执行-如果不在该位置,则程序将在启动后立即终止。我已经为这种类型的应用程序设置了模板,因此它显示在我的“新建项目”对话框中-您可能希望这样做。

一个简单的应用程序

现在我们可以使用DirectX 8,我们将建立一个非常简单的示例-它要做的就是创建Direct3D实例并清除屏幕,然后终止。

我们需要做的第一件事是将一些变量放入表单的(声明)部分中:

Dim Dx As DirectX8 'The master Object, everything comes from here
Dim D3D As Direct3D8 'This controls all things 3D
Dim D3DDevice As Direct3DDevice8 'This actually represents the hardware doing the rendering
Dim bRunning As Boolean 'Controls whether the program is running or not…
这里的前3个变量(Dx,D3D,D3Ddevice)都是类-我们需要初始化它们并终止它们;第四个变量bRunning只是一个简单的Yes / No标志,它指示应用程序是否正在运行-稍后再介绍。

现在似乎是解释这些不同对象的作用的好时机。DirectX 8具有对象和接口的层次结构,每个对象和接口都有一个父对象,在这种情况下,“ DirectX 8”对象要追溯到最远。“ Direct3D8”对象负责创建设备并枚举其功能。最后,“ Direct3DDevice8”对象代表您的3D卡-您告诉它执行操作,并且(在一定程度上)将执行该操作。因此,我们创建了一个“ DirectX 8”对象;然后,这可以帮助我们创建“ Direct3D8”对象,

我们还可以创建其他几个接口/对象,但是现在我们真的不需要太多了解它们-您将在使用过程中看到它们。处理这些对象时,拥有DirectX 8 SDK帮助文件的副本非常有用。虽然VB的Intellisense和对象浏览器非常有用,但SDK帮助文件说明并列出了每个接口/对象的所有功能。

现在我们已经定义了变量,我们可以开始对它们做一些事情了。为此,我们将创建一个名为“ Initialize()”的函数,该函数将按照其声明的样子进行操作-完成执行后(假设没有错误),我们将能够使用所有对象并开始使事物显示在屏幕上。

Public Function Initialize() As Boolean
On Error GoTo ErrHandler:

Initialize = True '//We succeeded
Exit Function

ErrHandler:
Debug.Print "Error Number Returned: " & Err.Number, Err.Description
Initialize = False
End Function
上面是该函数的基本框架-您将看到大多数函数都是这样设计的。从技术上讲,Initialize()不必是一个函数,因为它不返回任何特定数据。但是我特别喜欢这种布局,因为它使我可以设计一个绝不应该降低应用程序其余部分的功能-如果失败,它所做的就是将false返回给任何调用它的人。调用此函数时,我们应使用如下代码:

If Not (Initialize() = True) Then GoTo Error_Handler:
它将执行初始化代码。然后,如果成功,它将照常进行,但是如果失败,将进入“ Error_Handler”进行处理/更正。上面的调用只是以下内容的简化(更易于阅读)版本:

If Initialize() = False Then
GoTo Error_Handler:
End If
现在我们已经布局了基本的函数结构,我们将在其中添加一些内容。我们需要做的第一件事是定义两个结构并初始化Direct3D对象:

Dim DispMode As D3DDISPLAYMODE '//Describes our Display Mode
Dim D3DWindow As D3DPRESENT_PARAMETERS '//Describes our Viewport

Set Dx = New DirectX8 '//Create our Master Object
Set D3D = Dx.Direct3DCreate() '//Let our Master Object create the Direct3D Interface
稍后将使用这两个结构来帮助创建最终的Direct3DDevice8对象,但是现在我们要做的就是定义它们。接下来,我们创建DirectX 8对象-您可能完全知道定义“ Dim Dx as New DirectX 8”之类的对象是完全合法的,但这对我们想要做的事情不利。刚才提到的方法称为“早期绑定”,我们使用的方法称为“后期绑定”;区别在于,如果您后期绑定它,则Visual Basic在尝试使用它时将不会检查它是否已创建(但是会返回错误)。如果您早期绑定它,VB会在每个周围用一条语句编译代码沿“如果对象什么都不是,请创建它”这一行调用该对象。虽然这可能只是第一次,但对于计算机而言,这仍然是一件额外的事情,而作为一款游戏,我们希望能够获得所有的速度,并且这些对象每秒将被使用1000次-所以您可以想象我们会浪费的速度。其次,我们使主接口创建通用的Direct3D接口。无论安装的硬件可以做什么,您都将始终能够创建Direct3D接口。现在我们需要填写刚才定义的两个结构:

D3D.GetAdapterDisplayMode D3DADAPTER_DEFAULT, DispMode '//Retrieve the current display Mode

D3DWindow.Windowed = 1 '//Tell it we’re using Windowed Mode
D3DWindow.SwapEffect = D3DSWAPEFFECT_COPY_VSYNC '//We’ll refresh when the monitor does
D3DWindow.BackBufferFormat = DispMode.Format '//We’ll use the format we just retrieved…
确实不是太复杂-但是如果我们要使用全屏渲染(稍后介绍),它将变得更加复杂。尽管此示例确实无关紧要,但如果您使用窗口模式,则最好将窗口保持较小-对于大多数分辨率而言,400x300像素是一个不错的尺寸。下一部分实际上涉及创建Direct3D设备的实例。这部分可能会有些危险。如果发送最终用户计算机无法处理的参数,则它将失败并导致错误。当您使用全屏模式并且需要选择适合其硬件/显示器的分辨率/色彩深度时,通常会发生这种情况。

Set D3DDevice = D3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _
frmMain.hWnd, _
D3DCREATE_SOFTWARE_VERTEXPROCESSING, _
D3DWindow)
实际的代码并不太复杂,但这就是参数中要包含的内容。参数如下:

适配器长:虽然他们似乎没有用主/从设备制作3D卡,但可以在其中进行更改。几乎所有图形卡都将在默认适配器(D3DADAPTER_DEFAULT)上。将其设置为0或1,否则将

DeviceType设置为Const_D3DDEVTYPE:我们要使用哪种类型的设备;默认为0 。设备有2种主要类型,第3种是可选设备:
D3DDEVTYPE_HAL-硬件加速,其中实际的3D卡进行渲染
D3DDEVTYPE_REF-仅用于开发人员的参考设备-如果每秒获得0.25帧以上,您将很幸运。另一方面,您可以用它做任何事情-全功能支持。
D3DDEVTYPE_SW-除非您注册了软件渲染器(DirectX的插件),否则它将无法使用,但是Direct3D没有捆绑任何软件,因此您必须自己制作(非常困难)或获得第三者。hFocusWindow As Long:这使Direct3D知道需要渲染到哪个窗口(主要用于窗口模式),因此它可以检查它是否在其他窗口后面或是否已关闭,等等……始终传递[小于] FormName> .hWnd在此处,但请确保首先显示该窗口。

行为标志已久:此设备的行为方式以及做什么(处理器和/或3D卡)。在大多数计算机上-或没有硬件转换和照明或没有更好的设备(几乎所有卡(GeForce卡除外))的计算机上,该名称应为D3DCREATE_SOFTWARE_VERTEXPROCESSING;在这种情况下,您可以放入D3DCREATE_HARDWARE_VERTEXPROCESSING,这将强制3D卡执行变换和照明操作;另外,您也可以使用D3DCREATE_PUREDEVICE选项-Direct3D8的新功能,仅在300+ GeForce2 Ultra芯片组上可用(无论如何在编写时)。

PresentationParameters As D3DPRESENT_PARAMETERS:只需将我们前面填充的结构放在这里…

初始化代码已经完成-假设代码成功运行,那么我们将在表单上附加一个完全初始化的设备以供使用。关于DirectX错误的一个词-它们始终具有描述“ Automation Error”,并且数字的负数为2百万(例如-2001230)。如果您想知道英语的含义,则需要对照DirectX 8库提供给我们的一组常数检查Err.Number。如果您拥有SDK,则可以检查每个函数可能返回的错误编号,并仅检查这些错误编号,否则您将需要全部检查-其中有很多!在对象浏览器中查找它们,它们都倾向于以“ D3DERR_”或“ E_”开头。

在继续讨论之前,我最后要讲的最后一件事是枚举主题。您可能以前没有听说过-但是如果您花任何时间在DirectX中进行编程,就会变得熟悉。枚举是分析硬件以查看其功能(或不具备)的过程。我们将在过程中满足您的大部分需求-但在这里我需要介绍一些与设备创建相关的信息。

在上面的初始化代码中,我们指定了D3DDEVTYPE_HAL,该D3DDEVTYPE_HAL在主机上可能会或可能不可用,并且如果要跳到全屏模式,我们将需要知道硬件也支持什么分辨率和颜色深度。尽管软件顶点处理可在所有计算机上运行,​​但最好利用可用的任何其他硬件功能。为此,我们使用以下代码:

Dim DevCaps As D3DCAPS8
On Local Error Resume Next
D3D.GetDeviceCaps D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, DevCaps
If Err.Number = D3DERR_INVALIDDEVICE Then
'We couldn’t get data from the hardware device - probably doesn’t exist…
D3D.GetDeviceCaps D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, DevCaps
Err.Clear '//Remove the error value…
End If

'//For Hardware vertex processing:
If (DevCaps.DevCaps And D3DDEVCAPS_HWTRANSFORMANDLIGHT) Then
Debug.Print “Hardware Transform and lighting supported”
Else
Debug.Print “Hardware Transform and lighting is not supported”
End If

'//For Pure Device processing:
If (DevCaps.DevCaps And D3DDEVCAPS_PUREDEVICE) Then
Debug.Print “Pure Device is supported.”
Else
Debug.Print “Pure device is not supported”
End If

'//To check the rest we use:
If D3D.CheckDeviceType(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
DispMode.Format, DispMode.Format, 1) = D3D_OK Then
Debug.Print “The selected device format is acceptable”
Else
Debug.Print “The selected device format is not acceptable”
End If
目前,上述代码仅能告诉开发人员硬件可以做什么或不能做什么。前两行检索我们指定设备的所有枚举数据。如果没有硬件设备,这里有一个简单的错误处理程序,应该可以防止我们崩溃-如果是这种情况,则程序将从参考设备中获取数据(它将支持几乎所有内容)。现在,D3DCAPS8结构保存了评估设备及其功能所需的所有信息。如果您知道您的应用程序需要其他功能,则可以立即枚举它们,并确定使用此设备是否有任何意义。

因此,我们的结构充满了数据,我们现在要做的就是提取所需的信息。在这一点上,使用SDK帮助文件是个好主意-实际上,我们可以检查数千种不同的标志和功能,并且帮助文件列出了每个标志和功能并附带一个简短的说明(并不总是那么有用) 。如果您只是想从网上阅读教程,或者自己不花很多时间来学习,那不是太重要-大多数教程都将说明您需要执行哪些枚举。

在上面的示例中,我们首先检查硬件转换和照明功能。转换部分与顶点操作有关,如果它是在硬件中完成的,则意味着我们可以设置一个使用D3DCREATE_HARDWARE_VERTEXPROCESSING标志的设备。然后,我们检查是否存在纯设备选项,这是从硬件转换和照明升级的下一步(如果存在,请使用此选项)。如果返回true,则在创建设备时可以指定D3DCREATE_PUREDEVICE。另一方面,如果两个都返回false,则只需要使用D3DCREATE_SOFTWARE_VERTEXPROCESSING。最后,我们检查是否可以创建设备类型-我们指定设备的类型,格式以及是否处于窗口模式;如果此调用的评估结果为D3D_OK,那么我们 能够创建具有相同参数的设备,如果没有,那么我们需要找到其他一些参数-这通常只是意味着更改为“参考”设备-正如我们之前为获取当前显示模式所做的调用将告诉我们正确的格式,如果我们使用的是全屏模式,那么我们将列举各种可能性(稍后会详细介绍)-这只会留下没有硬件设备的可能性。我们可以完全避免这种情况,只记得获得枚举数据时发生了什么-以及来自哪个设备。再次使用全屏模式,那么我们将列举出可能性(稍后会详细介绍)-这仅留下没有硬件设备的可能性。我们可以完全避免这种情况,只记得获得枚举数据时发生了什么-以及来自哪个设备。再次使用全屏模式,那么我们将列举出可能性(稍后会详细介绍)-这仅留下没有硬件设备的可能性。我们可以完全避免这种情况,只记得获得枚举数据时发生了什么-以及来自哪个设备。

主循环

接下来的部分相当快速,简单,但对任何Direct3D应用程序都非常重要-事物的运行方式。任何关注商业游戏的人都会知道帧速率-计算机每秒更新游戏多少次;高帧率好,低帧率不好。

现在,我们需要设置我们的应用程序,使其在循环中运行;基于事件(我们仅在发生更改时才进行更新)不会在这里砍掉它-您将浪费宝贵的时间,要么无所事事,要么尝试解决某些更改,并且最重要的是,变化。其次,永远,永远,永远不会使用计时器控件或类似控件来完成这项工作-它们不准确且运行缓慢,您可能可以将它们设置为1ms,但实际上它们只能精确到50-100ms(最大帧)因此,速率为10-20fps)。

我们将改为使用循环-理论上是永无止境的循环。此循环将尽可能快地执行,并将构成我们的帧频的基础-循环越快,帧频就越高。这个循环将使用一个简单的布尔型标志来确定它是否正在运行。一旦此变量为假,循环就会终止,我们将执行其他操作(可能关闭应用程序)。

这是示例程序使用的代码:

Private Sub Form_Load()

Me.Show '//Make sure our window is visible

bRunning = Initialize()
'So you can see what happens…
Debug.Print "Device Creation Return Code : ", bRunning

Do While bRunning
Render '//Update the frame…
DoEvents '//Allow windows time to think; otherwise you’ll
'//get into a really tight (and bad) loop…

Loop '//Begin the next frame…

'//If we’ve gotten to this point the loop must have been terminated
’ So we need to clean up after ourselves. This isn’t essential, but it’s
’ good coding practise.

On Error Resume Next 'If the objects were never created;
’ (the initialization failed) we might get an
’ error when freeing them… which we need to
’ handle, but as we’re closing anyway…
Set D3DDevice = Nothing
Set D3D = Nothing
Set Dx = Nothing
Debug.Print “All Objects Destroyed”

'//Final termination:
Unload Me
End
End Sub
正如您可以立即看到的那样,该代码全部位于Form_Load事件中,这不是放置它的最佳位置。每当我设计一个更大的项目时,我总是将控制循环作为Sub Main()放在单独的控制模块中,并使表格完全为空-然后所有后续代码进入类(一个用于图形,实用程序,数学,音频,物理,人工智能,输入,文件处理等)。

第一步是确保该窗体可见,通常在此过程完成之前不会发生这种情况(在本例中,该情况要等到程序终止后才会发生)。如前所述,如果表单不可见或未加载,Direct3D将无法正常运行。

接下来,我们初始化Direct3D。我们将其返回值放置在布尔型开/关开关中(而不是我向您展示的原始方法);这样做的好处是,如果在循环的第一遍返回false,它将自行终止。

在中间,我们有一个主循环,一个Do While …循环结构。目前,它由两个语句组成;这将是绝大多数运行时执行的仅有两条语句。逐帧放置要处理的所有其他调用或语句。这里的关键部分是在循环结束时进行DoEvents调用,如果没有它,您的程序将很快崩溃-在大多数情况下会锁定系统。如果此语句不在此处,我们将不会收到任何消息,输入(键盘,鼠标),并且纯VB语言语句可能无法正确执行。DoEvents为系统(在本例中为Windows)提供了思考问题并执行其认为合适的时间。如果其他程序正在运行,那么他们将有时间,如果您

最后,我们有终止代码,如摘录中所述,这不是必需的-DirectX库将看到对象已被安全销毁-但是您无法确定计算机之间的冲突,因此最好自己完成。与所有其他基于COM的接口一样,以与创建它们时相反的顺序销毁它们。

Render()

在上面的主循环代码中,您应该已经注意到,在主循环的每次遍历中都有对Render()函数的调用。正是这段代码实际在屏幕上显示了图形-并处理了与图形显示方式有关的所有内容。

对于此示例,此代码不会做任何令人兴奋的事情-毕竟这是我们的第一个DirectX Graphics应用程序。接下来的几篇文章将解释更有趣的部分。

Public Sub Render()
'//1. We need to clear the render device before we can draw anything
’ This must always happen before you start rendering stuff…
D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET, &HCCCCFF, 1#, 0
'the hexidecimal value in the middle is the same as when you’re using
’ colours in HTML - if you’re familiar with that.

'//2. Next we would render everything. This example doesn’t do this,
’ but if it did it’d look something like this:

D3DDevice.BeginScene
'All rendering calls go between these two lines
D3DDevice.EndScene

'//3. Update the frame to the screen…
’ This is the same as the Primary.Flip method as used in DirectX 7
’ These values below should work for almost all cases…
D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
End Sub
不太复杂,但是请相信我,随着我们开始添加新内容,它会变得越来越大。渲染过程始终遵循相同的模式-清除,绘制,在屏幕上显示。透明部分将删除帧缓冲区中剩下的所有内容,然后绘制所有内容-所有三角形,模型以及我们喜欢的其他内容。最终,我们更新屏幕-调用完成后,我们刚才绘制的内容将显示在监视器上。

随着您对DirectX Graphics的了解越来越多,并且随着这些文章的继续,此功能将被极大地修改和更改-它可能变得非常大而复杂。关于此过程(以及放置在主循环中的任何其他过程)要时刻牢记的一件事是,它们必须进行微调并尽可能地平滑-任何不整洁或缓慢的代码都会对整体速度产生巨大影响您的应用程序。如果每个呼叫要花费6毫秒来处理,但是您要做的事情花费了10毫秒,则帧速率将从167fps下降到100fps-仍然相当快,但是假设您还有其他事情要做(AI,音频,物理和一般游戏性) ),那么此下降幅度将更大。

画画-理论

好的,您已经学会了如何在Visual Basic中初始化Direct3D应用程序-哇。几乎没有尖端的视觉效果,也完全没有用。因此,我很确定您想学习如何绘画。

掌握Direct3D的过程非常简单,但是在达到这一点之前,它需要大量的工作和内存。第一部分是理解不同单词的含义-现在,我们将坚持简单的定义,并在稍后进行更高级的介绍时对其进行详细说明。

1.顶点(多个顶点)
顶点可以视为定义点-三角形,正方形或其他形状的角。使用顶点,我们可以构造具有各种属性的2D和3D形状。顶点将由Visual Basic用户定义类型(我们将在后面介绍)描述,并且通常由位置,颜色和纹理坐标组成。

2.多边形
多边形通常是大多数“普通”人会听到的东西-如此,3D卡每秒就会抽出1.01亿个多边形(或其他)。实际上,Direct3D使用三角形渲染了所有图元。但是话又说回来,三角形是最简单的多边形。三角形列表存储为您使用的顶点类型的数组。

3.脸
一个面通常是3个以某种多边形排列的顶点,但是它也有一个方向-您可以知道它面对的是哪种方式。Direct3D会在整个面上插值顶点分量,尤其是颜色-稍后我们将看到。3D模型(从3D建模程序中导入)通常由100或1000个面组成。

4.纹理
将纹理应用于三角形以使其看起来更真实,纹理只是从硬盘驱动器加载到内存中的2D位图/图片,然后在渲染过程中映射到相关的多边形。稍后我们将更详细地介绍这些内容。

5.网格
网格是模型的另一种说法-网格通常由程序中的一个对象表示,包含100或1000的顶点和面以及所有相关的材质和纹理。这些将在以后介绍。

现在您已经有了那些词,我们可以开始做一些有趣的事情。但是,请不要对以上定义发誓-它们非常失误,仅是提供术语的基本介绍,在必要时会提供更复杂和有意义的定义。

我要介绍的第一件事是可以使用的三种顶点类型。虽然Direct3D的结构允许100种不同的组合,但三种类型将适用于大多数情况-所有其他情况将是这三种的改编或修改。

1.未变换和未照明的顶点
这些顶点实际上只是3D空间中具有方向和一组纹理坐标的点。Direct3D将为您完成照明-您设置一些照明和一些顶点,其余的工作将由它完成。除非人们选择使用光照贴图或预先计算的光照(如某些商业游戏所做的那样),否则它们通常是最常用的。

2.未变换和点亮的顶点
这些顶点是3D空间中具有纹理坐标(如第一种类型)的点,但是这些顶点具有颜色值。这使我们可以制作适当的3D几何图形而不必担心照明。当我们开始制作3D几何图形时,它们将是第一种使用-因为它们是最简单的。

3.转换后的顶点
这些是二维指定的顶点-屏幕坐标。Direct3D所做的只是将纹理应用于它们,对其进行裁剪并绘制它们-您应该指定一个颜色值和一个有效的2D位置。使用这些是获得Direct3D进行2D图形处理的唯一方法。

还有更多的事情要记住…但是,在我们进行有关如何使用这些顶点的简单演示之前,您还需要了解一件事。灵活的顶点格式。正如我之前提到的,Direct3D允许100种可能的顶点类型-通过此灵活顶点格式(FVF)系统,我们可以实现这一点。灵活的顶点格式描述是Long类型的变量,它是Direct3D可用于确定传递的数据格式的标志的组合。如果传递的数据无效或不正确,则使用以下两种方法之一将会发生:什么都不会渲染,或者有些非常奇怪的东西会渲染。

3种主要类型的顶点格式如下:

Const FVF_TLVERTEX = (D3DFVF_XYZRHW Or D3DFVF_TEX1 Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR)
Const FVF_LVERTEX = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_SPECULAR Or D3DFVF_TEX1)
Const FVF_VERTEX = (D3DFVF_XYZ Or D3DFVF_NORMAL Or D3DFVF_TEX1)
他们的UDT结构将如下所示:

Private Type TLVERTEX
X As Single
Y As Single
Z As Single
rhw As Single
color As Long
Specular As Long
tu As Single
tv As Single
End Type

Private Type LITVERTEX
X As Single
Y As Single
Z As Single
color As Long
Specular As Long
tu As Single
tv As Single
End Type

Private Type VERTEX
X As Single
Y As Single
Z As Single
nx As Single
ny As Single
nz As Single
tu As Single
tv As Single
End Type
此时此刻,您实际上并不需要了解所有这些信息-我将它们主要用于参考目的;那些熟悉Visual Basic中的DirectX7或另一种语言的DirectX的人可能希望略微跳起来。

在下一节中,我们将扩展示例程序,以全屏模式渲染一些基本的2D和3D几何。这听起来似乎是一项艰巨的任务,但您已经爬上了这座山,但我的朋友却没有。

绘制内容-实际部分

如前所述,我们使用三角形渲染所有几何图形(我更喜欢使用术语三角形而不是多边形-但可以随意使用任何一个)。这些三角形将由一组顶点组成,并且这些顶点将具有一组特定的属性,具体取决于它们的用途。

因此,有意义的是,我们有一个填充数据的单一顶点类型的数组-这正是我们要做的。可以想象,这将变得非常长-每个顶点一行代码,每个三角形3行-甚至一个简单的多维数据集也可能占用一百行代码…这就是我要保留此代码的原因简单。

步骤1:进行设置。

首先,我们需要创建一个顶点数组:

Dim TriVert(0 To 2) As TLVERTEX '//we require 3 vertices to make a triangle…
然后,我们需要在初始化过程中设置几个参数:

D3DDevice.SetVertexShader FVF_TLVERTEX
D3DDevice.SetRenderState D3DRS_LIGHTING, 0
第一行告诉渲染设备我们将使用什么类型的顶点-传递我们在前面定义的常量。第二个参数告诉Direct3D我们不希望它进行照明-默认情况下会。

第2步:制作三角形

这就像用所需数据填充数组一样简单,为清楚起见,我们将其粘贴在一个全新的过程中,该过程将在初始化过程结束时调用:

Private Sub InitializeGeometry()
TriVert(0) = CreateTLVertex(0, 0, 0, 1, &HFF0000, 0, 0, 0)
TriVert(1) = CreateTLVertex(175, 0, 0, 1, &HFF00&, 0, 0, 0)
TriVert(2) = CreateTLVertex(0, 175, 0, 1, &HFF&, 0, 0, 0)
End Sub
这里要注意三件事:首先我们在这里有了一个新函数-CreateTLVertex(),这是我编写的一个小辅助函数,用于帮助用相关数据填充结构,它看起来像这样:

Private Function CreateTLVertex(X As Single, Y As Single, Z As Single, _
rhw As Single, Color As Long, _
Specular As Long, tu As Single, _
tv As Single) As TLVERTEX
CreateTLVertex.X = X
CreateTLVertex.Y = Y
CreateTLVertex.Z = Z
CreateTLVertex.rhw = rhw
CreateTLVertex.Color = Color
CreateTLVertex.Specular = Specular
CreateTLVertex.tu = tu
CreateTLVertex.tv = tv
End Function
其次,我们必须将颜色指定为长整数,使用RGB()函数在此处无法正常工作-可以反转并使用它的值-RGB(B,G,R),但是如果您可以使用十六进制,那就是首选方法。可以将其视为与HTML颜色代码相同的代码,这样就可以了。

第三,更细微的是创建顶点的顺序。这实际上非常重要-弄错了,Direct3D会剔除(删除)三角形而不渲染它们;如果您编写程序但没有呈现任何内容(但似乎已正确设置),则通常很可能是原因。如果我们按顺序绘制三角形坐标,我们将看到模式-顺时针:

Image7.gif

您可以设置Direct3D会剔除哪种类型的三角形(顺时针,逆时针或无),但是默认情况下它将剔除逆时针的三角形,因此仅渲染那些按顺时针顺序排列的三角形。您可能会认为,很容易阻止它删除任何三角形-确实如此,这是不好的做法。如果您养成了从一开始就以正确顺序生成顶点的习惯,那就更好了;但是了解如何指定剔除模式仍然很有用:

D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW
确实很简单,无论您指定了哪个相反的选项,都将被渲染;例如,我们的三角形是按顺时针方向排列的,因此您应将上面的名称指定为D3DCULL_CCW(但这是默认设置,因此您不需要这样做)。

在继续之前,要注意的最后一件事是处理变形和光照的顶点。正如我告诉您的那样,变换后的顶点和点亮的顶点是2D-X和Y,那么为什么会有Z坐标呢?Z坐标应该在0.0到1.0的比例上,并且当附加了深度缓冲区时(稍后会更多),三角形将基于此值相互绘制,例如-Z值为0的三角形在Z值为1的三角形上。具有3个不同Z值的三角形将穿过与之相交的任何其他三角形…。如果两个三角形具有相同的Z值,则最后渲染的那个将出现在顶部。

步骤3:渲染三角形

现在,我们将回到前面讨论过的Render()过程,并对其进行更新以渲染新的三角形。目前,我们没有做任何聪明的事情,没有纹理,没有变换-因此一切都可以通过一个电话完成。

稍后,我们将使用一种更优化的渲染方法以及一些更聪明的技巧。但是现在,我们将坚持以下基本原则:

D3DDevice.BeginScene
'All rendering calls go between these two lines
D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, 1, TriVert(0), Len(TriVert(0))

D3DDevice.EndScene
就这么简单。我们使用函数“ DrawPrimativeUP”直接从内存中渲染自定义类型的顶点-它的类型为TRIANGLELIST(多于一秒钟),由一个三角形组成,并使用具有指定大小的指定数组。更详细地讲:

第一个参数是基本类型,而Direct3D会将所有实体渲染都绘制为三角形,但是我们也可以渲染点和线。此处列出了所有选项的全部范围:

D3DPT_POINTLIST: Direct3D将每个顶点绘制为3D空间中的未连接点,而在屏幕上只是一个像素点。对于粒子效果很有用(但有更好的方法)。

D3DPT_LINELIST:每对条目为Direct3D指定一对坐标以在(0&1,2&3,4&5等之间画一条线…

D3DPT_LINESTRIP:与线列表相同,但是一条线的开头与上一条线的结尾连接在一起-在所有指定点之间创建一条连续线。

D3DPT_TRIANGLELIST:每个顶点的三元组都定义了一个新的三角形-将用实心填充,并在其表面上混合颜色分量;这是我们目前使用的方法。(0-1-2和3-4-5和6-7-8-9等)。

D3DPT_TRIANGLESTRIP:与三角形列表相同,但是它创建了一系列三角形,所有三角形都连接在一起。从第二个三角形开始,每个三角形都使用前一个三角形的最后一个顶点作为它的第一个顶点(0-1-2和2-3-4和4-5-6等)。颜色以与三角形列表相同的方式混合在这些颜色的表面上。

D3DPT_TRIANGLEFAN:绘制一系列均连接到第一个顶点的三角形-非常适合八边形,六边形或圆形形状。0-1-2&0-3-4&0-5-6等…这些三角形以纯色绘制,并且在它们之间混合了颜色。

所有这些方法都有很多优点和缺点。一些比其他更明显。首先,您应该已经猜到使用的三角形越少,它运行的速度就越快-因此,请尝试将其保持在一个较低的数字(不要看上去很讨厌);更巧妙地,在创建几何图形时,还应将顶点数保持尽可能低。这是基于Direct3D将通过各种电缆,管道和芯片将所有数据发送到3D卡的情况,而发送的数据越多,花费的时间就越长。因此使用的顶点越少,数据传输速度就越快。当画一条连续的线时,最好使用一条线带。在处理三角形时,请确定您需要做的事情-通常,使用三角形条来渲染某些东西会更快,更简单,其他时候,它变得更加复杂,您应该使用三角形列表;然后可以选择对圆形对象使用三角形风扇。实验看看…

第二个参数是图元计数,将其视为三角形数或点数(取决于图元类型)。在此示例中,我们仅创建了一个三角形,因此指定了只有1个三角形的事实。

第三个参数指定Direct3D在哪里寻找顶点数据,它必须是数组中的一个条目。通常是第一个条目-但这不是必须的;请记住,对于指定的图元计数,需要有正确数量的顶点。

最后一个参数指定我们的顶点结构的大小(以内存字节为单位)-这是Direct3D内部使用的。您实际上并不需要了解它是如何工作的,但是基本上第三个参数指向要查找的内存的开头,Direct3D知道将有多少个条目(原始计数值),使用此参数可以获取结构的大小,因此可以计算出数据的所有单个位在哪里,以及整个数据应该占用多少内存。为此,在数组的第一个元素上使用Len()。

现在您可以渲染简单的2D几何了,您应该进行练习-尝试使用各种方法制作一个正方形,用一个三角形扇形画一个类似圆形的对象;并尝试使用三角带制作拱形或圆角矩形。如果您查看几乎任何基本的几何形状,它总是会分解为一系列三角形(有时不是很好),但是借助一些基本的数学技巧,很容易编写一种从一系列生成弧形(或其他形式)的算法三角形…

你可能感兴趣的:(vb.net,工业自动化,directx,vb.net,算法)