在看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指向人眼的位置,左右与上下指旋转方向.最下面指细分的程度,值越大,细分的越厉害.
大家可以把顶点索引变下顺序,然后再来看下效果,也可以把余下的六面全部补起.