在看OpenGL紅皮書,看到生成球體這節,講了很多,總感覺不如自己動手寫一些代碼來的實在,用OpenGL中三角形模擬球形生成.主要要點,模型視圖變換,多邊形表面環繞一致性,矩陣堆棧.先貼上代碼.
雖然是用F#寫的,但是處理全是過程式的,很好理解.

1 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.dll" 2 #r "F:\3D\1.0\Binaries\OpenTK\Debug\OpenTK.GLControl.dll" 3 4 open System 5 open System.Collections.Generic 6 open System.Windows.Forms 7 open System.Threading 8 open System.Drawing 9 open System.Drawing.Imaging 10 open OpenTK 11 open OpenTK.Graphics 12 open OpenTK.Graphics.OpenGL 13 14 type loopForm() as form= 15 inherit Form() 16 let mutable x = 5.f 17 let mutable y = 5.f 18 let mutable z = 5.f 19 let offest = 1.f 20 let glControl = new OpenTK.GLControl() 21 let textX= new TextBox() 22 let textY= new TextBox() 23 let textZ= new TextBox() 24 let textLR = new TextBox() 25 let textUD= new TextBox() 26 let textInfo = new TextBox() 27 let labelX= new Label() 28 let labelY= new Label() 29 let labelZ= new Label() 30 let labelLR = new Label() 31 let labelUD= new Label() 32 let mutable first = 0 33 let mutable buffer = 0 34 let list = 0 35 let scale = 3.f 36 do 37 form.SuspendLayout() 38 glControl.Location <- new Point(10,40) 39 glControl.Size <- new Size(400,300) 40 glControl.BackColor <- Color.Red 41 glControl.Resize.Add(form.resize) 42 glControl.Paint.Add(form.paint) 43 form.MouseWheel.Add(form.MouseDown) 44 form.ClientSize <- new Size(600,400) 45 form.Text <- "opengl" 46 form.StartPosition <- FormStartPosition.Manual 47 form.Location <- new Point(1200,600) 48 form.Controls.Add(glControl) 49 form.ResumeLayout(false) 50 labelX.Location <- new Point(420,40) 51 labelY.Location <- new Point(420,70) 52 labelZ.Location <- new Point(420,100) 53 labelLR.Location <- new Point(420,130) 54 labelUD.Location <- new Point(420,160) 55 labelX.Text <- "X:" 56 labelY.Text <- "Y:" 57 labelZ.Text <- "Z:" 58 labelLR.Text <- "水平:" 59 labelUD.Text <-"上下:" 60 textX.Location <- new Point(460,40) 61 textY.Location <- new Point(460,70) 62 textZ.Location <- new Point(460,100) 63 textLR.Location <- new Point(460,130) 64 textUD.Location <- new Point(460,160) 65 textInfo.Location <- new Point(420,190) 66 textInfo.Width <- 140 67 form.Controls.Add(textX) 68 form.Controls.Add(textY) 69 form.Controls.Add(textZ) 70 form.Controls.Add(textLR) 71 form.Controls.Add(textUD) 72 form.Controls.Add(labelX) 73 form.Controls.Add(labelY) 74 form.Controls.Add(labelZ) 75 form.Controls.Add(labelLR) 76 form.Controls.Add(labelUD) 77 form.Controls.Add(textInfo) 78 //#endregion 79 override v.OnLoad e = 80 base.OnLoad e 81 GL.ClearColor Color.MidnightBlue 82 Application.Idle.Add(v.AIdle) 83 v.ShowUI 84 textX.TextChanged.Add(form.TextChange) 85 textY.TextChanged.Add(form.TextChange) 86 textZ.TextChanged.Add(form.TextChange) 87 textLR.TextChanged.Add(form.TextChange) 88 textUD.TextChanged.Add(form.TextChange) 89 //踢除正反面 90 //GL.Enable EnableCap.CullFace 91 //GL.CullFace CullFaceMode.Back 92 //指定正反面 93 GL.FrontFace FrontFaceDirection.Ccw 94 //設置材料面填充模式 95 GL.PolygonMode(MaterialFace.Front,PolygonMode.Fill) 96 GL.PolygonMode(MaterialFace.Back,PolygonMode.Line) 97 //啟用數組功能. 98 GL.EnableClientState(ArrayCap.VertexArray) 99 //GL.EnableClientState(ArrayCap.ColorArray) 100 GL.EnableClientState(ArrayCap.IndexArray) 101 //#region "" 102 member v.resize (e:EventArgs) = 103 GL.Viewport(0,0,glControl.ClientSize.Width,glControl.ClientSize.Height) 104 let aspect = float32 glControl.ClientSize.Width /float32 glControl.ClientSize.Height 105 let mutable projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4,aspect,0.1f,64.f) 106 GL.MatrixMode MatrixMode.Projection 107 GL.LoadMatrix(&projection) 108 member v.paint (e:PaintEventArgs) = 109 v.Render() 110 member v.AIdle (e:EventArgs) = 111 while (glControl.IsIdle) do 112 v.Render() 113 member v.TextChange (e:EventArgs) = 114 x <- v.UIValue(textX) 115 y <- v.UIValue(textY) 116 z <- v.UIValue(textZ) 117 member v.MouseDown(e:MouseEventArgs) = 118 match v.ActiveControl with 119 | :? TextBox as t1 -> 120 let mutable t = v.UIValue(t1) 121 t <- t + float32 e.Delta * offest * 0.01f 122 t1.Text <- t.ToString() 123 | _ -> 124 v.Text <- v.ActiveControl.Text 125 let state =float32 e.Delta * offest * 0.01f 126 x<- x+state 127 y<- y + state 128 z <- z + state 129 v.ShowUI 130 member x.UIValue 131 with get (text:TextBox) = 132 let mutable value = 0.f 133 if System.Single.TryParse(text.Text,&value) then 134 value <- value 135 value 136 and set (text:TextBox) (value:float32) = text.Text<- value.ToString() 137 member v.ShowUI = 138 textX.Text <- x.ToString() 139 textY.Text <- y.ToString() 140 textZ.Text <- z.ToString() 141 textLR.Text <- v.UIValue(textLR).ToString() 142 textUD.Text <- v.UIValue(textUD).ToString() 143 member v.Normal (c:Vector3) = 144 c.Normalize() 145 c * scale 146 member v.Subdivide (v1:Vector3,v2:Vector3,v3:Vector3) = 147 let vs = Array.create 6 Vector3.Zero 148 vs.[0] <- v1 149 vs.[1] <- v.Normal( Vector3.Lerp(v1,v2,0.5f)) 150 vs.[2] <- v.Normal( Vector3.Lerp(v3,v1,0.5f)) 151 vs.[3] <- v2 152 vs.[4] <- v.Normal( Vector3.Lerp(v2,v3,0.5f)) 153 vs.[5] <- v3 154 let is = Array.create 12 0 155 is.[0] <- 0 156 is.[1] <- 1 157 is.[2] <- 2 158 is.[3] <- 2 159 is.[4] <- 1 160 is.[5] <- 4 161 is.[6] <- 4 162 is.[7] <- 1 163 is.[8] <- 3 164 is.[9] <- 2 165 is.[10] <-4 166 is.[11] <- 5 167 (vs,is) 168 //#endregion 169 member v.CreatePane (angle:float32,x,y,z) = 170 GL.PushMatrix() 171 GL.Color3(Color.Green) 172 GL.Rotate(angle,x,y,z) 173 let mutable vv = [|Vector3.UnitY*scale;Vector3.UnitZ*scale;Vector3.UnitX*scale|] 174 let mutable iv = [|0;1;2|] 175 //let show array = printfn "%A" array 176 let mutable t =int (v.UIValue(textInfo)) 177 if t > 6 then 178 t <- 6 179 elif t < 0 then 180 t <- 0 181 for j in 0 .. t do 182 let mutable av = Array.create 0 Vector3.Zero 183 let mutable ev = Array.create 0 0 184 for i in 0 .. 3 .. iv.Length - 1 do 185 let (vvv,iiv) = v.Subdivide(vv.[iv.[i]],vv.[iv.[i+1]],vv.[iv.[i+2]]) 186 let length = av.Length 187 av <- Array.append av vvv 188 let map = iiv |> Array.map (fun p -> p + length) 189 ev <- Array.append ev map 190 vv <- av 191 iv <- ev 192 //show vv 193 //show iv 194 GL.VertexPointer(3,VertexPointerType.Float,0,vv) 195 GL.DrawElements(BeginMode.Triangles,iv.Length,DrawElementsType.UnsignedInt,iv) 196 GL.PopMatrix() 197 member v.Render = 198 let mutable lookat = Matrix4.LookAt(new Vector3(x,y,z),Vector3.Zero,Vector3.UnitY) 199 GL.MatrixMode(MatrixMode.Modelview) 200 GL.LoadMatrix(&lookat) 201 GL.Rotate(v.UIValue(textLR),0.f,1.f,0.f) 202 GL.Rotate(v.UIValue(textUD),1.f,0.f,0.f) 203 GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit) 204 GL.Color3(Color.Green) 205 v.CreatePane(0.f,0.f,1.f,0.f) 206 v.CreatePane(90.f,0.f,1.f,0.f) 207 //v.CreatePane(180.f,0.f,1.f,0.f) 208 glControl.SwapBuffers() 209 ignore 210 let t = new loopForm() 211 t.Show()
首先我們設定逆時針方向為正方向,分別設定正面為畫布填充,反面為線填充,這樣我們就能很容易知道我們生成的三角形倒底是不是正確生成的我們要的面向.
然后分別取用頂點數組和頂點數組索引功能.畢竟后面的點多,一個一個去組裝沒這個腦力,還沒性能.
如何用三角開來模擬生成球面,方法肯定很多種,這里我們可以想象下,在坐標軸的原點就是球的原點,半徑為1,被X,Y,Z軸分成八個部分,我們找到正右上的那邊,與X,Y,Z軸的交點分別為x1(1,0,0),y1(0,1,0),z1(0,0,1).
然后我們把x1,y1,z1連起來畫一個三角形.注意這里就有順序了,想像一下,三個點的位置,要畫正面是用逆時針方向,一算,x1,y1,z1這個方向就是,但是通常為了形象些,我們從y1上開始畫,然后是前z1,然后是右x1.如代碼是這樣
let mutable vv = [|Vector3.UnitY*scale;Vector3.UnitZ*scale;Vector3.UnitX*scale|]
let mutable iv = [|0;1;2|]
然后就是細分這三角形,過程就是這樣,一個三角形分成四個.在for j in 0 .. t 這里,t越多,分的就越多,t是0,分一次成四個,t是1,四個再分別分成四個,就是16個.最后總的三角形就是4的t+1次方.
細分算法,大致如下,已知球面上二點,a1,a2,求在這球二點中間點ax.
已知球心在中間,得知,三個向量有如下關系,向量ax = (向量a2-向量a1)*0.5 + 向量a1.這樣可以算到向量a1,那點ax就是向量a1*半徑.
當一個三角形分成幾個三角形,也就是三個頂點變成六個頂點,那么生成生成三角形的索引也要重新更新.具體過程用圖來說明.
分的越細,球面越光滑,這樣只生成了八分之一的球面,后面的如何畫了,前面講了矩陣堆棧的用法,剛好可以用在這樣能在我方便生成另外幾個面,v.CreatePane(90.f,0.f,1.f,0.f)這個是我們繞Y軸90度后,生成另外的一個面,為了不破壞全局坐標,我們可以在轉之前調用PushMatrix記住當前矩陣,畫完后再用PopMatrix回到當前矩陣.
看一下程序運行后的效果圖.界面上的X,Y,Z指向人眼的位置,左右與上下指旋轉方向.最下面指細分的程度,值越大,細分的越厲害.
大家可以把頂點索引變下順序,然后再來看下效果,也可以把余下的六面全部補起.