這個系列我想用來運用opengl紅皮書的前八章節的內容,來打造一個室內小屋.
這一章主要是定義幾個基本的結構.並給出球體與立方體的畫法,先讓我們來定義一些基本的結構.一個是包含點,法向量,紋理貼圖向量,二是矩形與圓形的父類,包含一些基本公有的處理.

1 type T2N3V3 = 2 struct
3 val mutable TexCoord : Vector2 4 val mutable Normal : Vector3 5 val mutable Position : Vector3 6 new(v,n,p) = {TexCoord = v;Normal = n;Position = p} 7 end
8 [<AbstractClass>]
9 type Shape() = 10 let mutable bCreate = false
11 let mutable vi = 0
12 let mutable ei = 0
13 let mutable count = 0
14 member this.vboID with get() = vi and set value = vi <- value 15 member this.eboID with get() = ei and set value = ei <- value 16 member this.TriangelCount with get() = count and set value = count <- value 17 member this.IsCreate with get() = bCreate and set value = bCreate <- value 18 abstract Draw : unit -> unit 19 abstract Init : unit -> unit 20 member this.InitQ : unit -> unit =fun () -> ()
然后是球體的畫法,相關具體過程如上篇,先貼上代碼,我會對其中一些做些說明.

1 type Sphere(radius:float32,level:int) = 2 inherit Shape() 3 let mutable rad,lev = radius,level 4 let RightLevel = 5 if lev < 0 then lev <- 0
6 elif lev > 6 then lev <-6
7 override this.Draw() = 8 if this.IsCreate<>true then this.Init() 9 GL.BindBuffer(BufferTarget.ArrayBuffer,this.vboID) 10 GL.BindBuffer(BufferTarget.ElementArrayBuffer,this.eboID) 11 GL.InterleavedArrays(InterleavedArrayFormat.T2fN3fV3f,0,IntPtr.Zero) 12 GL.DrawElements(BeginMode.Triangles,this.TriangelCount,DrawElementsType.UnsignedInt,IntPtr.Zero) 13 override this.Init() = 14 let alls = Array.create 6 (new T2N3V3()) 15 alls.[0] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), Vector3.UnitX, Vector3.UnitX * rad ) 16 alls.[1] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), Vector3.UnitY, Vector3.UnitY * rad ) 17 alls.[2] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), Vector3.UnitZ, Vector3.UnitZ * rad ) 18 alls.[3] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), -Vector3.UnitX, -Vector3.UnitX * rad ) 19 alls.[4] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), -Vector3.UnitY, -Vector3.UnitY * rad ) 20 alls.[5] <- new T2N3V3(new Vector2( 0.0f, 0.0f ), -Vector3.UnitZ, -Vector3.UnitZ * rad ) 21 let is = [|
22 1;2;0
23 0;2;4
24 0;4;5
25 5;1;0
26 1;3;2
27 4;2;3
28 4;3;5
29 1;5;3
30 |] 31 let (vvv:T2N3V3 []),(iv: int[]) = this.Sub (alls,is) 32 let mutable vID,eID = 0,0
33 //let mutable tv,vv,pv = vvv |> Array.map (fun v -> v.TexCoord,v.Normal,v.Position) |> Array.unzip3
34 GL.GenBuffers(1,&vID) 35 GL.BindBuffer(BufferTarget.ArrayBuffer,vID) 36 GL.BufferData(BufferTarget.ArrayBuffer,IntPtr (4 * 8 * vvv.Length),vvv,BufferUsageHint.StaticDraw) 37
38 GL.GenBuffers(1,&eID) 39 GL.BindBuffer(BufferTarget.ElementArrayBuffer,eID) 40 GL.BufferData(BufferTarget.ElementArrayBuffer,IntPtr (4 * iv.Length),iv,BufferUsageHint.StaticDraw) 41
42 this.vboID <- vID 43 this.eboID <- eID 44 this.TriangelCount <- iv.Length 45 this.IsCreate <- true
46 () 47 member v.GetMidValue (first:T2N3V3,second:T2N3V3) = 48 let midN = Vector3.Lerp(first.Position,second.Position,0.5f) |> Vector3.Normalize 49 let midP = midN *(float32 rad) 50 let midT = Vector2.Lerp(first.TexCoord,second.TexCoord,0.5f) |> Vector2.Normalize 51 let result = new T2N3V3(midT,midN,midP) 52 result 53 member v.Subdivide (v1:T2N3V3,v2:T2N3V3,v3:T2N3V3) = 54 let vs = Array.create 6 (new T2N3V3()) 55 vs.[0] <- v1 56 vs.[1] <- v.GetMidValue(v1,v2) 57 vs.[2] <- v.GetMidValue(v3,v1) 58 vs.[3] <- v2 59 vs.[4] <- v.GetMidValue(v2,v3) 60 vs.[5] <- v3 61 let is = Array.create 12 0
62 is.[0] <- 0
63 is.[1] <- 1
64 is.[2] <- 2
65 is.[3] <- 2
66 is.[4] <- 1
67 is.[5] <- 4
68 is.[6] <- 4
69 is.[7] <- 1
70 is.[8] <- 3
71 is.[9] <- 2
72 is.[10] <-4
73 is.[11] <- 5
74 (vs,is) 75 member this.Sub(alls:T2N3V3 [],is:int []) = 76 //let mutable tv,vv,pv = alls |> Array.map (fun v -> v.TexCoord,v.Normal,v.Position) |> Array.unzip3
77 let mutable allv = alls 78 let mutable iv = is 79 let show array = printfn "%A" array 80 for j in 0 .. lev do
81 let mutable av = Array.create 0 (new T2N3V3()) 82 let mutable ev = Array.create 0 0
83 printfn "%i" allv.Length 84 printfn "%i" iv.Length 85 for i in 0 .. 3 .. iv.Length - 1 do
86 let (vvv,iiv) = this.Subdivide(allv.[iv.[i]],allv.[iv.[i+1]],allv.[iv.[i+2]]) 87 let length = av.Length 88 av <- Array.append av vvv 89 let map = iiv |> Array.map (fun p -> p + length) 90 ev <- Array.append ev map 91 allv <- av 92 iv <- ev 93 allv |> Array.map (fun p -> p.Position) |> show 94 show iv 95 allv,iv
初始化需要的二個參數,分別代表球的大小(radius),與畫的細分程度(level).其中相關如何繪制球體代碼在上文有講,相當於有是把一個分別位於x,y,z各(+radius,-radius)這六個點,組成的一個八個三角形,索引點的位置如Init里的is代表的3(每個三角形)*8(8個面).對其中每個三角形一變4,level表示4的level加一次方.
GL.InterleavedArrays(InterleavedArrayFormat.T2fN3fV3f,0,IntPtr.Zero)其中的T2fN3fV3f對應於我們的數據結構T2N3V3,這個函數分別相當於指定GL.TexCoordPointer,GL.NormalPointer,GL.VertexPointer(還會打開相應狀態),會自動給我們處理好,我們也可以只指定頂點,如下GL.VertexPointer(3,VertexPointerType.Float,4*8,IntPtr (4*8-4*5)),這些數據之間的間隔對應與我們前面寫入的GL.BufferData(BufferTarget.ArrayBuffer,IntPtr (4 * 8 * vvv.Length),vvv,BufferUsageHint.StaticDraw)其中vvv是T2N3V3的結構.
基本的GL.BindBuffer的對應的三個處理就不細說了,在Draw里,BindBuffer是指定我們當前格式數據在存儲位置,然后分別調用InterleavedArrays設定各頂點的狀態,然后是調用DrawElements對應上面的GL.BindBuffer(BufferTarget.ElementArrayBuffer,this.eboID)處理.
然后是立方體的繪制,其中立方體的繪制用的方法看起來會容易理解.如下

1 type Cube(width:float32,height:float32,length:float32,index:int) = 2 inherit Shape() 3 let mutable id = index 4 let xl,yl,zl =width/2.f,height/2.f,length/2.f 5 let mutable color = Color.White 6 let v8 = [|
7 new Vector3(xl,yl,zl) 8 new Vector3(-xl,yl,zl) 9 new Vector3(-xl,-yl,zl) 10 new Vector3(xl,-yl,zl) 11 new Vector3(xl,yl,-zl) 12 new Vector3(-xl,yl,-zl) 13 new Vector3(-xl,-yl,-zl) 14 new Vector3(xl,-yl,-zl) 15 |] 16 new(x,y,z) = 17 let rnd = System.Random().Next() 18 printfn "%i" rnd 19 Cube(x,y,z,-1) 20 override this.Draw() = 21 if this.IsCreate<>true then this.Init() 22 GL.EnableClientState(ArrayCap.VertexArray) 23 GL.EnableClientState(ArrayCap.NormalArray) 24 GL.BindBuffer(BufferTarget.ArrayBuffer,this.vboID) 25 GL.VertexPointer(3,VertexPointerType.Float,0,IntPtr.Zero) 26 GL.PushMatrix() 27 if id >= 0 && id < 8 then
28 GL.Translate(v8.[id]) 29 GL.Color3(this.Color:Color) 30 GL.Normal3(Vector3.UnitZ) 31 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|0;1;2;0;2;3|]) 32 //GL.Color3(Color.Black)
33 GL.Normal3(Vector3.UnitY) 34 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|4;5;1;4;1;0|]) 35 //GL.Color3(Color.Red)
36 GL.Normal3(Vector3.UnitX) 37 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|4;0;3;4;3;7|]) 38 //GL.Color3(Color.Green)
39 GL.Normal3(-Vector3.UnitY) 40 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|3;2;6;3;6;7|]) 41 //GL.Color3(Color.Blue)
42 GL.Normal3(-Vector3.UnitX) 43 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|1;5;6;1;6;2|]) 44 //GL.Color3(Color.DodgerBlue)
45 GL.Normal3(-Vector3.UnitZ) 46 GL.DrawElements(BeginMode.Triangles,6,DrawElementsType.UnsignedInt,[|5;4;7;5;7;6|]) 47 GL.PopMatrix() 48 override this.Init() = 49 let mutable vID = 0
50 GL.GenBuffers(1,&vID) 51 GL.BindBuffer(BufferTarget.ArrayBuffer,vID) 52 GL.BufferData(BufferTarget.ArrayBuffer,IntPtr (4 * 3 * v8.Length),v8,BufferUsageHint.StaticDraw) 53 this.vboID <- vID 54 this.IsCreate <- true
55 let rnd = System.Random(this.GetHashCode()) 56 this.Color <- Color.FromArgb(rnd.Next(0,255),rnd.Next(0,255),rnd.Next(0,255)) 57 () 58 member this.Index with get() = id and set value = id <-value 59 member this.Color with get() = color and set value = color <- value
上圖中的V8分別對應其中的0-7個頂點,GL.DrawElements后面的0;1;2;0;2;3分別是以三角形的畫法來畫一個正方形.
立方體的畫法主要是確定寬,高,長,我們這樣定義,我們站在原點上,面向Z+軸,我們的手展開來表示X軸(對應寬度),而我們的身高表示Y軸(對應高度),和我們面向距離的長遠來表示Z軸,(對應長度).繪制也是8個面,和上面一樣,也是在緩存中記錄頂點,但是不一樣的是,頂點索引是在繪制時沒有用到頂點索引緩存,主要考慮后面有法向量的處理,還有現在沒有加上的紋理貼圖的處理.
在這里,我們要特別注意,頂點順序問題,在opengl,默認正面是逆時針,而我們可以看0;1;2;0;2;3對應圖上的順序.然后大家可能會發現,畫立方體的后面5;4;7;5;7;6,這個順序好像不對,是順時針的.大家可以想象下,我們在門外看門的逆時針畫法與我們在門內看門外順時針的畫法是一樣的.所以如果我們要畫后面4,5,6,7我們以為的是逆時針是不對的,我們要想象我們在那邊來看,然后再畫,應該是5,4,7,6這個方向.其中紋理貼圖后面會說,和這處理也有類似.
在上面,我們看到我們都沒對頂點的顏色來定義,這里沒必要,因為后面我們肯定要用到燈光,用到燈光的話,設置的顏色都沒用,我們會用到燈光顏色與材質的顏色.紋理貼圖也有沒對應處理,這里在后面我會根據講到再來改寫相關處理.
下一節,主要是講第一人稱漫游的相關處理.