• 处理模型——对不同对象设置不同的世界矩阵,组合世界矩阵


    问题

    你想在绘制模型之前独立地移动、旋转并/或缩放它们。

    解决方案

    你可以设置对象的世界矩阵实现这个功能,而矩阵是可以存储任何变换(平移、旋转或缩放等操作)的对象。你可以简单地使用XNA框架提供的基本方法创建一个变换矩阵:

    • Matrix.CreateTranslation
    • Matrix.CreateScale
    • Matrix.CreateRotationX-Y-Z

    第一个方法创建一个平移矩阵,你可以定义将模型沿着X、Y和Z方向移动的距离。第二个方法让你可以缩放模型,第三个方法返回绕X、Y和Z轴旋转的矩阵。

    你可以乘以这些矩阵组合多个变换,但必须按照正确的顺序,这会在本教程中讨论到。

    工作原理

    当将模型绘制到屏幕上时,会将它的初始位置放置在3D空间的(0,0,0)点上,而最常见的操作就是让模型在3D场景中移动。

    这很简单。在Draw方法中定义一个时间变量代表程序开始以来经过的秒数(精确起见使用毫秒,然后除以1000)。然后定义一个矩阵保存沿X轴方向的平移,而平移量由当前时间决定:

    float time = (float)gameTime.TotalRealTime.TotalMilliseconds/1000.0f; 
    Matrix worldMatrix = Matrix.CreateTranslation(time, 0, 0); 

    当将这个矩阵设置为世界矩阵时,可以让模型以每秒一个单位的速度沿x方向移动。下面的代码使用这个矩阵绘制模型:

    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (BasicEffect effect in mesh.Effects) 
        { 
            effect.EnableDefaultLighting(); 
            effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; 
            effect.View = fpsCam.GetViewMatrix(); 
            effect.Projection = fpsCam.GetProjectionMatrix(); 
        }
        mesh.Draw(); 
    } 

    要让模型中的所有部分都在正确的位置,需要使Bone变换作为世界矩阵的一部分 (可见教程4-9学习bone变换的更多知识)。但这次,你通过将bone变换矩阵与平移矩阵相乘将两者组合起来,一定要将世界矩阵放置在乘式的右边,因为在矩阵乘法中顺序是很重要的。当你运行这个代码时,模型会缓缓地沿着x轴运动。

    对于缩放和旋转方法类似。试着使用下面得世界矩阵:

    Matrix worldMatrix = Matrix.CreateScale(time); 

    开始时time = 0,模型看不见,随后慢慢变大。一秒后达到原始大小,之后会继续变大。接下来使用这个矩阵:

    Matrix worldMatrix = Matrix.CreateRotationY(time); 

    模型会持续地绕y轴旋转。

    组合多个变换

    通常情况下,你会组合多个变换作为世界矩阵。例如,你想将一个角色移动到一个新位置,将它旋转到行走的方向并对它进行缩放。

    因为这是一个小例子,所以使用下列世界矩阵:

    Matrix worldMatrix = Matrix.CreateTranslation(time, 0, 0) * 	Matrix.CreateRotationY(time/10.0f); 

    这个组合矩阵将作为模型的世界矩阵。但是你可以用另一种方式组合这两个矩阵:

    Matrix worldMatrix = Matrix.CreateRotationY(time / 10.0f) * Matrix.CreateTranslation(time, 0, 0); 

    当你运行程序后会发现结果是不同的。前面已经提到,在矩阵数学中,乘法的顺序是很重要的,接下来我将讨论这个问题。

    矩阵乘法的顺序

    在矩阵数学中,将矩阵M1乘以矩阵M2的结果通常与将M2乘以M1的结果是不同的,在下面的章节中,我会讨论所有可能的组合。

    有一个规则(或者说是技巧)你必须记得:在矩阵乘法中,M1*M2就是“M1在M2之后”的意思。

    旋转与另一个旋转的组合

    旋转矩阵乘法的顺序是很重要的,这是因为当你首先绕A1轴旋转然后绕A2轴旋转,那么绕A1轴的旋转会在绕A2轴旋转前改变A2轴!

    例如,M1表示绕向右轴旋转90度,M2表示绕向上轴旋转90度。

    首先看一下M1*M2的情况,它的意思是“在绕向上轴之后绕向右轴旋转。”想一下结果会如何。在这种情况中,你的右手臂始终对着向右方向。你首先绕向上轴旋转90度,这样你会面向左方,如图4-2左图所示。当你看一下右手臂,你会发现它跟着你一起旋转!所以当你绕向右轴旋转90度后,你会面朝下躺倒在地,如图4-2的右图所示。

    1

    图4-2 先绕Up轴旋转后绕Right轴旋转

    下面看一下第二种情况M2*M1,表示“在绕Right轴旋转后绕Up轴旋转。”你首先绕你的向右方向旋转,结果是面朝下,如图4-3的左图所示。你的向上方向变成了水平,即世界坐标系的向前方向。然后当你绕这个向前方向旋转时,结果是你面朝侧面,如图4-3的右图所示!

    2

    图4-3 先绕Right轴旋转后绕Up轴旋转

    如你所见,M1*M2和M2*M1会导致不同的结果,这是因为当组合两个旋转时,第二个旋转轴会受到第一个旋转地影响。

    旋转与平移的组合

    在这种情况中,乘法的顺序仍是重要的。在这个例子中,M3表示绕Up轴旋转90度,M4表示沿x轴方向移动10个单位。 M3*M4表示“平移后旋转,”模型会首先移动到新位置。然后绕Up轴旋转,这两步如图4-4所示。

    3

    图4-4 先平移后旋转

    M4*M3的情况不同。首先整个坐标系(包括模型和x轴)绕Up轴旋转90度,如图4-5左图所示。然后,模型沿着旋转过的x轴移动10个单位。但初始的x轴已经旋转了,本来它位于你的右方,但现在在你的前方!这意味着你实际上是沿世界坐标系向前的z轴在做平移。

    4

    图4-5 先旋转后平移

    缩放与平移的组合

    缩放和平移的乘法顺序还是重要的。本例中。M5表示缩放0.5倍,M6表示沿x轴平移10个单位。 M5*M6表示先平移后缩放。所以首先模型向右沿x轴平移10个单位,然后缩小,如图4-6所示。

    5

    图4-6 先平移后缩放

    M6*M5的情况中你首先将整个坐标系(包括模型和x轴)缩小,然后缩小的模型沿着缩小的x轴移动10个单位。因为x轴也缩小了一半,所以只移动了相当于5个单位!结果是模型移动的不够远,如图4-7所示。

    6

    图4-7 先缩放后平移

    安全的组合

    幸运的是,有些变换无需考虑乘法的顺序。例如,组合两个平移矩阵是安全的,因为模型只是简单地移动了两次。两个缩放变换的组合也是安全的。

    例如,先放大2倍再缩小10倍和先缩小10倍再放大2倍结果是一样的。最后,缩放不影响旋转,反之亦然。这是因为只会缩放坐标轴,但轴之间的角度仍保持90度不变。所以当缩放矩阵和旋转矩阵相乘时,你可以不用考虑乘法的顺序。

    译者注:在《Microsoft XNA Game Studio Creator’s Guide》一书的第5章提到一个小技巧:使用I.S.R.O.T.作为矩阵组合的顺序通常就是你想要的结果,此处I.S.R.O.T. 分别代表Identity,Scale,Revolve,Orbit,Translate。

    代码

    下面的代码组合了旋转和平移矩阵:

    //draw model 
    float time = (float)gameTime.TotalRealTime.TotalMilliseconds/1000.0f; 
    Matrix worldMatrix = Matrix.CreateScale(0.005f)* Matrix.CreateRotationY(time / 10.0f) * Matrix.CreateTranslation(time, 0, 0); 
    myModel.CopyAbsoluteBoneTransformsTo(modelTransforms); 
    foreach (ModelMesh mesh in myModel.Meshes) 
    {
        foreach (BasicEffect effect in mesh.Effects) 
        { 
            effect.EnableDefaultLighting(); 
            effect.World = modelTransforms[mesh.ParentBone.Index] * worldMatrix; 
            effect.View = fpsCam.ViewMatrix; 
            effect.Projection = fpsCam.ProjectionMatrix; 
        }
        mesh.Draw(); 
    } 

    7

  • 相关阅读:
    交易盈利核心
    tbquant 两个画线函数的说明
    胜率40% 盈亏2:1 交易策略源码
    Apache是如何运作的
    JSON_UNESCAPED_UNICODE的作用与理解
    Python的装饰器是什么?
    Python中的浅拷贝、深拷贝和赋值之间有什么区别?
    Python面试题——基础篇
    GD32F30x_ADC电压采集(规则并行+DMA方式)
    GD32F30x_定时器输出比较模式输出方波(DMA方式)
  • 原文地址:https://www.cnblogs.com/AlexCheng/p/2120144.html
Copyright © 2020-2023  润新知