UWP簡單示例(二):快速開始你的3D編程


准備

  IDE:Visual Studio

  開源庫:GitHub.SharpDx

  入門示例:SharpDX_D3D12HelloWorld

  為什么選擇 SharpDx?

  SharpDx 庫與 UWP 兼容,其他如 SharpGL 不兼容

  如果你是 C# 開發者,Unity3D 會是更好的選擇

  Direct3D 是底層的 3D 圖形庫,通過接觸它你可以學習到很多底層圖形編程知識

  了解底層知識會使你在接觸並使用 Unity3D 等引擎時更加得心應手

第一節 世界

  世界坐標系是一個特殊的坐標系,它建立了描述其他坐標系所需要的參考框架。

  世界坐標系

  從另一方面說,不能用更大的、外部的坐標系來描述世界坐標系

  關於世界坐標系的典型問題都是關於初始位置和環境的:

  • 每個物體的位置和方向
  • 攝像機的位置和方向
  • 世界中每一點的地形是什么(如山丘、建築、湖泊等)
  • 一個物體從哪里來,到哪里去(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% 的存儲空間
  • 多個四元數表示一系列旋轉變換時,將它們相乘(而非直接相加)
  • 四元數“減法”,一個變換 Q到另一個變換 Q的差 △Q 等於 Q的逆乘以 Q(而非直接相減)
  • 通過標准化四元數確保它為單位大小,否則它將不合法

 

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 編程中,形式轉換經常是錯誤的根源,尤其要注意坐標系的手性

  在限制歐拉角中,俯仰角 Pitch 的范圍是 ±90º,偏航角 Yaw 的范圍是 ±180º

  《3D數學基礎:圖形與游戲開發》[美] Fletcher Dunnlan Parberry 著


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM