• 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 著

  • 相关阅读:
    es6 学习小计
    aligin-items与aligin-content的区别
    编写Shader时的一些性能考虑
    Shader预处理宏、内置状态变量、多版本编译等
    Unity内置的shader include files
    Vertex and Fragment Shader
    对于资源上MissingScript的清理方案讨论
    Surface Shader
    LOD设置
    《蛙》
  • 原文地址:https://www.cnblogs.com/experdot/p/5644612.html
Copyright © 2020-2023  润新知