UWP簡單示例(二):快速開始你的3D編程,uwp3d
准備
IDE:Visual Studio 2015
了解並學習:SharpDx官方GitHub
推薦Demo:SharpDX_D3D12HelloWorld
第一節 世界
世界坐標系是一個特殊的坐標系,它建立了描述其他坐標系所需要的參考框架。
世界坐標系
從另一方面說,不能用更大的、外部的坐標系來描述世界坐標系
關於世界坐標系的典型問題都是關於初始位置和環境的:
- 每個物體的位置和方向
- 攝像機的位置和方向
- 世界中每一點的地形是什麼(如山丘、建築、湖泊等)
- 一個物體從哪裡來,到哪裡去(NPC的運動策略)
左、右手坐標系
所有的2D坐標系是等價的,但3D坐標系有“手性”之分
左、右手坐標系可以互相轉換,最簡單的方法是只翻轉一個軸的符號
傳統的計算機圖形學使用左手坐標系,而線性代數則傾向於使用右手坐標系
SharpDx采用左手坐標系,即X軸由右向左,Y軸由下至上,Z軸由裡至外
SharpDx的世界有多大
首先,這個世界是有限且離散的
描述三維坐標需要使用SharpDx或System.Numerics命名空間下的Vector3類型
Vector3表示一個三維向量,它的x,y,z分量都是float類型(單精度浮點數),我們知道float范圍是-3.40E+38 ~ +3.40E+38
而原子的直徑是0.1nm級別,若以它作為基本單位,那麼這個世界大約是一個邊長6.80E+25公裡的方盒(約71877億光年)
這個世界足夠大了嗎
目前認為銀河系直徑是10~12萬光年,宇宙可視直徑是920億光年
單精度浮點數可精確到小數點後6位,即當前世界最小分辨率是10-6倍原子大小
那麼離散的float類型足以描述現實世界了嗎?向您介紹計算機圖形學第一准則,留給您思考:
- 計算機圖形學第一准則:近似原則如果它看上去是對的它就是對的:)

![]()
Imports SharpDX
''' <summary>
''' 表示一個三維世界
''' </summary>
Public Interface IWorld
''' <summary>
''' 模型頂點變換矩陣的數組
''' </summary>
''' <returns></returns>
Property ModelMatrix As Matrix()
''' <summary>
''' 更新模型頂點變換矩陣
''' </summary>
Sub Update()
End Interface
VB.NET-IWorld

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示一個三維世界
/// </summary>
public interface IWorld
{
/// <summary>
/// 模型頂點變換矩陣的數組
/// </summary>
/// <returns></returns>
Matrix[] ModelMatrix { get; set; }
/// <summary>
/// 更新模型頂點變換矩陣
/// </summary>
void Update();
}
C#-IWorld
第二節 物體
在編程中,具有宏觀形狀、體積或質量的抽象對象。
位置Location
一個三維向量,它表示當前物體在世界坐標系中的絕對位置
比例Scale
一個三維向量,表示當前物體x,y,z軸縮放比例
旋轉Rotation
通常物體角位移有歐拉角和四元數兩種表示方式
歐拉角:
- 歐拉角有三個分量,偏航角Yaw、俯仰角Pitch、橫滾角Roll
- 給定方位的表達方式不唯一
- 兩個角度間求插值非常困難
- 萬向鎖是一個底層問題,至今沒有簡單的解決方案
四元數:
- 四元數(Quaternion)有四個分量,它是一個超復數
- 四元數能夠平滑插值,但它比歐拉角多占用33.3%的存儲空間
- 多個四元數表示一系列旋轉變換時,將它們相乘(而非直接相加)
- 四元數“減法”,一個變換Q1到另一個變換Q2的差△Q等於Q1的逆乘以Q2(而非直接相減)
- 通過標准化四元數確保它為單位大小,否則它將不合法

![]()
Imports SharpDX
''' <summary>
''' 表示一個可包含若干子對象的剛體
''' </summary>
Public Interface IRigidBody
''' <summary>
''' 子物體
''' </summary>
''' <returns></returns>
Property Children As List(Of IRigidBody)
''' <summary>
''' 父物體
''' </summary>
''' <returns></returns>
Property Parent As IRigidBody
''' <summary>
''' 位置
''' </summary>
''' <returns></returns>
Property Location As Vector3
''' <summary>
''' 縮放
''' </summary>
''' <returns></returns>
Property Scale As Vector3
''' <summary>
''' 旋轉
''' </summary>
''' <returns></returns>
Property Qua As Quaternion
''' <summary>
''' 可見性
''' </summary>
''' <returns></returns>
Property Visible As Boolean
Sub Update()
End Interface
VB.NET-IRigidBody

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示一個可包含若干子對象的剛體
/// </summary>
public interface IRigidBody
{
/// <summary>
/// 子物體
/// </summary>
/// <returns></returns>
List<IRigidBody> Children { get; set; }
/// <summary>
/// 父物體
/// </summary>
/// <returns></returns>
IRigidBody Parent { get; set; }
/// <summary>
/// 位置
/// </summary>
/// <returns></returns>
Vector3 Location { get; set; }
/// <summary>
/// 縮放
/// </summary>
/// <returns></returns>
Vector3 Scale { get; set; }
/// <summary>
/// 旋轉
/// </summary>
/// <returns></returns>
Quaternion Qua { get; set; }
/// <summary>
/// 可見性
/// </summary>
/// <returns></returns>
bool Visible { get; set; }
void Update();
}
C#-IRigidBody
第三節 矩陣與線性變換
線性變換總是把線性子空間變為線性子空間,但是維數可能降低。矩陣的本質就是描述線性變換。
模型與世界空間
物體最開始由物體空間來描述。其中常見的信息包括頂點位置和表面法向量
可將坐標從物體空間轉換到世界空間中,此過程稱作模型變換
通常,光照計算使用世界空間,其實光照計算只需確保幾何體和光線在同一空間
攝像機空間
通過視變換,頂點從世界空間變換到攝像機空間,此空間也稱作眼睛空間
裁剪與屏幕空間
裁剪空間又名標准視體空間,它是為透視投影做准備
一旦用視錐完成了幾何體裁剪,即可向屏幕空間投影
ModelMatrix=World*View*Projection
World=ScaleMatrix*RotationMatrix*TranslateMatrix:
- 縮放矩陣 ScaleMatrix=Matrix.Scaling(Object.Scale)
- 旋轉矩陣 RotationMatrix=Matrix.RotationQuaternion(Object.Quaternion)
- 平移矩陣 TranslateMatrix=Matrix.Translation(Object.Location)
- 默認旋轉中心是原點,所以這三者相乘的順序不能變
View=Matrix.LookAtLH(eye,target,up):
- 眼睛位置 eye=New Vector3(0,0,100),表示當前攝像機位於Z軸100值處
- 視點位置 target=New Vector3(0,0,0),表示當前攝像機看向3D空間的原點
- 向上向量 up=Vector.UnitY,當前攝像機的向上方向
- LH表示左手坐標系,Matrix.LookAtRH是用於右手坐標系
Projection=Matrix.PerspectiveFovLH(fov, aspect, znear, zfar):
- 視椎體水平角 fov=Math.PI/ 3.0F,即水平可視角范圍,通常為60度
- 視錐體寬高比 aspect=ScreenWidth/ScreenHeight,通常和屏幕寬高比一致
- 近裁面深度值 znear=1,即最近可視范圍,用戶可自由設置
- 遠裁面深度值 zfar=10000,即最遠可視范圍,用戶可自由設置
- 實際上這是裁剪變換矩陣,投影到屏幕是由API完成的

![]()
Imports SharpDX
''' <summary>
''' 表示用於視變換的攝像機
''' </summary>
Public Interface ICamera
''' <summary>
''' 獲取或設置攝像機位置
''' </summary>
''' <returns></returns>
Property Eye As Vector3
''' <summary>
''' 獲取或設置目標視點位置
''' </summary>
''' <returns></returns>
Property Target As Vector3
''' <summary>
''' 獲取或設置攝像機向上方向
''' </summary>
''' <returns></returns>
Property Up As Vector3
''' <summary>
''' 獲取當前視變換矩陣
''' </summary>
''' <returns></returns>
ReadOnly Property View As Matrix
End Interface
VB.NET-ICamera

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示用於視變換的攝像機
/// </summary>
public interface ICamera
{
/// <summary>
/// 獲取或設置攝像機位置
/// </summary>
/// <returns></returns>
Vector3 Eye { get; set; }
/// <summary>
/// 獲取或設置目標視點位置
/// </summary>
/// <returns></returns>
Vector3 Target { get; set; }
/// <summary>
/// 獲取或設置攝像機向上方向
/// </summary>
/// <returns></returns>
Vector3 Up { get; set; }
/// <summary>
/// 獲取當前視變換矩陣
/// </summary>
/// <returns></returns>
Matrix View { get; }
}
C#-ICamera
第四節 三角網格
多邊形網格用來模擬復雜物體的表面,任意多邊形網格都能轉成三角網格。
表示網格
多邊形和三角網格在圖形學和建模中廣泛使用,最直接表示方法是用三角形數組
三角網格需要存儲三類信息:
- 頂點 每個三角形都有三個頂點,各頂點都有可能和其他三角形共享
- 邊 連接兩個頂點的邊,每個三角形有三條邊
- 面 每個三角形對應一個面,我們可以用頂點或者邊列表表示面
索引三角網格
在索引三角網格中,我們維護兩個列表:頂點表和三角形表
每個頂點包含一個3D位置,也可能有如紋理映射坐標、表面法向量、光照值等復雜數據
每個三角形由頂點列表的三個索引組成
頂點列出的順序非常重要,它決定面是“正面”還是“反面”
另外,表面法向量、紋理映射保存在三角形一級
索引三角形列表中的鄰接信息是隱含的,邊信息不會被直接存儲
我們可以通過搜索三角形表找出公共邊
創建一個立方體
一個立方體有6個矩形面,每個面有4個頂點
一個矩形面由兩個三角形組成
可見我們共需要24個頂點,12個三角形
假若不分開描述各面,8個頂點就足夠描述一個六面體,但仍需要12個三角形

![]()
''' <summary>
''' 表示一個頂點
''' </summary>
Public Structure Vertex
Public Position As Vector3
Public Color As Vector4
Public Sub New(position As Vector3, color As Vector4)
Me.Position = position
Me.Color = color
End Sub
End Structure
VB.NET-Vertex

![]()
''' <summary>
''' 返回一個指定長寬高的正六面體的頂點數組
''' </summary>
Public Shared Function CreateCube(w As Single, h As Single, d As Single) As Vertex()
w = w / 2
h = h / 2
d = d / 2
Dim vertices As Vertex() = New Vertex() {
New Vertex(New Vector3(-w, h, d), New Vector4(0, 1, 0, 1)), New Vertex(New Vector3(w, h, d), New Vector4(0, 1, 0, 1)),
New Vertex(New Vector3(w, h, -d), New Vector4(0, 1, 0, 1)), New Vertex(New Vector3(-w, h, -d), New Vector4(0, 1, 0, 1)),
New Vertex(New Vector3(-w, -h, d), New Vector4(1, 0, 1, 1)), New Vertex(New Vector3(w, -h, d), New Vector4(1, 0, 1, 1)),
New Vertex(New Vector3(w, -h, -d), New Vector4(1, 0, 1, 1)), New Vertex(New Vector3(-w, -h, -d), New Vector4(1, 0, 1, 1)),
New Vertex(New Vector3(-w, -h, d), New Vector4(1, 0, 0, 1)), New Vertex(New Vector3(-w, h, d), New Vector4(1, 0, 0, 1)),
New Vertex(New Vector3(-w, h, -d), New Vector4(1, 0, 0, 1)), New Vertex(New Vector3(-w, -h, -d), New Vector4(1, 0, 0, 1)),
New Vertex(New Vector3(w, -h, d), New Vector4(1, 1, 0, 1)), New Vertex(New Vector3(w, h, d), New Vector4(1, 1, 0, 1)),
New Vertex(New Vector3(w, h, -d), New Vector4(1, 1, 0, 1)), New Vertex(New Vector3(w, -h, -d), New Vector4(1, 1, 0, 1)),
New Vertex(New Vector3(-w, h, d), New Vector4(0, 1, 1, 1)), New Vertex(New Vector3(w, h, d), New Vector4(0, 1, 1, 1)),
New Vertex(New Vector3(w, -h, d), New Vector4(0, 1, 1, 1)), New Vertex(New Vector3(-w, -h, d), New Vector4(0, 1, 1, 1)),
New Vertex(New Vector3(-w, h, -d), New Vector4(0, 0, 1, 1)), New Vertex(New Vector3(w, h, -d), New Vector4(0, 0, 1, 1)),
New Vertex(New Vector3(w, -h, -d), New Vector4(0, 0, 1, 1)), New Vertex(New Vector3(-w, -h, -d), New Vector4(0, 0, 1, 1))}
Return vertices
End Function
VB.NET-CreateCube

![]()
using SharpDx;
/// <summary>
/// 表示一個存儲3D位置與顏色信息的頂點
/// </summary>
public struct Vertex
{
public Vector3 Position;
public Vector4 Color;
public Vertex(Vector3 position, Vector4 color)
{
this.Position = position;
this.Color = color;
}
}
C#-Vertex

![]()
/// <summary>
/// 返回一個指定長寬高的正六面體的頂點數組
/// </summary>
public static Vertex[] CreateCube(float w, float h, float d)
{
w = w / 2;
h = h / 2;
d = d / 2;
Vertex[] vertices = new Vertex[] {
new Vertex(new Vector3(-w, h, d), new Vector4(0, 1, 0, 1)),
new Vertex(new Vector3(w, h, d), new Vector4(0, 1, 0, 1)),
new Vertex(new Vector3(w, h, -d), new Vector4(0, 1, 0, 1)),
new Vertex(new Vector3(-w, h, -d), new Vector4(0, 1, 0, 1)),
new Vertex(new Vector3(-w, -h, d), new Vector4(1, 0, 1, 1)),
new Vertex(new Vector3(w, -h, d), new Vector4(1, 0, 1, 1)),
new Vertex(new Vector3(w, -h, -d), new Vector4(1, 0, 1, 1)),
new Vertex(new Vector3(-w, -h, -d), new Vector4(1, 0, 1, 1)),
new Vertex(new Vector3(-w, -h, d), new Vector4(1, 0, 0, 1)),
new Vertex(new Vector3(-w, h, d), new Vector4(1, 0, 0, 1)),
new Vertex(new Vector3(-w, h, -d), new Vector4(1, 0, 0, 1)),
new Vertex(new Vector3(-w, -h, -d), new Vector4(1, 0, 0, 1)),
new Vertex(new Vector3(w, -h, d), new Vector4(1, 1, 0, 1)),
new Vertex(new Vector3(w, h, d), new Vector4(1, 1, 0, 1)),
new Vertex(new Vector3(w, h, -d), new Vector4(1, 1, 0, 1)),
new Vertex(new Vector3(w, -h, -d), new Vector4(1, 1, 0, 1)),
new Vertex(new Vector3(-w, h, d), new Vector4(0, 1, 1, 1)),
new Vertex(new Vector3(w, h, d), new Vector4(0, 1, 1, 1)),
new Vertex(new Vector3(w, -h, d), new Vector4(0, 1, 1, 1)),
new Vertex(new Vector3(-w, -h, d), new Vector4(0, 1, 1, 1)),
new Vertex(new Vector3(-w, h, -d), new Vector4(0, 0, 1, 1)),
new Vertex(new Vector3(w, h, -d), new Vector4(0, 0, 1, 1)),
new Vertex(new Vector3(w, -h, -d), new Vector4(0, 0, 1, 1)),
new Vertex(new Vector3(-w, -h, -d), new Vector4(0, 0, 1, 1))
};
return vertices;
}
C#-CreateCube
第五節 方塊人物
可直接用一個骨骼模型描述生物外形,至少MineCraft是這樣的。
骨骼關系
一個骨骼節點有若干子骨骼,但只能有一個父骨骼
易見,我們可以用一個樹形結構來描述骨骼系統
父子骨骼間存在一種“聯動”關系,比如我們移動右手手臂,右手也會跟隨移動
為體現這種“聯動”,在編程中需要將作用於某個骨骼的的變換也同等作用於它的子骨骼
人體骨骼方塊
上部(10塊):頭部、頸部、左右肩、左右上臂,左右下臂,左右手
中部(2 塊):胸部、腰部
下部(8 塊):左右骻、左右大腿,左右小腿和左右腳
通常,腰部是根節點的較好選擇

![]()
Imports SharpDX
''' <summary>
''' 表示骨骼結點
''' </summary>
Public Class Bone
Inherits RigidBodyBase
Public Overrides Property Qua As Quaternion
Set(value As Quaternion)
If IsNewQua Then
IsNewQua = False
sQua = value
sQua.Invert()
sQua.Normalize()
End If
mQua = value
End Set
Get
Return Quaternion.Normalize(sQua * mQua)
End Get
End Property
''' <summary>
''' 絕對坐標
''' </summary>
Public AbsoluteLoc As Vector3
''' <summary>
''' 相對坐標
''' </summary>
Public RelativeLoc As Vector3
''' <summary>
''' 父骨骼
''' </summary>
Public ParentBone As Bone
''' <summary>
''' 骨骼相對旋轉
''' </summary>
Public BoneQua As New Quaternion(0, 0, 0, 1)
''' <summary>
''' 子骨骼
''' </summary>
Public ChildrenBone As New List(Of Bone)
''' <summary>
''' 索引
''' </summary>
Public Index As Integer
Private mQua As New Quaternion(0, 0, 0, 1)
Private sQua As New Quaternion(0, 0, 0, 1)
Private IsNewQua As Boolean = True
Public Sub New(loc As Vector3, scale As Vector3)
Me.RelativeLoc = loc * 10
Me.Scale = scale
End Sub
End Class
VB.NET-Bone

![]()
Imports SharpDX
''' <summary>
''' 表示一個用於描述骨骼信息的對象
''' </summary>
Public Class BoneInf
Public Loc As Vector3
Public Scale As Vector3
Public ParentIndex As Integer
Public ChildIndexArr() As Integer
Public Sub New(l As Vector3, s As Vector3, p As Integer, c As Integer())
Loc = New Vector3(l.Z, l.Y, l.X)
Scale = New Vector3(s.Z, s.Y, s.X)
ParentIndex = p
ChildIndexArr = c
End Sub
End Class
VB.NET-BoneInf

![]()
Imports SharpDX
''' <summary>
''' 表示一個人類模型
''' </summary>
Public Class Human
Inherits RigidBodyBase
Public RootBone As Bone
Dim BoneInfArr() As BoneInf = {
New BoneInf(New Vector3(0, 0, 0), New Vector3(1, 1, 1), 0, New Integer() {1, 12, 16}),'腰部0
New BoneInf(New Vector3(0, 5, 0), New Vector3(2.5, 5, 1), 0, New Integer() {2, 4, 8}),'胸部1
New BoneInf(New Vector3(0, 1, 0), New Vector3(0.7, 1, 1), 1, New Integer() {3}),'頸部2
New BoneInf(New Vector3(0, 1.5, 0), New Vector3(1.3, 1.5, 1), 2, New Integer() {}),'頭部3
New BoneInf(New Vector3(-2, 0, 0), New Vector3(2, 1, 1), 1, New Integer() {5}),'左肩4
New BoneInf(New Vector3(0, -2.5, 0), New Vector3(1, 2.5, 1), 4, New Integer() {6}),'左上臂5
New BoneInf(New Vector3(0, -2.5, 0), New Vector3(1, 2.5, 1), 5, New Integer() {7}),'左小臂6
New BoneInf(New Vector3(0, -1, 0), New Vector3(1, 1, 1), 6, New Integer() {}),'左手7
New BoneInf(New Vector3(2, 0, 0), New Vector3(2, 1, 1), 1, New Integer() {9}),'右肩8
New BoneInf(New Vector3(0, -2.5, 0), New Vector3(1, 2.5, 1), 8, New Integer() {10}),'右上臂9
New BoneInf(New Vector3(0, -2.5, 0), New Vector3(1, 2.5, 1), 9, New Integer() {11}),'右小臂10
New BoneInf(New Vector3(0, -1, 0), New Vector3(1, 1, 1), 10, New Integer() {}),'右手11
New BoneInf(New Vector3(-0.8, 0, 0), New Vector3(0.8, 1, 1), 0, New Integer() {13}),'左骻12
New BoneInf(New Vector3(0, -4, 0), New Vector3(1, 4, 1), 12, New Integer() {14}),'左大腿13
New BoneInf(New Vector3(0, -4, 0), New Vector3(1, 4, 1), 13, New Integer() {15}),'左小腿14
New BoneInf(New Vector3(0, -1, 0), New Vector3(1, 1, 1), 14, New Integer() {}),'左腳15
New BoneInf(New Vector3(0.8, 0, 0), New Vector3(0.8, 1, 1), 0, New Integer() {17}),'右骻16
New BoneInf(New Vector3(0, -4, 0), New Vector3(1, 4, 1), 16, New Integer() {18}),'右大腿17
New BoneInf(New Vector3(0, -4, 0), New Vector3(1, 4, 1), 17, New Integer() {19}),'右小腿18
New BoneInf(New Vector3(0, -1, 0), New Vector3(1, 1, 1), 18, New Integer() {})'右腳19
}
Public Sub New()
CreateBody()
CalcBone(RootBone)
End Sub
''' <summary>
''' 更新指定索引的骨骼
''' </summary>
''' <param name="qua">旋轉</param>
''' <param name="index">骨骼索引</param>
Public Sub UpdateBone(qua As Quaternion, index As Integer)
qua.Normalize()
DirectCast(Children(index), Bone).Qua = qua
CalcBone(DirectCast(Children(index), Bone).ParentBone)
End Sub
''' <summary>
''' 更新所有子骨骼
''' </summary>
''' <param name="parent"></param>
Private Sub CalcBone(parent As Bone)
For Each SubBone As Bone In parent.ChildrenBone
SubBone.BoneQua = Quaternion.Normalize(Me.Qua * SubBone.Qua)
Dim tempLoc = (Matrix.Translation(SubBone.RelativeLoc) * Matrix.RotationQuaternion(SubBone.BoneQua)).TranslationVector
SubBone.AbsoluteLoc = parent.AbsoluteLoc + tempLoc
SubBone.Location = parent.AbsoluteLoc + tempLoc / 2
CalcBone(SubBone)
Next
End Sub
''' <summary>
''' 創建人物身體的所有骨骼
''' </summary>
Private Sub CreateBody()
For i = 0 To BoneInfArr.Count - 1
Children.Add(New Bone(BoneInfArr(i).Loc, BoneInfArr(i).Scale))
DirectCast(Children(i), Bone).Index = i
Next
For i = 0 To BoneInfArr.Count - 1
DirectCast(Children(i), Bone).ParentBone = Children(BoneInfArr(i).ParentIndex)
For Each SubIndex In BoneInfArr(i).ChildIndexArr
DirectCast(Children(i), Bone).ChildrenBone.Add(Children(SubIndex))
Next
Next
RootBone = DirectCast(Children(0), Bone)
RootBone.Parent = RootBone
End Sub
End Class
VB.NET-Human

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示骨骼結點
/// </summary>
public class Bone : RigidBodyBase
{
public override Quaternion Qua {
get { return Quaternion.Normalize(sQua * mQua); }
set {
if (IsNewQua) {
IsNewQua = false;
sQua = value;
sQua.Invert();
sQua.Normalize();
}
mQua = value;
}
}
/// <summary>
/// 絕對坐標
/// </summary>
public Vector3 AbsoluteLoc;
/// <summary>
/// 相對坐標
/// </summary>
public Vector3 RelativeLoc;
/// <summary>
/// 父骨骼
/// </summary>
public Bone ParentBone;
/// <summary>
/// 骨骼相對旋轉
/// </summary>
public Quaternion BoneQua = new Quaternion(0, 0, 0, 1);
/// <summary>
/// 子骨骼
/// </summary>
public List<Bone> ChildrenBone = new List<Bone>();
/// <summary>
/// 索引
/// </summary>
public int Index;
private Quaternion mQua = new Quaternion(0, 0, 0, 1);
private Quaternion sQua = new Quaternion(0, 0, 0, 1);
private bool IsNewQua = true;
public Bone(Vector3 loc, Vector3 scale)
{
this.RelativeLoc = loc * 10;
this.Scale = scale;
}
}
C#-Bone

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示一個用於描述骨骼信息的對象
/// </summary>
public class BoneInf
{
public Vector3 Loc;
public Vector3 Scale;
public int ParentIndex;
public int[] ChildIndexArr;
public BoneInf(Vector3 l, Vector3 s, int p, int[] c)
{
Loc = new Vector3(l.Z, l.Y, l.X);
Scale = new Vector3(s.Z, s.Y, s.X);
ParentIndex = p;
ChildIndexArr = c;
}
}
C#-BoneInf

![]()
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using SharpDX;
/// <summary>
/// 表示一個人類模型
/// </summary>
public class Human : RigidBodyBase
{
public Bone RootBone;
BoneInf[] BoneInfArr = {
new BoneInf(new Vector3(0, 0, 0), new Vector3(1, 1, 1), 0, new int[] {
1,
12,
16
}),
//腰部0
new BoneInf(new Vector3(0, 5, 0), new Vector3(2.5, 5, 1), 0, new int[] {
2,
4,
8
}),
//胸部1
new BoneInf(new Vector3(0, 1, 0), new Vector3(0.7, 1, 1), 1, new int[] { 3 }),
//頸部2
new BoneInf(new Vector3(0, 1.5, 0), new Vector3(1.3, 1.5, 1), 2, new int[]),
//頭部3
new BoneInf(new Vector3(-2, 0, 0), new Vector3(2, 1, 1), 1, new int[] { 5 }),
//左肩4
new BoneInf(new Vector3(0, -2.5, 0), new Vector3(1, 2.5, 1), 4, new int[] { 6 }),
//左上臂5
new BoneInf(new Vector3(0, -2.5, 0), new Vector3(1, 2.5, 1), 5, new int[] { 7 }),
//左小臂6
new BoneInf(new Vector3(0, -1, 0), new Vector3(1, 1, 1), 6, new int[]),
//左手7
new BoneInf(new Vector3(2, 0, 0), new Vector3(2, 1, 1), 1, new int[] { 9 }),
//右肩8
new BoneInf(new Vector3(0, -2.5, 0), new Vector3(1, 2.5, 1), 8, new int[] { 10 }),
//右上臂9
new BoneInf(new Vector3(0, -2.5, 0), new Vector3(1, 2.5, 1), 9, new int[] { 11 }),
//右小臂10
new BoneInf(new Vector3(0, -1, 0), new Vector3(1, 1, 1), 10, new int[]),
//右手11
new BoneInf(new Vector3(-0.8, 0, 0), new Vector3(0.8, 1, 1), 0, new int[] { 13 }),
//左骻12
new BoneInf(new Vector3(0, -4, 0), new Vector3(1, 4, 1), 12, new int[] { 14 }),
//左大腿13
new BoneInf(new Vector3(0, -4, 0), new Vector3(1, 4, 1), 13, new int[] { 15 }),
//左小腿14
new BoneInf(new Vector3(0, -1, 0), new Vector3(1, 1, 1), 14, new int[]),
//左腳15
new BoneInf(new Vector3(0.8, 0, 0), new Vector3(0.8, 1, 1), 0, new int[] { 17 }),
//右骻16
new BoneInf(new Vector3(0, -4, 0), new Vector3(1, 4, 1), 16, new int[] { 18 }),
//右大腿17
new BoneInf(new Vector3(0, -4, 0), new Vector3(1, 4, 1), 17, new int[] { 19 }),
//右小腿18
new BoneInf(new Vector3(0, -1, 0), new Vector3(1, 1, 1), 18, new int[])
//右腳19
};
public Human()
{
CreateBody();
CalcBone(RootBone);
}
/// <summary>
/// 更新指定索引的骨骼
/// </summary>
/// <param name="qua">旋轉</param>
/// <param name="index">骨骼索引</param>
public void UpdateBone(Quaternion qua, int index)
{
qua.Normalize();
((Bone)Children(index)).Qua = qua;
CalcBone(((Bone)Children(index)).ParentBone);
}
/// <summary>
/// 更新所有子骨骼
/// </summary>
/// <param name="parent"></param>
private void CalcBone(Bone parent)
{
foreach (Bone SubBone in parent.ChildrenBone) {
SubBone.BoneQua = Quaternion.Normalize(this.Qua * SubBone.Qua);
dynamic tempLoc = (Matrix.Translation(SubBone.RelativeLoc) * Matrix.RotationQuaternion(SubBone.BoneQua)).TranslationVector;
SubBone.AbsoluteLoc = parent.AbsoluteLoc + tempLoc;
SubBone.Location = parent.AbsoluteLoc + tempLoc / 2;
CalcBone(SubBone);
}
}
/// <summary>
/// 創建人物身體的所有骨骼
/// </summary>
private void CreateBody()
{
for (i = 0; i <= BoneInfArr.Count - 1; i++) {
Children.Add(new Bone(BoneInfArr[i].Loc, BoneInfArr[i].Scale));
((Bone)Children(i)).Index = i;
}
for (i = 0; i <= BoneInfArr.Count - 1; i++) {
((Bone)Children(i)).ParentBone = Children(BoneInfArr[i].ParentIndex);
foreach (object SubIndex_loopVariable in BoneInfArr[i].ChildIndexArr) {
SubIndex = SubIndex_loopVariable;
((Bone)Children(i)).ChildrenBone.Add(Children(SubIndex));
}
}
RootBone = (Bone)Children(0);
RootBone.Parent = RootBone;
}
}
C#-Human
附錄
這是開始你的3D編程的第一步
需要注意哪些問題?
3D編程中,形式轉換經常是錯誤的根源,尤其要注意坐標系的手性
在限制歐拉角中,俯仰角Pitch的范圍是±90º,偏航角Yaw的范圍是±180º
(額外說明一點,UWP的CompositeTransform3D使用的就是限制歐拉角)
為什麼選擇SharpDx?
SharpDx庫與UWP兼容,其他如SharpGL不兼容
如果你是C#開發者,Unity3D會是更好的選擇
Direct3D是底層的3D圖形庫,通過接觸它你可以學習到很多底層圖形編程知識
了解底層知識會使你在接觸並使用Unity3D等引擎時更加得心應手
其它
開源鏈接:ExperDot.SharpDx3DEngine
參考書籍:《3D數學基礎:圖形與游戲開發》[美]Fletcher Dunnlan Parberry著 清華大學出版社(史銀雪、陳洪和王榮靜譯)