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(軟件開發(fā)套件)將是巨大的優(yōu)勢。
這些文章將在Visual Basic中發(fā)布,但實際的DirectX 8接口幾乎與C / C ++中使用的接口相同(除了明顯的語言差異),因此,如果可以在C / C ++中使用DirectX 8,則覺得這很容易。
入門
DirectX圖形都以Direct3D的名稱命名,這是從現在開始使用的術語(簡稱),但是名稱可以互換。當Direct3D變得困難時,它的確會變得非常困難。但是幸運的是,基礎知識非常簡單,一個基本應用程序可以在100左右的代碼行中建立。這樣就可以了:
首先,我們需要將DirectX 8附加到我們的VB程序中-這樣它就知道如何使用它。打開VB并創(chuàng)建一個新的“ Standard EXE”項目。單個表單應添加到項目視圖。項目菜單,然后單擊引用以顯示庫對話框。您應該在窗口中間看到一長串對象和庫,所有對象和庫都在左側帶有一個小復選框。向下滾動,直到看到一個名為“ Visual Basic類型庫的DirectX 8”的條目,選中該復選框,然后單擊“確定”。
現在,我們已將我們的項目引用到DirectX 8運行時庫中。這就是我們要在Visual Basic中使用DirectX 8功能所要做的全部。請記住,最終用戶必須在其計算機上安裝DirectX 8,才能使您的應用程序開始執(zhí)行-如果不在該位置,則程序將在啟動后立即終止。我已經為這種類型的應用程序設置了模板,因此它顯示在我的“新建項目”對話框中-您可能希望這樣做。
一個簡單的應用程序
現在我們可以使用DirectX 8,我們將建立一個非常簡單的示例-它要做的就是創(chuàng)建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”對象負責創(chuàng)建設備并枚舉其功能。最后,“ Direct3DDevice8”對象代表您的3D卡-您告訴它執(zhí)行操作,并且(在一定程度上)將執(zhí)行該操作。因此,我們創(chuàng)建了一個“ DirectX 8”對象;然后,這可以幫助我們創(chuàng)建“ Direct3D8”對象,
我們還可以創(chuàng)建其他幾個接口/對象,但是現在我們真的不需要太多了解它們-您將在使用過程中看到它們。處理這些對象時,擁有DirectX 8 SDK幫助文件的副本非常有用。雖然VB的Intellisense和對象瀏覽器非常有用,但SDK幫助文件說明并列出了每個接口/對象的所有功能。
現在我們已經定義了變量,我們可以開始對它們做一些事情了。為此,我們將創(chuàng)建一個名為“ Initialize()”的函數,該函數將按照其聲明的樣子進行操作-完成執(zhí)行后(假設沒有錯誤),我們將能夠使用所有對象并開始使事物顯示在屏幕上。
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:
它將執(zhí)行初始化代碼。然后,如果成功,它將照常進行,但是如果失敗,將進入“ 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
稍后將使用這兩個結構來幫助創(chuàng)建最終的Direct3DDevice8對象,但是現在我們要做的就是定義它們。接下來,我們創(chuàng)建DirectX 8對象-您可能完全知道定義“ Dim Dx as New DirectX 8”之類的對象是完全合法的,但這對我們想要做的事情不利。剛才提到的方法稱為“早期綁定”,我們使用的方法稱為“后期綁定”;區(qū)別在于,如果您后期綁定它,則Visual Basic在嘗試使用它時將不會檢查它是否已創(chuàng)建(但是會返回錯誤)。如果您早期綁定它,VB會在每個周圍用一條語句編譯代碼沿“如果對象什么都不是,請創(chuàng)建它”這一行調用該對象。雖然這可能只是第一次,但對于計算機而言,這仍然是一件額外的事情,而作為一款游戲,我們希望能夠獲得所有的速度,并且這些對象每秒將被使用1000次-所以您可以想象我們會浪費的速度。其次,我們使主接口創(chuàng)建通用的Direct3D接口。無論安裝的硬件可以做什么,您都將始終能夠創(chuàng)建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像素是一個不錯的尺寸。下一部分實際上涉及創(chuàng)建Direct3D設備的實例。這部分可能會有些危險。如果發(fā)送最終用戶計算機無法處理的參數,則它將失敗并導致錯誤。當您使用全屏模式并且需要選擇適合其硬件/顯示器的分辨率/色彩深度時,通常會發(fā)生這種情況。
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-僅用于開發(fā)人員的參考設備-如果每秒獲得0.25幀以上,您將很幸運。另一方面,您可以用它做任何事情-全功能支持。
D3DDEVTYPE_SW-除非您注冊了軟件渲染器(DirectX的插件),否則它將無法使用,但是Direct3D沒有捆綁任何軟件,因此您必須自己制作(非常困難)或獲得第三者。hFocusWindow As Long:這使Direct3D知道需要渲染到哪個窗口(主要用于窗口模式),因此它可以檢查它是否在其他窗口后面或是否已關閉,等等……始終傳遞[小于] FormName> .hWnd在此處,但請確保首先顯示該窗口。
行為標志已久:此設備的行為方式以及做什么(處理器和/或3D卡)。在大多數計算機上-或沒有硬件轉換和照明或沒有更好的設備(幾乎所有卡(GeForce卡除外))的計算機上,該名稱應為D3DCREATE_SOFTWARE_VERTEXPROCESSING;在這種情況下,您可以放入D3DCREATE_HARDWARE_VERTEXPROCESSING,這將強制3D卡執(zhí)行變換和照明操作;另外,您也可以使用D3DCREATE_PUREDEVICE選項-Direct3D8的新功能,僅在300+ GeForce2 Ultra芯片組上可用(無論如何在編寫時)。
PresentationParameters As D3DPRESENT_PARAMETERS:只需將我們前面填充的結構放在這里…
初始化代碼已經完成-假設代碼成功運行,那么我們將在表單上附加一個完全初始化的設備以供使用。關于DirectX錯誤的一個詞-它們始終具有描述“ Automation Error”,并且數字的負數為2百萬(例如-2001230)。如果您想知道英語的含義,則需要對照DirectX 8庫提供給我們的一組常數檢查Err.Number。如果您擁有SDK,則可以檢查每個函數可能返回的錯誤編號,并僅檢查這些錯誤編號,否則您將需要全部檢查-其中有很多!在對象瀏覽器中查找它們,它們都傾向于以“ D3DERR_”或“ E_”開頭。
在繼續(xù)討論之前,我最后要講的最后一件事是枚舉主題。您可能以前沒有聽說過-但是如果您花任何時間在DirectX中進行編程,就會變得熟悉。枚舉是分析硬件以查看其功能(或不具備)的過程。我們將在過程中滿足您的大部分需求-但在這里我需要介紹一些與設備創(chuàng)建相關的信息。
在上面的初始化代碼中,我們指定了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
目前,上述代碼僅能告訴開發(fā)人員硬件可以做什么或不能做什么。前兩行檢索我們指定設備的所有枚舉數據。如果沒有硬件設備,這里有一個簡單的錯誤處理程序,應該可以防止我們崩潰-如果是這種情況,則程序將從參考設備中獲取數據(它將支持幾乎所有內容)。現在,D3DCAPS8結構保存了評估設備及其功能所需的所有信息。如果您知道您的應用程序需要其他功能,則可以立即枚舉它們,并確定使用此設備是否有任何意義。
因此,我們的結構充滿了數據,我們現在要做的就是提取所需的信息。在這一點上,使用SDK幫助文件是個好主意-實際上,我們可以檢查數千種不同的標志和功能,并且?guī)椭募谐隽嗣總€標志和功能并附帶一個簡短的說明(并不總是那么有用) 。如果您只是想從網上閱讀教程,或者自己不花很多時間來學習,那不是太重要-大多數教程都將說明您需要執(zhí)行哪些枚舉。
在上面的示例中,我們首先檢查硬件轉換和照明功能。轉換部分與頂點操作有關,如果它是在硬件中完成的,則意味著我們可以設置一個使用D3DCREATE_HARDWARE_VERTEXPROCESSING標志的設備。然后,我們檢查是否存在純設備選項,這是從硬件轉換和照明升級的下一步(如果存在,請使用此選項)。如果返回true,則在創(chuàng)建設備時可以指定D3DCREATE_PUREDEVICE。另一方面,如果兩個都返回false,則只需要使用D3DCREATE_SOFTWARE_VERTEXPROCESSING。最后,我們檢查是否可以創(chuàng)建設備類型-我們指定設備的類型,格式以及是否處于窗口模式;如果此調用的評估結果為D3D_OK,那么我們 能夠創(chuàng)建具有相同參數的設備,如果沒有,那么我們需要找到其他一些參數-這通常只是意味著更改為“參考”設備-正如我們之前為獲取當前顯示模式所做的調用將告訴我們正確的格式,如果我們使用的是全屏模式,那么我們將列舉各種可能性(稍后會詳細介紹)-這只會留下沒有硬件設備的可能性。我們可以完全避免這種情況,只記得獲得枚舉數據時發(fā)生了什么-以及來自哪個設備。再次使用全屏模式,那么我們將列舉出可能性(稍后會詳細介紹)-這僅留下沒有硬件設備的可能性。我們可以完全避免這種情況,只記得獲得枚舉數據時發(fā)生了什么-以及來自哪個設備。再次使用全屏模式,那么我們將列舉出可能性(稍后會詳細介紹)-這僅留下沒有硬件設備的可能性。我們可以完全避免這種情況,只記得獲得枚舉數據時發(fā)生了什么-以及來自哪個設備。
主循環(huán)
接下來的部分相當快速,簡單,但對任何Direct3D應用程序都非常重要-事物的運行方式。任何關注商業(yè)游戲的人都會知道幀速率-計算機每秒更新游戲多少次;高幀率好,低幀率不好。
現在,我們需要設置我們的應用程序,使其在循環(huán)中運行;基于事件(我們僅在發(fā)生更改時才進行更新)不會在這里砍掉它-您將浪費寶貴的時間,要么無所事事,要么嘗試解決某些更改,并且最重要的是,變化。其次,永遠,永遠,永遠不會使用計時器控件或類似控件來完成這項工作-它們不準確且運行緩慢,您可能可以將它們設置為1ms,但實際上它們只能精確到50-100ms(最大幀)因此,速率為10-20fps)。
我們將改為使用循環(huán)-理論上是永無止境的循環(huán)。此循環(huán)將盡可能快地執(zhí)行,并將構成我們的幀頻的基礎-循環(huán)越快,幀頻就越高。這個循環(huán)將使用一個簡單的布爾型標志來確定它是否正在運行。一旦此變量為假,循環(huán)就會終止,我們將執(zhí)行其他操作(可能關閉應用程序)。
這是示例程序使用的代碼:
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事件中,這不是放置它的最佳位置。每當我設計一個更大的項目時,我總是將控制循環(huán)作為Sub Main()放在單獨的控制模塊中,并使表格完全為空-然后所有后續(xù)代碼進入類(一個用于圖形,實用程序,數學,音頻,物理,人工智能,輸入,文件處理等)。
第一步是確保該窗體可見,通常在此過程完成之前不會發(fā)生這種情況(在本例中,該情況要等到程序終止后才會發(fā)生)。如前所述,如果表單不可見或未加載,Direct3D將無法正常運行。
接下來,我們初始化Direct3D。我們將其返回值放置在布爾型開/關開關中(而不是我向您展示的原始方法);這樣做的好處是,如果在循環(huán)的第一遍返回false,它將自行終止。
在中間,我們有一個主循環(huán),一個Do While …循環(huán)結構。目前,它由兩個語句組成;這將是絕大多數運行時執(zhí)行的僅有兩條語句。逐幀放置要處理的所有其他調用或語句。這里的關鍵部分是在循環(huán)結束時進行DoEvents調用,如果沒有它,您的程序將很快崩潰-在大多數情況下會鎖定系統。如果此語句不在此處,我們將不會收到任何消息,輸入(鍵盤,鼠標),并且純VB語言語句可能無法正確執(zhí)行。DoEvents為系統(在本例中為Windows)提供了思考問題并執(zhí)行其認為合適的時間。如果其他程序正在運行,那么他們將有時間,如果您
最后,我們有終止代碼,如摘錄中所述,這不是必需的-DirectX庫將看到對象已被安全銷毀-但是您無法確定計算機之間的沖突,因此最好自己完成。與所有其他基于COM的接口一樣,以與創(chuàng)建它們時相反的順序銷毀它們。
Render()
在上面的主循環(huán)代碼中,您應該已經注意到,在主循環(huán)的每次遍歷中都有對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
不太復雜,但是請相信我,隨著我們開始添加新內容,它會變得越來越大。渲染過程始終遵循相同的模式-清除,繪制,在屏幕上顯示。透明部分將刪除幀緩沖區(qū)中剩下的所有內容,然后繪制所有內容-所有三角形,模型以及我們喜歡的其他內容。最終,我們更新屏幕-調用完成后,我們剛才繪制的內容將顯示在監(jiān)視器上。
隨著您對DirectX Graphics的了解越來越多,并且隨著這些文章的繼續(xù),此功能將被極大地修改和更改-它可能變得非常大而復雜。關于此過程(以及放置在主循環(huán)中的任何其他過程)要時刻牢記的一件事是,它們必須進行微調并盡可能地平滑-任何不整潔或緩慢的代碼都會對整體速度產生巨大影響您的應用程序。如果每個呼叫要花費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的頂點和面以及所有相關的材質和紋理。這些將在以后介紹。
現在您已經有了那些詞,我們可以開始做一些有趣的事情。但是,請不要對以上定義發(fā)誓-它們非常失誤,僅是提供術語的基本介紹,在必要時會提供更復雜和有意義的定義。
我要介紹的第一件事是可以使用的三種頂點類型。雖然Direct3D的結構允許100種不同的組合,但三種類型將適用于大多數情況-所有其他情況將是這三種的改編或修改。
1.未變換和未照明的頂點
這些頂點實際上只是3D空間中具有方向和一組紋理坐標的點。Direct3D將為您完成照明-您設置一些照明和一些頂點,其余的工作將由它完成。除非人們選擇使用光照貼圖或預先計算的光照(如某些商業(yè)游戲所做的那樣),否則它們通常是最常用的。
2.未變換和點亮的頂點
這些頂點是3D空間中具有紋理坐標(如第一種類型)的點,但是這些頂點具有顏色值。這使我們可以制作適當的3D幾何圖形而不必擔心照明。當我們開始制作3D幾何圖形時,它們將是第一種使用-因為它們是最簡單的。
3.轉換后的頂點
這些是二維指定的頂點-屏幕坐標。Direct3D所做的只是將紋理應用于它們,對其進行裁剪并繪制它們-您應該指定一個顏色值和一個有效的2D位置。使用這些是獲得Direct3D進行2D圖形處理的唯一方法。
還有更多的事情要記住…但是,在我們進行有關如何使用這些頂點的簡單演示之前,您還需要了解一件事。靈活的頂點格式。正如我之前提到的,Direct3D允許100種可能的頂點類型-通過此靈活頂點格式(FVF)系統,我們可以實現這一點。靈活的頂點格式描述是Long類型的變量,它是Direct3D可用于確定傳遞的數據格式的標志的組合。如果傳遞的數據無效或不正確,則使用以下兩種方法之一將會發(fā)生:什么都不會渲染,或者有些非常奇怪的東西會渲染。
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的人可能希望略微跳起來。
在下一節(jié)中,我們將擴展示例程序,以全屏模式渲染一些基本的2D和3D幾何。這聽起來似乎是一項艱巨的任務,但您已經爬上了這座山,但我的朋友卻沒有。
繪制內容-實際部分
如前所述,我們使用三角形渲染所有幾何圖形(我更喜歡使用術語三角形而不是多邊形-但可以隨意使用任何一個)。這些三角形將由一組頂點組成,并且這些頂點將具有一組特定的屬性,具體取決于它們的用途。
因此,有意義的是,我們有一個填充數據的單一頂點類型的數組-這正是我們要做的。可以想象,這將變得非常長-每個頂點一行代碼,每個三角形3行-甚至一個簡單的多維數據集也可能占用一百行代碼…這就是我要保留此代碼的原因簡單。
步驟1:進行設置。
首先,我們需要創(chuàng)建一個頂點數組:
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顏色代碼相同的代碼,這樣就可以了。
第三,更細微的是創(chuàng)建頂點的順序。這實際上非常重要-弄錯了,Direct3D會剔除(刪除)三角形而不渲染它們;如果您編寫程序但沒有呈現任何內容(但似乎已正確設置),則通常很可能是原因。如果我們按順序繪制三角形坐標,我們將看到模式-順時針:
Image7.gif
您可以設置Direct3D會剔除哪種類型的三角形(順時針,逆時針或無),但是默認情況下它將剔除逆時針的三角形,因此僅渲染那些按順時針順序排列的三角形。您可能會認為,很容易阻止它刪除任何三角形-確實如此,這是不好的做法。如果您養(yǎng)成了從一開始就以正確順序生成頂點的習慣,那就更好了;但是了解如何指定剔除模式仍然很有用:
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CW
D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_CCW
確實很簡單,無論您指定了哪個相反的選項,都將被渲染;例如,我們的三角形是按順時針方向排列的,因此您應將上面的名稱指定為D3DCULL_CCW(但這是默認設置,因此您不需要這樣做)。
在繼續(xù)之前,要注意的最后一件事是處理變形和光照的頂點。正如我告訴您的那樣,變換后的頂點和點亮的頂點是2D-X和Y,那么為什么會有Z坐標呢?Z坐標應該在0.0到1.0的比例上,并且當附加了深度緩沖區(qū)時(稍后會更多),三角形將基于此值相互繪制,例如-Z值為0的三角形在Z值為1的三角形上。具有3個不同Z值的三角形將穿過與之相交的任何其他三角形…。如果兩個三角形具有相同的Z值,則最后渲染的那個將出現在頂部。
步驟3:渲染三角形
現在,我們將回到前面討論過的Render()過程,并對其進行更新以渲染新的三角形。目前,我們沒有做任何聰明的事情,沒有紋理,沒有變換-因此一切都可以通過一個電話完成。
稍后,我們將使用一種更優(yōu)化的渲染方法以及一些更聰明的技巧。但是現在,我們將堅持以下基本原則:
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:與線列表相同,但是一條線的開頭與上一條線的結尾連接在一起-在所有指定點之間創(chuàng)建一條連續(xù)線。
D3DPT_TRIANGLELIST:每個頂點的三元組都定義了一個新的三角形-將用實心填充,并在其表面上混合顏色分量;這是我們目前使用的方法。(0-1-2和3-4-5和6-7-8-9等)。
D3DPT_TRIANGLESTRIP:與三角形列表相同,但是它創(chuàng)建了一系列三角形,所有三角形都連接在一起。從第二個三角形開始,每個三角形都使用前一個三角形的最后一個頂點作為它的第一個頂點(0-1-2和2-3-4和4-5-6等)。顏色以與三角形列表相同的方式混合在這些顏色的表面上。
D3DPT_TRIANGLEFAN:繪制一系列均連接到第一個頂點的三角形-非常適合八邊形,六邊形或圓形形狀。0-1-2&0-3-4&0-5-6等…這些三角形以純色繪制,并且在它們之間混合了顏色。
所有這些方法都有很多優(yōu)點和缺點。一些比其他更明顯。首先,您應該已經猜到使用的三角形越少,它運行的速度就越快-因此,請嘗試將其保持在一個較低的數字(不要看上去很討厭);更巧妙地,在創(chuàng)建幾何圖形時,還應將頂點數保持盡可能低。這是基于Direct3D將通過各種電纜,管道和芯片將所有數據發(fā)送到3D卡的情況,而發(fā)送的數據越多,花費的時間就越長。因此使用的頂點越少,數據傳輸速度就越快。當畫一條連續(xù)的線時,最好使用一條線帶。在處理三角形時,請確定您需要做的事情-通常,使用三角形條來渲染某些東西會更快,更簡單,其他時候,它變得更加復雜,您應該使用三角形列表;然后可以選擇對圓形對象使用三角形風扇。實驗看看…
第二個參數是圖元計數,將其視為三角形數或點數(取決于圖元類型)。在此示例中,我們僅創(chuàng)建了一個三角形,因此指定了只有1個三角形的事實。
第三個參數指定Direct3D在哪里尋找頂點數據,它必須是數組中的一個條目。通常是第一個條目-但這不是必須的;請記住,對于指定的圖元計數,需要有正確數量的頂點。
最后一個參數指定我們的頂點結構的大小(以內存字節(jié)為單位)-這是Direct3D內部使用的。您實際上并不需要了解它是如何工作的,但是基本上第三個參數指向要查找的內存的開頭,Direct3D知道將有多少個條目(原始計數值),使用此參數可以獲取結構的大小,因此可以計算出數據的所有單個位在哪里,以及整個數據應該占用多少內存。為此,在數組的第一個元素上使用Len()。
現在您可以渲染簡單的2D幾何了,您應該進行練習-嘗試使用各種方法制作一個正方形,用一個三角形扇形畫一個類似圓形的對象;并嘗試使用三角帶制作拱形或圓角矩形。如果您查看幾乎任何基本的幾何形狀,它總是會分解為一系列三角形(有時不是很好),但是借助一些基本的數學技巧,很容易編寫一種從一系列生成弧形(或其他形式)的算法三角形…
總結
以上是生活随笔為你收集整理的vb.net使用DirectX入门知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(3067):vue+eleme
- 下一篇: FFMPEG 视频图像解封装解码