Opengl绘制我们的小屋(一)球体,立方体绘制

这个系列我想用来运用opengl红皮书的前八章节的内容,来打造一个室内小屋.

这一章主要是定义几个基本的结构.并给出球体与立方体的画法,先让我们来定义一些基本的结构.一个是包含点,法向量,纹理贴图向量,二是矩形与圆形的父类,包含一些基本公有的处理.

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 () -> ()
View Code
然后是球体的画法,相关具体过程如上篇,先贴上代码,我会对其中一些做些说明.
Opengl绘制我们的小屋(一)球体,立方体绘制
 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
View Code
初始化需要的二个参数,分别代表球的大小(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)处理.

然后是立方体的绘制,其中立方体的绘制用的方法看起来会容易理解.如下

Opengl绘制我们的小屋(一)球体,立方体绘制
 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
View Code

立方体顶点索引

上图中的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这个方向.其中纹理贴图后面会说,和这处理也有类似.

在上面,我们看到我们都没对顶点的颜色来定义,这里没必要,因为后面我们肯定要用到灯光,用到灯光的话,设置的颜色都没用,我们会用到灯光颜色与材质的颜色.纹理贴图也有没对应处理,这里在后面我会根据讲到再来改写相关处理.

下一节,主要是讲第一人称漫游的相关处理.

你可能感兴趣的:(OpenGL)