?? 9-2.txt
字號:
三維化三角形
再來看看我們的程序,看起來并不是那么“三維”。而且我們所做的都能用GDI+輕易完成。So,我們應該怎樣在3維空間里繪圖,并且給人留下深刻的印象呢?實際上,簡單的修改就能達到這樣的效果。
如果你還記得,先前在我們創建第一個三角形的時候,我們使用了一個叫做“經過變換的”(transformed)坐標系統。這種坐標是顯示器的屏幕區所使用的坐標,也是最容易定義的。如果我們使用未變換過的坐標系統會怎樣呢?實際上,未變換過的坐標系統被廣泛的用于現代游戲場景。
與屏幕坐標(screem space)相比我們定義這些坐標時,還應在世界坐標(world space)里定義每一個頂點。你可以把世界坐標設想為一個無限大的三維笛卡兒坐標。你可以把你的對象放到這個“世界”的任意位置。現在來修改我們的程序,繪制一個未經過世界坐標變換的三角形。
首先使用未變換頂點格式類型中的一種來改變三角形的數據。在這里我們只關心頂點的位置,以及顏色,因此使用CustomVertex.PositionColored。
CustomVertex.positionColored[] verts = new CustomVertex. positionColored[3];
Verts[0].SetPosition(new Vector3(0.0f,1.0f,1.0f));
Verts[0].Color = System.Drawing.Color.Aqua.ToArgb();
Verts[1]`````````
Verts[2]`````````
(參見DirectX sdk Tutorial 3: Using Matrices)
同樣改變VertexFormat屬性:
device.VertexFormat = CustomVertex.PositionColored.Format;
好了,現在運行程序:什么也沒有發生,僅獲得一個填充過的窗口。在討論為什么之前,先來看看我們都作了些什么。就像你看到的,我們選擇了PositonColored結構來保存數據。這個結構用世界坐標保存了頂點的位置,也保存了它的顏色。因為為頂點是沒有變換過的,所以我們使用Vector3類來代替Vector4類,沒有變換過的頂點是沒有rhw值的。Vector3結構的成員直接映射為世界坐標系里x,y,z的值。同時,我們需要確定DirectX3D知道所做的改變,所以我們通過更新VertexFormat屬性來讓固定功能管道使用新的未變換但填充過顏色的頂點。
So,為什么程序運行時沒有正確的顯示呢?問題在于,我們只是在世界坐標里繪圖,但并沒有給DirectX3D任何關于如何來顯示它們的信息。我們需要為場景添加一個攝像機來確定如何觀看我們的頂點。在經過變換的坐標系統里不需要攝像機的原因是:DirectX3D已經知道在屏幕的哪個位置來顯示頂點。
在device上通過兩個不同的變換來控制攝像機。每一種變換都被定義為一個4×4的矩陣傳遞給DirectX3D。(???each transform is defined as a 4*4 matrix that you can pass in to DirectX3D)
映射變換定義了場景被怎樣投影到顯示器。最簡單的產生投影矩陣的方法就是使用Matrix類的PerspectiveFovLH方法。它將會使用左手坐標系創建一個正對場景的透視投影變換。(關于左右手坐標系的詳細內容請參見sdk,或你的高等數學、高等物理教材^_^)DirectX3D通常使用左手坐標系。
以下是投影函數的簽名:
public static Matrix PerspectiveFovLH( float fieldOfViewY,float aspectRatio,float znearPlane,float zfarPlane);
投影變換描繪了場景的視見體(注:即可見部分)。視見體是由可視角度和前裁剪面(Near Plane)與后裁剪面(Far Plane)定義的一個平截頭體(注:比如四棱錐橫截面與底面之間的部分,上帝保佑,你還記得高中幾何),在這個平截頭體之內的即是可見部分。函數頭里的nearPlane和farPlane兩個參數,描繪了錐體的邊界:farPlane就是錐體的底面,而nearPlane則是橫截面。fieldOfView參數描繪了錐體的角度。aspectRatio類似于電視的高寬比,比如,寬銀幕電視的高寬比是1.85。你可以用可視區域的寬度來比上高度得出這個值。DirectX3D只繪制在這個平截頭體中的物體。
既然我沒從來沒有進行過投影變換,也就根本不存在一個視見體,應此DirectX3D什么也沒有繪制。但是,就算我們進行了投影變換,我們還沒有進行包含了攝像機信息的view transform。可以用一下函數完成這個任務:
public static Matrix LookAtLH(Matrix pOut, Vector3 cameraPosition, Vector3 cameraTarget, Vector3 cameraUpVector);
僅僅通過各變量的名字你就可以知道如何使用這個函數。其中三個是用來描述攝像機的屬性:它的位置、它觀察點的位置以及一個被參考為“up”的方向。有了投影變換和view transform的幫助,DirectX3D已經有足夠的信息來繪制三角了。添加代碼:(參見DirectX sdk Tutorial 3: Using Matrices中的SetupMatrices()函數)
再運行一次試試,哦,我們已經有一個三角了,不過它完全是黑色的!問題在哪呢?在沒有經過變換的環境里,DirectX3D默認使用燈光來計算場景中幾何體每一個象素的顏色,我們沒有定義燈光,也沒有額外的光照在三角上,So,它完全是黑色的。既然我們已經為每一個點定義過了顏色,所以現在,可以安全并且簡單的把場景里的燈關了。加上如下帶碼:
dev.RenderState.Lighting = false;
再試一次,終于,我們回到了未變換坐標前的樣子。做了這么多改變到底有什么好處呢?最大的好處就是和在屏幕上直接繪制相比我們獲得了一個三維空間里的三角形——邁向偉大三維作品的第一步!^_^
既然有了三維空間里的三角,我們怎樣做才能讓他看起來確實是一個三維空間里的三角呢?最簡單的事就是讓它旋轉起來。如何來做呢?很簡單,我們只需要更改世界坐標就可以了。
Device的世界坐標變換會把每一個用局部坐標定義的頂點位置轉換為用世界坐標定義的頂點位置。(the world transform on the device is used to transform the objects being drawn from model space ,whice is where each vertex is defined with respect to the model, to world space,where each vertex is actually placed in the world.)Matrix對象的很多方法能完成這種變換:
device.Transform.World = Matrix.RotationZ( (float)Math.PI/6.0f );
它告訴DirectX3D除非指定一個新的世界坐標變換,否則在這段代碼之后所有繪制的對象都將進行這種變換。以上的世界坐標變換是根據所給的弧度旋轉x軸。注意這里的參數必須是弧度而不是角度。有規率的改變參數的值就能讓三角形平滑的轉動起來了(以下代碼略,參考sdk中的示例)。
我們旋轉的三角并不能給人留下深刻的印象。我們來試試讓他變得特別一點,并且同時旋轉多個軸。很幸運,恰好有這樣一個方法,好了,更新代碼:
device.Transform.World = Matrix.RotationAxis( new Vector3(angle/((float)Math.PI*2.0f), angle/((float)Math.PI*4.0f), angle/((float)Math.PI*6.0f)), angle/((float)Math.PI);
這里使用了RotationAxis函數,通過這個函數,我們先定義了旋轉軸,并在每一維上用一個簡單的式子不停改變軸的位置,然后再傳入三角形圍繞著軸旋轉的角度,就像我們先前做的一樣。
再次運行程序,哦,我們確實得到了一個圍繞著旋轉軸轉動的三角形,但似乎三角形會有規律的消失一陣,然后再顯示出來。好了,還記得我們先前提到的背面剔除(back face culling)嗎?這就是背面剔除在起作用的最好例子。當DirectX3D渲染物體的時候,如果它發現某一個面沒有對著攝相機,就不會繪制它,這就叫做背面剔除。那么程序在運行時,又是怎樣知道某一個特定的幾何面是否對著攝像機呢?快速的看看DirectX3D中的裁剪選項或許能給你一點提示。三種可用的剔除選項分別是:none,clockwise(順時針)以及counterclockwise(逆時針)。在clockwise以及counterclockwise的情況下,當簡單幾何體的頂點排列順序與剔除模式相反時,它就不會被繪制。
看看我們的三角形,它的頂點是按逆時針順序來排列的(注:有關頂點的排列順序,可參考sdk文檔Face and Vertex Normal Vectors)。DirectX3D默認的剔除模式就是逆時針模式。
你可以簡單在頂點集合中把第一個和第三個元素交換一下,看看會有什么不同。
現在我們知道背面剔除是怎樣工作的,很顯然,我們簡單的程序并不需要剔除功能。有一個簡單的render state來控制剔除模式,添加如下代碼:
Device.RenderStates.CullMode = Cull.None;
再一次,erying works as expected,試試拖放窗口的大小會怎樣??
添上關于投影變換的圖片一張
攝像機位于o點,視見體為ABCD--A'B'C'D',ABCD為后裁剪面,A'B'DC'D'前裁剪面
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -