UWP简单示例(二):快速开始你的3D编程

准备

  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著 清华大学出版社(史银雪、陈洪和王荣静译)

时间: 2024-10-13 12:03:38

UWP简单示例(二):快速开始你的3D编程的相关文章

UWP简单示例(二):快速开始你的MineCraft

准备 IDE:Visual Studio 2015 了解并学习:SharpDx官方示例 第一节 世界 在他诞生之初,天地还是一片混沌. 世界坐标系 世界坐标系是一个特殊的坐标系,它建立了描述其他坐标系所需要的参考框架 从另一方面说,不能用更大的.外部的坐标系来描述世界坐标系 关于世界坐标系的典型问题都是关于初始位置和环境的: 每个物体的位置和方向 摄像机的位置和方向 世界中没一点的地形是什么(如山丘.建筑.湖泊等) 个物体从哪里来,到哪里去(NPC的运动策略) 左.右手坐标系 所有的2D坐标系是

WPF MVVM+EF增删改查 简单示例(二) 1对1 映射

WPF MVVM+EF增删改查 简单示例(一)实现了对学生信息的管理. 现在需求发生变更,在录入学生资料的时候同时需要录入学生的图片信息,并且一名学生只能有一张图片资料.并可对学生的图片资料进行更新. 添加了那些功能,先看看效果图: 第一步:添加实体类StudentPhotoEntity.cs public class StudentPhotoEntity { public int StudentId { get; set; } public byte[] StudentPhoto { get;

UWP简单示例(三):快速开发2D游戏引擎

准备 IDE:VisualStudio 2015 Language:VB.NET/C# 图形API:Win2D MSDN教程:UWP游戏开发 写在前面的话 没有什么比重复造轮子更让人心碎的事情了. (如果有,那就是造了两遍) 是否有必要开发游戏引擎? 现在市面上有很多成熟的跨平台游戏引擎,对个人开发者也较为友好 若你是一名C#开发者,可以选择CocosSharp或Unity3D 尤其Unity3D,用它开发2D和3D游戏一样出色 当然,亲自编写一款简单的2D游戏引擎也是一件让人自信感满满的事情

RPC学习----Thrift快速入门和Java简单示例

一.什么是RPC? RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议. RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据.在OSI网络通信模型中,RPC跨越了传输层和应用层.RPC使得开发包括网络分布式多程序在内的应用程序更加容易. 二.什么是Thrift? thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和

ActiveMQ学习教程(二)——简单示例

ActiveMQ学习教程(二)--简单示例 一.应用IDEA构建Maven项目 File->New->Module...->Maven->勾选->选择->Next -> GroupId:com.jd.myMaven   |    ArtifactId:activeMQ    |    version:默认   ->Finish 项目构建成功!项目结构如下所示: 二.创建生产者类,模拟生产者发消息 Step1:java/activemq/JMSProducer

Redis 安装与简单示例

Redis 安装与简单示例 一.Redis的安装 Redis下载地址如下:https://github.com/dmajkic/redis/downloads 解压后根据自己机器的实际情况选择32位或者64位.下载解压后图片如下: redis-server.exe redis服务器的daemon启动程序 redis.conf redis配置文件 redis-cli.exe redis命令行操作工具.当然,也可以用telnet根据其纯文本协议来操作 redis-check-dump.exe 本地数

AMQP消息队列之RabbitMQ简单示例

前面一篇文章讲了如何快速搭建一个ActiveMQ的示例程序,ActiveMQ是JMS的实现,那这篇文章就再看下另外一种消息队列AMQP的代表实现RabbitMQ的简单示例吧.在具体讲解之前,先通过一个图来概览下: 1.添加Maven依赖 <!-- rabbitmq begin --> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit

关于Ajax实现的简单示例

一.代码示例 关于Ajax的基本概念(包括XMLHttpRequest对象及其相关方法属性)移步这里(w3school中文版)学习了解. <!doctype html> <html lang = "en"> <head> <meta charset = "utf-8"> <title>使用Ajax异步加载数据</title> <script type = "text/javasc

动态网站开发技术学习2:VS 2010制作作第一个简单示例网站

制作一个简单示例网站的步骤: 一,启动VS 2010开发环境,选择菜单中的"文件",选择其中的"新建",再选择"网站",会弹出"新建网站"对话框. 二,在"新建网站"的对话框,在"已安装模板"中选择Visual C#,右侧的选择"ASP.NET网站"选项,单击"新建网 站"中的"文件系统",下拉菜单中有"文件系统&qu