WPF 3D model - Sphere, Cone, and Cylinder
?
Extending Visual3D - Sphere, Cone, and Cylinder
http://blogs.msdn.com/b/danlehen/archive/2005/10/16/481597.aspx
?
He?put together a short sample which derives a Sphere, a Cone, and a Cylinder primitive from ModelVisual3D.? (Source available here.)?
?
You will notice that these primitives are directly usable from Xaml:
<Viewport3D Camera=”{Camera}” >
? <my:Sphere3D Transform=”{XForm1}” Material=”{Azul}” />
? <my:Cone3D Transform=”{XForm2}” Material=”{Tulips}” />
? <my:Cylinder3D Transform=”{XForm3}” Material=”{Autumn}” />
</Viewport3D>
?
?
Perspective : Easy 3D programming with WPF
?
http://www.odewit.net/ArticleContent.aspx?id=Wpf3DIntro&lang=en&format=xaml
?
?
[Lieo原創]圓柱體的三角形剖分——使用WPF 3D繪制圓柱體
上個學期在做某個軟件時需要使用WPF繪制一些基本的三維物體。找了很多資料,發現大多是介紹球體的,因為這些圖形的繪制原理基本類似。
但是在繪制圓柱體時遇到了一個問題,就是不知道空間中任意一個圓的參數方程。憑大學裏學的高等數學知識僅能推導出底面與坐標平面平行的圓的參數方程,如果圓與坐標面成任意夾角就無法解決。使用與坐標面平行的圓繪制出的“類圓柱體”已經可以滿足我的程序的需求了,但是繪制出來的畢竟不是一個真正的圓柱。向同學請教了參數方程後後,終於解決了這個問題。
【鋪墊:使用WPF 3D繪制三維物體的方法概述】
與Direct 3D類似,在WPF中,任何三維物體都是由三角形組成的。MeshGeometry3D對象定義組成圖像的各個三角形頂點和這些頂點的連接方式。該對象的Positions屬性是一個Point3D類型的集合,用戶記錄三角形的各頂點坐標,TriangleIndices屬性則描述這些頂點的鏈接方式。
GeometryModel3D對象的Material和BackMaterial屬性可以定義三維物體的表面和背面材質。在本程序中,材質只用到了漫反射材質DiffuseMaterial。Geometry屬性指定該三維物體的MeshGeometry3D對象。為此,我們可以為每一個原子、鍵和平面生成各自的GeometryModel3D對象,這樣就可以為每一個元素分別進行著色。
每個GeometryModel3D被承載在一個ModelVisual3D對象中。整個三維場景都被定義在一個Viewport3D對象中。該對象的Children屬性是ModelVisual3D的集合。將預先設定的三維物體的ModelVisual3D對象添加到集合中,便可以在Viewport3D視圖中顯示。
?
【引子:先了解如何繪制一個球面】
一個球體可以表示成由經線和緯線組成的網格,我們可以將每個網格近似地看成由兩個三角形組成的平面,如下圖所示。
為了依次生成球面上離散的各點坐標,需要將球面寫成參數形式。設球心在坐標原點,球體半徑為R,則其參數方程為:
使用參數方程,用雙重循環即可生成球面的點。代碼如下:
?
??????? ‘將球面進行三角形拆分。設球面的參數方程為:
??????? ‘ x = - r * cosφ * sinθ
??????? ‘ y = r * sinφ
??????? ‘ z = - r * cos φ * cosθ
??????? ‘其中,-π/2≤φ≤π/2,-2π≤θ≤2π
??????? Dim mesh As New MeshGeometry3D
??????? Dim x, y, z, theta, phi As Double
??????? Dim normal As Vector3D?? ‘組成球面的某個三角形的法向量
??????? For i As Integer = 0 To Stacks? ‘將球面拆分成Stacks個等距離的薄片(緯度,自上向下)
??????????? phi = Math.PI / 2 - i * Math.PI / Stacks?? ‘計算該緯度位置的phi角
??????????? y = R * Math.Sin(phi)? ‘計算該緯度位置的y軸坐標(假設球心在原點)
??????????? For j As Integer = 0 To Slices? ‘將球面縱向切成Slices份(經度方向,自西向東)
??????????????? theta = j * 2 * Math.PI / Slices???? ‘計算該經度位置的theta角
??????????????? ‘按照參數方程計算出另外兩個坐標值
??????????????? x = -R * Math.Cos(phi) * Math.Sin(theta)
??????????????? z = -R * Math.Cos(phi) * Math.Cos(theta)
??????????????? normal = New Vector3D(x, y, z)? ‘當球心在原點時,某點的法向量就是(x,y,z)
??????????????? mesh.Positions.Add(normal + center)? ‘某點的坐標:指定的球心+當前計算出的坐標
??????????????? ‘計算材質對應的二維坐標,點(j / Slices, i / Stacks)是該點在二維平面展開後對應的坐標
??????????????? mesh.TextureCoordinates.Add(New Windows.Point(j / Slices, i / Stacks))
??????????? Next j
??????? Next i
?
其中Stacks表示緯度剖分數,Slices為經度拆分數。每計算一個頂點的坐標,便將其添加到Positions集合中。mesh.Normals.Add方法由於指定該點的法向量。該點的法向量方向是球心與該點連線的向量方向,即 ,而該點的坐標為 ,其中c是球心的坐標。
接下來需要考慮的問題是如何為球面著色。設想球面按球面軸展開,就成了如下圖所示的二維平面。
與頂點集合對應的TextureCoordinates集合指定了填充三維對象表面時頂點與二維圖像之間的關系。WPF定義與三維物體表面對應的二維填充平面的左上頂點和右下頂點分別為(0,0)和(1,1),如上圖所示的(i,j),其對應的二維向量為 。mesh.TextureCoordinates.Add(i/stacks,j/slices)就是指定該頂點與二維填充平面的對應關系。
頂點和相關集設置好後,需要指定這些頂點如何構成三角形。具體實現方式請參考Lieo3DModel類中GenerateSphereMesh方法。
【進入正題:圓柱體側面的三角形剖分】
與球體類似,圓柱體按照其軸展開後也是一個矩形。
可以想象,設n表示從底面圓心指向頂面圓心的向量,將一個底面圓上的按照圓的參數方程等間隔地生成,那麽頂面相對應的頂點的坐標可按照向量n平移得到。將這樣的兩個點連起來,連線與軸平行。按照這個思路,只需要生成底面圓上各點的坐標,即可按照圖將圓柱剖分成三角形。
空間中任意一個圓的參數方程為:
其中 為圓的法向量。為了計算這兩個與法向量相互正交的向量,可以使用向量的叉積運算。
設M是空間中任意一個不與圓的法向量共線的向量,令 ,這樣得到的 就是相互垂直的。將向量歸一化後,便得到了參數方程所需要的a,b。
參照球面生成的代碼,生成圓柱的代碼能夠很容易寫出。具體請下列代碼:
??? ‘‘‘ <summary>
??? ‘‘‘ 將兩底面圓心在 p1、p2 位置,底面半徑為 R 的圓柱體進行三角形剖分。
??? ‘‘‘ </summary>
??? ‘‘‘ <param name="p1">圓柱上底面圓心的坐標。</param>
??? ‘‘‘ <param name="p2">圓柱下底面圓心的坐標。</param>
??? ‘‘‘ <param name="R">圓柱的底面半徑。</param>
??? ‘‘‘ <returns>返回值:代表該圓柱面的MeshGeometry3D對象。</returns>
??? Public Shared Function GenerateCylinderMesh(ByVal p1 As Point3D, ByVal p2 As Point3D, ByVal R As Double) As MeshGeometry3D
??????? ‘將圓柱體進行三角形剖分
??????? ‘空間中圓的參數方程:(x,y,z) = r*(A*cosθ+B*sinθ)+(x0,y0,z0)?? 【0≤θ≤2π】
??????? ‘? 其中 a、b 是單位向量,且滿足 A⊥B⊥n(圓的法向量)
??????? Dim mesh As New MeshGeometry3D
??????? Dim CircleVector As Vector3D = p2 - p1?? ‘從p1點到p2點的3D向量
??????? Dim M As New Vector3D(1, 1, 1)
??????? If Vector3D.AngleBetween(M, CircleVector) < 0.1 Then
??????????? M = New Vector3D(1, 0, 0)
??????? End If
??????? Dim A As Vector3D = Vector3D.CrossProduct(CircleVector, M)
??????? Dim B As Vector3D = Vector3D.CrossProduct(CircleVector, A)
??????? A.Normalize()
??????? B.Normalize()
??????? Dim theta As Double
??????? Dim Pos1, Pos2 As Point3D
??????? ‘設圓與 xz 軸平行
??????? For i As Integer = 0 To Stacks
??????????? ‘計算剖分三角形頂點在參數方程中對應的角度
??????????? theta = i / Stacks * Math.PI * 2
??????????? Pos1 = R * (A * Math.Cos(theta) + B * Math.Sin(theta)) + p1
??????????? Pos2 = R * (A * Math.Cos(theta) + B * Math.Sin(theta)) + p2
??????????? mesh.Positions.Add(Pos1)
??????????? mesh.Positions.Add(Pos2)
??????????? mesh.TextureCoordinates.Add(New Windows.Point(i / Stacks, 0))
??????????? mesh.TextureCoordinates.Add(New Windows.Point(i / Stacks, 1))
??????? Next
??????? For i As Integer = 0 To Stacks - 1
??????????? mesh.TriangleIndices.Add(i * 2)
??????????? mesh.TriangleIndices.Add(i * 2 + 1)
??????????? mesh.TriangleIndices.Add(i * 2 + 3)
??????????? mesh.TriangleIndices.Add(i * 2)
??????????? mesh.TriangleIndices.Add(i * 2 + 3)
??????????? mesh.TriangleIndices.Add(i * 2 + 2)
??????? Next
??????? Return mesh
??? End Function
?
WPF 3D model - Sphere, Cone, and Cylinder