【译】开始构建三维图形引擎:线性变换

发表于2016-01-03
评论3 2.3k浏览

开始构建三维图形引擎:线性变换

  翻译出处:http://gamedevelopment.tutsplus.com/tutorials/lets-build-a-3d-graphics-engine-linear-transformations--gamedev-7716

  该系列文章参考链接:Let’s Build a 3D Graphics Software Engine.

  欢迎来到三维图形引擎系列的第二部分!本文我们将讨论线性变换(linear transformations),可以对向量进行旋转、缩放等操作以及如何将它们应用于我们已经定义好的类中。

  如果你没有读过该系列的第一部分the first part of this series,我建议你现在阅读一下第一部分。为了防止你忘记之前的内容,下面是一个上节内容的快速回顾。

上面两个类将会是我们整个图形引擎的基础,第一个类表示一个点(空间中的一个实际位置),第二个类表示一个向量(两个点之间的空间)。

  为了讨论线性变换,你需要在类Point中做一些小的变动:不同于之前将数据输出到一个控制台中的方法,而是使用你喜欢的图形API函数,使用对应的函数将当前的点绘制在屏幕中。(译者注:即将计算的点绘制在屏幕中)


1、线性变换基础

  一个小提示:线性变换公式看起来要比实际情况更糟糕。其中将会涉及到一些三角函数,但是实际上你并不需要知道如何进行三角变换,我将会解释每一个函数分别包含哪些输入和输出数据,对于中间处理过程,你可以使用任何计算或数学的第三方库来完成。

提示:如果你想要更深入的了解这些公式,请观看如下视频(video),和PDF文档(PDF)。

所有的线性变换都可以表示为如下形式:

B = F(A)

上面公式是说,如果有一个线性变换函数F()和一个输入向量A,那么将会得到一个输出向量B。

上面提到的内容——两个向量和一个函数,都可以被表示为一个矩阵。向量A和B是1x3的矩阵,线性变换F是一个3x3的矩阵(称之为变换矩阵(transformation matrix))。

这意味着,当你表达上述公式时,它看起来是下面的样子:

如果你之前学过三角函数或者是线性代数,你可能会想到噩梦般的矩阵数学。幸运的是,有一种简单的方法来写下上述公式。它看起来如下:

  然而,这些公式需要通过两个输入来计算,例如在旋转操作中,需要同时指定一个向量和旋转量。让我们看一下旋转矩阵是如果工作的。


2、旋转矩阵

  按照定义,旋转是围绕一个点进行旋转的圆周运动。我们空间的旋转点可以是以下三种情况之一:XY平面、XZ平面和YZ平面(每一个平面是由两个我们之前定义的基向量组成的)。

 

定义的三个旋转点意味着有三个分离的旋转矩阵,如下所示:

XY旋转矩阵:

XZ旋转矩阵:

YZ旋转矩阵:

  因此,将一个点A绕着XY平面旋转90角度(pi/2弧度,大多数数学库具有弧度和角度互相转换的函数),你可以进行如下操作:

因此,如果初始点是A(3, 4, 5),那么输出点会是B(-4, 3, 5)。


练习旋转函数

作为一个练习,尝试为类Vector创建三个新的函数。一个绕着XY平面旋转,一个绕着YZ平面旋转,一个绕着XZ平面旋转。函数应当将旋转的角度作为输入参数,然后将旋转之后的向量作为输出。

该函数的基本流程如下所示:

  1. 创建输出向量。
  2. 将输入的角度转换为弧度形式。
  3. 通过使用上面的公式计算输出向量。
  4. 返回输出向量。

3、缩放矩阵
  缩放是通过一个缩放因子将对象进行放大或者缩小的变换。
  该变换相对简单(至少比旋转操作简单)。一个缩放矩阵需要两个输入参数:一个输入向量和一个缩放三元组,用于定义输入向量分别在空间的对应轴应当缩放的大小。
  例如,给定缩放三元组(s0, s1, s2),s0表示沿着X轴方向进行缩放,s1和s2同理。
  缩放变换矩阵如下(s0, s1, s2是三元组对应的元素):

 为了使输入向量A(a0, a1, a2)沿着X轴方向放大两倍(使用缩放三元组 S = (2, 1, 1)),数学公式如下:

 因此,如果给定输入向量A = (3, 4, 0),那么输出向量B将会是(6, 4, 0)。

练习:缩放函数

另外一个练习,为类vector增加一个新的函数。该函数接收一个三元元组,返回一个输出向量。

该函数的基本流程如下所示:

      创建一个输出向量。
      根据上面公式计算输出向量(可以简化为y0 = x0 * s0; y1 = x1 * s1; y2 = x2 * s2)。
      返回输出向量。

    4、开始构建
      至此,我们得到了线性变换。让我们快速的构建一个小的程序来展示新的技能。我们将会创建一组点绘制到屏幕上,然后将它们作为整体通过线性变换修改它们。
      在开始之前,我们在类Point中增加另外一个函数。这个函数叫做setPointToPoint(),这样将会简化将当前点设置为传入点的操作。接收一个点作为输入,不返回任何值。
      在
    开始之前,我们在类Point中增加另外一个函数。这个函数叫做setPointToPoint()这样将会简化将当前点设置为传入点操作。接收一个点作为输入,不返回任何值。

    下面是该程序的一些快速的描述

             程序使用一个包含100个点的数组。

             按下按键D,程序清除屏幕,绘制点。

             按下按键A,程序对所有的点执行0.5缩放。

             按下按键S,程序对所有的点执行2.0缩放。

             按下按键R,程序对所有点绕着XY平面旋转15度。

             ESC按下,程序退出。

    当前的两个类如下所示:

    示例程序代码如下:

    现在你有一个很酷的小程序来展示你的新技术。可以查看示例demo。


    5、结论

      尽管我们没有涉及所有可能的线性变换,但是我们小引擎已经开始成形了。

      同样的,为了简化引擎,有一些剩余内容(错切变换等)。如果你想要得到更多的线性变换的内容,你可以查看维基百科。

    该系列的下一个部分,我们将会处理不同的观察空间以及如果对目标进行拣选。

    如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引