我如何缀以从头人类可读的角度旋转矩阵 [英] How do I compose a rotation matrix with human readable angles from scratch

查看:282
本文介绍了我如何缀以从头人类可读的角度旋转矩阵的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想编写一个立方体(和其他形状)的3D旋转(这只是发生,每20秒钟或更长时间一次,所以计算成本在这里心不是一个问题),而无需访问的Open GL函数来为我做因为这是一个绘制调用完成每一次有新的变化显示列表中

一件事,总是从做3D编程阻碍我的是没能理解数学的工作。我可以附和着数学罚款编程流程使用方法和功能,那么它所有的清晰,逻辑性,但在数学符号,我只是不能从中做出正面或反面。

我一直在阅读的网站,看视频的机构试图解释这一点,但他们都用数学符号,我只是迷失在里面......我的脑海里会不会把它翻译的东西可以理解的。我可能有一个缺陷存在。

我会加悬赏这个问题,所以如果你向我提供usuable答案,多点给你!(和谷歌,真的不工作的人谁不翻译数学,因为我从来没有得到一个坚实的基础它在学校/老师没有能够解释它可以理解的术语) 此外,只要在用别人的code不是我的兴趣,我想了解它背后的机制,在逻辑。我很乐意用别人的code,但我真的想了解它是如何工作的。

问题

你能向我解释深入浅出的的数学符号,只是编程符号/功能/伪code,如何实现矩阵变换以及所有的3轴。 理想的情况是我想要的是材料/理解写的方法/对象,在那里我可以定义3轴相似的角度glRotate旋转的四边形/三角形我也有收藏。

我做了什么?

我试图在做一个90度的变换函数来获得数学的窍门,但完全在作出适当的基质这在理论上应该是最容易做的失败。你可以看到在 http://jsfiddle.net/bLfg0tj8/5/ 绿色的文字是一部开拓创新的三角形,白点的中心点,红点失败的转换(我想,因为它不围绕中心点对齐)。本教程中,我在想我怎么矩阵组合成组合矩阵,但我想我搞砸了某个地方。 正如我所说的......它真的很难,我理解数学符号并讲话。而没有帮助的是,大多数教师跳过部分说明。花了我一个人2小时的倍增你需要添加的每一步在一起,而不是只是不断mutiplying矩阵来理解。耶作出解释。

:一种是我用/工作实际的例子要使用

例如我有一个立方体,从位于世界波前OBJ文件加载在

  X = 50
Y = 100
Z = 200
 

立方体采用四边形和部分UV贴图绘制。没有问题在这里。它与所有的纹理显示正确呈现漂亮。 这些是位置坐标为立方体的每个面,这是使用一个四绘制。

  //前脸
-1.0,-1.0,1.0,
 1.0,-1.0,1.0,
 1.0,1.0,1.0,
-1.0,1.0,1.0,

//背面
-1.0,-1.0,-1.0,
-1.0,1.0,-1.0,
 1.0,1.0,-1.0,
 1.0,-1.0,-1.0,

//顶面
-1.0,1.0,-1.0,
-1.0,1.0,1.0,
 1.0,1.0,1.0,
 1.0,1.0,-1.0,

//底面
-1.0,-1.0,-1.0,
 1.0,-1.0,-1.0,
 1.0,-1.0,1.0,
-1.0,-1.0,1.0,

//右脸上
 1.0,-1.0,-1.0,
 1.0,1.0,-1.0,
 1.0,1.0,1.0,
 1.0,-1.0,1.0,

//左脸
-1.0,-1.0,-1.0,
-1.0,-1.0,1.0,
-1.0,1.0,1.0,
-1.0,1.0,-1.0
 

所以这个工程的所有伟大的。但是,如果我要把这个立方体旋转沿x轴旋转90度和45度,绕Z轴?通过OpenGL函数,我不能使用glRotate因为此刻我的数据传递给tesselator对象,我不能做任何矩阵转换到它,因为它只是采取的数据,而不是实际渲染它本身。

存储数据的方式是如下:

  WaveFrontObject()
   |
   |  - >组(字符串组名)
        |
        |  - >面()
              |
              |  - >顶点(浮X,浮动Y,浮Z)[]
              |  - >浮UVmap []对应于每一个顶点
              |  - > drawFace()//绘制面为四或三角形
 

所以,上述每个坐标我给存储为一个面组中的魔方波前的对象。

当立方体被添加到tesselator它被转换为右坐标中的世界,并将其呈现正常

它总是呈现但是同样的。如果我希望它渲染成一个角度,我将不得不作出这时一个单独的波阵面目的是能够做到这一点。在我opnion就是疯狂做时,它可以与一些数学来解决。

所需的答案

  1. 一步如何建立一个转换矩阵,并试图解释算算我解释一步。
  2. 说明如何翻译矩阵应用于四边形/在脸上三角形惠斯特,他们一直注重围绕他们的位置的中心

    X = 50.5 Y = 100.5 Z = 200.5

  3. 一些例子/伪code一起去解释。

用于说明所使用的编程语言是不是真的相关,只要其在C系列

请尽量远离数学符号/说话......我不知道该怎么α+β,thetha是,我不知道什么是X轴,Y轴和Z轴。我不知道是什么角度,但我不知道数学家找到它的名字。 如果你希望使用数学的名字,请向我解释什么,他们都在3D世界/ code和它们是如何形成的/计算的。

我只是想使沿线

的线的方法/对象

  Matrix.transformVertices(顶点[],90度的x,45度Y,0度Z);
 

解决方案

所以,真正的问题是了解的4x4同质变换矩阵

也没有落后,只剩下数学是几何重新presentation /意义远远更好地为人类的抽象/理解。

1。因此,4x4矩阵是什么?

有重新一些笛卡尔presentation坐标系统和它的组成如下:

  1. 3 基向量(每个轴一个)的红,绿,蓝

    因此​​,如果红,绿,蓝矢量互相垂直,然后坐标系是正交。如果他们也是单位向量那么它的正交(例如像单位矩阵)。

  2. 原点灰色

  3. 投影和均匀的面(基质无人盯防下休息)

    这部分是有只为实现旋转和平移一次,所以点使用必须是同质的,这意味着在形式上(X,Y,Z,W = 1) 。如果这只是(X,Y,Z)则矩阵将 3×3 ,这是不够的翻译。我不会用它们不易解释几何任何预测。

这个布局是从 OpenGL的符号也有换位重新presentation那里(向量行不列)

现在如何改造的任何一点到/从这个坐标系:

  G = M * L,
升=逆(M)*克;
 

其中:

  • M 是变换矩阵
  • M 局部坐标系点(LCS)
  • 先按g 是全局坐标系点(GCS)

对于转版(的DirectX ),它是:

  L = M *克;
G =逆(M)* L;
 

这是因为转正交矩阵也是自身反

2。如何对其进行可视化

是的,你可以得出矩阵号码,但他们没有在第一次看,特别是如果这些数字正在改变,以便得出轴系向量作为图像上方意义。其中每个轴是从线原产地产地+ line_size * axis_vector

3。如何构建它

只是计算轴矢量和来源,并把它们的内部矩阵。为了确保正交性剥削的叉积(但要小心为了multiplicants的使用方向是正确的)

4。效果

  • 在转动是通过旋转轴系这样你就可以通过参数圆式...
  • 计算每个轴做
  • 在缩放通过缩放系数相乘轴系做
  • 在偏斜只是用非垂直轴系

5。旋转

有关大多数情况下,增量旋转被使用。有两种类型的

  • 局部旋转 M'= M * rotation_matrix 它绕局部坐标轴系像你将控制飞机或汽车或玩家。 。大多数引擎/游戏不使用这些和捏造事实是欧拉角,而不是这是一个廉价的解决方案(有许多怪癖和问题),因为使用OpenGL谁大多数人甚至不知道这是可能的,而堆栈列表 glRotate / glTranslate 要求...

  • 全局循环 M'=反(反(M)* rotation_matrix)它绕全局坐标系等机件。

其中, rotation_matrix 是任何标准的旋转变换矩阵。

如果你有不同的矩阵布局(转),那么局部和全局的旋转计算周围的其他方法...

您也可以计算出你的 rotation_matrix 3 的角度想:

  rotation_matrix = rotation_around_x(AX)* rotation_around_y(AY)* rotation_around_z(AZ);
 

维基旋转矩阵的3D 接收,RY,RZ 基本转是你所需要的。正如你可以看到他们只是单位圆的参数方程真的。乘法变化的顺序的角度如何收敛到目标位置。这就是所谓的欧拉角,我不使用它(我整合步骤的变化,而不是有没有限制,如果处理得当更何况它更简单)。

6。 glRotate

如果你想 glRotate 那么你应该使用四元数,而不是因为这是绕轴旋转不是3角!有解决方法:

  1. 创建变换矩阵 N 该轴
  2. 然后将您的矩阵 M
  3. 旋转 N 由尖
  4. 然后变换 M N 为全局坐标回

要转换Matrix /从Matrix在这种情况下,只是变换轴系的分离开起源不过是 N 的起源一定是(0, 0,0)!

7。使用

转换是累积的,这意味着:

  • P'= M1 * M2 * M3 * M4 * P; 是一样的 M = M1 * M2 * M3 * M4; P'= M * P

所以,如果你有很多点进行改造,那么你precompute全部转换到一个矩阵,并只使用它。不需要通过所有后续的矩阵相乘点。现在好了这个概念:

你应该有 3 坐标系:

  • 摄像头 C
  • 在世界上(通常是单位矩阵)
  • 对象 0 (每个对象都有自己的矩阵)

所以如果你有立方体 8 顶点 P0,...,P7 ,那么你必须进行转化每个点的对象本地坐标摄像机本地坐标。一些显卡的API做一些,所以你只适用于你有什么让你真正需要的:

  • P(I)=逆(C)*单元* M * P(I);

的转换是累积的单位矩阵不会改变任何东西这样:

  • Q =逆(C)* M; P(I)= Q * P(I);

让绘图计算前问:的drawed对象,然后取每个点 P(I)的对象和计算的转化 P(I)并提请/使用变换之一... ...的 P(I)是在本地摄像机坐标系(x,屏幕中的Y),但没有任何角度来看,这样画,你也可以通过以Z cordinate在添加任何的投影矩阵和分裂前到底...投影也是累积的,因此可也放在问:

C ++的例子

  // $$ ----表格CPP ----
// ------------------------------------------------ ---------------------------
//除了math.h中包括可以忽略这台机器产生的VCL与code
#包括< vcl.h>
的#pragma hdrstop后面
#包括win_main.h
#包括<文件math.h>
// ------------------------------------------------ ---------------------------
的#pragma包(smart_init)
的#pragma资源* .DFM
TMain *主; //指向主窗口...
// ------------------------------------------------ ---------------------------
//这是最重要的东西,一些数学第一
// ------------------------------------------------ ---------------------------
常量双度= M_PI / 180.0;
双分频(双X,双Y);
无效matrix_mul(双* C,双*一,双* B); // C [16] = A [16] * B [16]
无效matrix_mul_vector(双* C,双*一,双* B); // C [4] =一个[16] * B [4]
无效matrix_subdet(双* C,双*一); // C [16] =的所有subdets [16]
一// = subdet(R,S)[16];双matrix_subdet(双*一,INT R,int类型)
双matrix_det(双*一); // = DET的[16]
双matrix_det(双*一,双* B); // = DET的[16]和subdets B〔16〕
无效matrix_inv(双* C,双*一); // C [16] = A [16] ^ -1
// ------------------------------------------------ ---------------------------
双分频(双X,双Y)
        {
        如果(Y!)返回0.0;
        返回X / Y;
        }
无效matrix_mul(双* C,双*一,双* B)
        {
        双Q [16];
        Q [0] =(一个[0] * B [0])+(一个[1] * B [4])+(一个[2] * B [8])+(一个[3] * B [12 ]);
        Q [1] =(一个[0] * B [1])+(一个[1] * B [5])+(一个[2] * B [9])+(一个[3] * B [13 ]);
        则q [2] =(一个[0] * B [2])+(一个[1] * B [6])+(一个[2] * B [10])+(一个[3] * B [14 ]);
        Q [3] =(一个[0] * B [3])+(一个[1] * B [7])+(一个[2] * B [11])+(一个[3] * B [15 ]);
        则q [4] =(一个[4] * B [0])+(一个[5] * B [4])+(一个[6] * B [8])+(一个[7] * B [12 ]);
        则q [5] =(一个[4] * B [1])+(一个[5] * B [5])+(一个[6] * B [9])+(一个[7] * B [13 ]);
        则q [6] =(一个[4] * B [2])+(一个[5] * B [6])+(一个[6] * B [10])+(一个[7] * B [14 ]);
        Q [7] =(一个[4] * B [3])+(一个[5] * B [7])+(一个[6] * B [11])+(一个[7] * B [15 ]);
        Q [8] =(一个[8] * B [0])+(一个[9] * B [4])+(一个[10] * B [8])+(一个[11] * B [12 ]);
        则q [9] =(一个[8] * B [1])+(一个[9] * B [5])+(一个[10] * B [9])+(一个[11] * B [13 ]);
        则q [10] =(一个[8] * B [2])+(一个[9] * B [6])+(一个[10] * B [10])+(一个[11] * B [14 ]);
        则q [11] =(一个[8] * B [3])+(一个[9] * B [7])+(一个[10] * B [11])+(一个[11] * B [15 ]);
        则q [12] =(一个[12] * B [0])+(一个[13] * B [4])+(一个[14] * B [8])+(一个[15] * B [12 ]);
        则q [13] =(一个[12] * B [1])+(一个[13] * B [5])+(一个[14] * B [9])+(一个[15] * B [13 ]);
        则q [14] =(一个[12] * B [2])+(一个[13] * B [6])+(一个[14] * B [10])+(一个[15] * B [14 ]);
        则q [15] =(一个[12] * B [3])+(一个[13] * B [7])+(一个[14] * B [11])+(一个[15] * B [15 ]);
        的for(int i = 0; I< 16;我++)C [i] = Q [我]
        }
无效matrix_mul_vector(双* C,双*一,双* B)
        {
        双则q [3];
        Q [0] =(一个[0] * B [0])+(一个[1] * B [1])+(一个[2] * B [2])+(一个[3]);
        Q [1] =(一个[4] * B [0])+(一个[5] * B [1])+(一个[6] * B [2])+(一个[7]);
        则q [2] =(一个[8] * B [0])+(一个[9] * B [1])+(一个[10] * B [2])+(一个[11]);
        对(INT I = 0; I&所述; 3;我+ +)C [I] = Q [I];
        }
无效matrix_subdet(双* C,双*一)
        {
        双Q [16];
        INT I,J;
        对于(I = 0; I&4;;我+ +)
         为(J = 0; J&4;; J ++)
          Q [J +(I&其中; 2)] = matrix_subdet(A,I,j)的;
        对于(i = 0; I< 16;我++)C [i] = Q [我]
        }
双matrix_subdet(双*一,INT R,int类型)
        {
        双C,Q [9];
        INT I,J,K;
        K = 0; // Q =子矩阵
        为(J = 0; J&4;; J ++)
         如果(j!= S)
          对于(I = 0; I&4;;我+ +)
           如果(我!= R)
                {
                Q [k]的一个= [I +(J&其中; 2)];
                ķ++;
                }
        C = 0;
        C + = Q [0] * Q [4] * Q; [8]
        C + = Q [1] * Q [5] * Q [6];
        C + = Q [2] * Q [3] * Q [7];
        C- = Q [0] *则q [5] * Q [7];
        C- = Q [1] * Q [3] * Q [8];
        C- = Q [2] * Q [4] * Q [6];
        如果(中间体((R + S)及1))C = -c; //添加正负号
        返回℃;
        }
双matrix_det(双*一)
        {
        双c = 0;
        C + =一个[0] * matrix_subdet(一,0,0);
        C + =一个[4] * matrix_subdet(一,0,1);
        C + =一个[8] * matrix_subdet(一,0,2);
        C + = A [12] * matrix_subdet(A,0,3);
        返回℃;
        }
双matrix_det(双*一,双* B)
        {
        双c = 0;
        C + =一个[0] * B [0];
        C + =一个[4] * B [1];
        C + =一个[8] * B [2];
        C + =一个[12] * B [3];
        返回℃;
        }
无效matrix_inv(双* C,双*一)
        {
        双D [16],D;
        matrix_subdet(D,A);
        D = matrix_det(A,D);
        如果(D),D = 1.0 / D;
        的for(int i = 0; I< 16;我++)C [i] = D [I] * D;
        }
// ------------------------------------------------ ---------------------------
//现在对象重新presentation
// ------------------------------------------------ ---------------------------
const int的PNTS = 8;
双PNT [PNTS * 3 = //顶点为100x100x100立方体中心在(0,0,0)
    {
    -100.0,-100.0,-100.0,
    -100.0,+ 100.0,-100.0,
    + 100.0 + 100.0,-100.0,
    + 100.0,-100.0,-100.0,
    -100.0,-100.0,+ 100.0,
    -100.0,+ 100.0 + 100.0,
    + 100.0 + 100.0 + 100.0,
    + 100.0,-100.0,+ 100.0,
    };
const int的FACS = 6;
INT FAC [FACS * 4 = //面(点指数使用)无缠绕规则
    {
    0,1,2,3,
    4,5,6,7,
    0,1,5,4,
    1,2,6,5,
    2,3,7,6,
    3,0,4,7,
    };
双代表[16] = // 4×4的变换对象(单位从开始)的基质(0,0,+ 100)
    {
    1.0,0.0,0.0,0.0,
    0.0,1.0,0.0,0.0,
    0.0,0.0,1.0,100.0,
    0.0,0.0,0.0,1.0,
    };
双眼睛[16] = // 4×4变换摄像头矩阵在(0,0,-150)
    {
    1.0,0.0,0.0,0.0,
    0.0,1.0,0.0,0.0,
    0.0,0.0,1.0,-150.0,
    0.0,0.0,0.0,1.0,
    };
// ------------------------------------------------ ---------------------------
//这是如何绘制
// ------------------------------------------------ ---------------------------
无效OBJ(双* PNT,INT PNTS,为int * FAC,诠释流式细胞仪,双*代表,双* ieye)
    {
    //变量绘图
    INT I;
    双P0 [3],p1的[3],p2的[3],P3 [3],米[16],D;
    // GFX API变量(改变你的东西)主要是这个应用的主要形式
    的TCanvas * SCR = Main-> BMP-&G​​T;帆布;
    双XS2 = Main-> ClientWidth / 2,YS2 = Main-> ClientHeight / 2;
    双V = XS2 *棕褐色(30.0 *度); // 60度视角透视投影

    matrix_mul(男,ieye,代表); //累积所有需要变换

    对于(i = 0; I< FACS * 4;)//通过所有的面
        {
        //转换的脸上所有点
        matrix_mul_vector(P0,M&放大器; PNT [FAC [我] * 3]);我++;
        matrix_mul_vector(P1,M&放大器; PNT [FAC [我] * 3]);我++;
        matrix_mul_vector(P2,M&放大器; PNT [FAC [我] * 3]);我++;
        matrix_mul_vector(P3,M&放大器; PNT [FAC [我] * 3]);我++;
        //这里去,如果需要用Z透视分割坐标
        D =除法(ⅴ,P0 [2]); P0 [0] * = D; P0 [1] * = D;
        D =鸿沟(V,P 1 [2]); P1 [0] * = D; P1 [1] * = D;
        D =除法(ⅴ,P2 [2]); p2的[0] * = D; p2的[1] * = D;
        D =除法(ⅴ,P3 [2]); p3的[0] * = D; P3 [1] * = D;
        //这里是视口转换(只是翻译(0,0)到屏幕的中间在这种情况下,
        P0 [0] + = XS2; P0 [1] + = YS2;
        P1 [0] + = XS2; P1 [1] + = YS2;
        p2的[0] + = XS2; p2的[1] + = YS2;
        p3的[0] + = XS2; P3 [1] + = YS2;
        //绘制四
        //我使用VCL GDI的TCanvas你用你所拥有的...
        //和线框只能保持这种简单(没有Z缓存,缠绕扑杀,...)
        scr->养老金>颜色= clAqua; //周边线框
        scr->通过MoveTo(P0 [0],P0 [1]);
        scr-> LineTo函数(P1 [0],P1 [1]);
        scr-> LineTo函数(p2的[0],P2 [1]);
        scr-> LineTo函数(P3 [0],p3的[1]);
        scr-> LineTo函数(P0 [0],P0 [1]);
// scr->养老金>颜色= clBlue; //脸十字visualy检查,如果我正确地产生FAC []
// scr->通过MoveTo(P0 [0],P0 [1]);
// scr-> LineTo函数(p2的[0],P2 [1]);
// scr->通过MoveTo(P1 [0],P1 [1]);
// scr-> LineTo函数(P3 [0],p3的[1]);
        }
    }
// ------------------------------------------------ ---------------------------
// ------------------------------------------------ ---------------------------
无效TMain ::平局()
    {
    如果回报(_redraw!);
    BMP-&G​​T; Canvas->电刷>颜色= clBlack;
    BMP-&G​​T; Canvas-> fillRect方法(TRect(0,0,XS,YS));

    //摄像机的反算需要计算只是一次对所有对象
    双ieye [16];
    matrix_inv(ieye,眼);
    //绘制所有对象
    OBJ(PNT,PNTS,FAC,流式细胞仪,代表,ieye);

    Main-> Canvas->绘制(0,0,BMP);
    _redraw = FALSE;
    }
// ------------------------------------------------ ---------------------------
__fastcall TMain :: TMain(TComponent的*业主):TForm的(所有者)
    {
    //窗口的构造函数,你可以忽略这个...(这里只是创建一个后备缓冲位图)
    BMP =新的Graphics :: TBitmap;
    BMP-&G​​T; HandleType = bmDIB;
    BMP-&G​​T;的PixelFormat = pf32bit;
    PYX = NULL;
    }
// ------------------------------------------------ ---------------------------
无效__fastcall TMain :: FormDestroy(TObject的*发件人)
    {
    //窗口的析构函数释放内存......也ignoe这个
    如果(PYX)删除PYX;
    删除BMP;
    }
// ------------------------------------------------ ---------------------------
无效__fastcall TMain :: FormResize(TObject的*发件人)
    {
    //在调整大小事件...只是调整/重绘后备缓冲也可以忽略此
    XS = ClientWidth; XS2 = XS>大于1;
    YS = ClientHeight; YS2 = YS>大于1;
    BMP-&G​​T; WIDTH = XS;
    BMP-&G​​T;身高=伊苏;
    如果(PYX)删除PYX;
    PYX =新INT * [YS]
    对于(INT Y = 0; Y< YS; Y ++)PYX [Y] =(INT *)BMP-&G​​T;扫描线[Y]
    _redraw = TRUE;
    }
// ------------------------------------------------ ---------------------------
无效__fastcall TMain :: FormPaint(TObject的*发件人)
    {
    //重画事件可以忽略
    _redraw = TRUE;
    }
// ------------------------------------------------ ---------------------------
无效__fastcall TMain :: tim_redrawTimer(TObject的*发件人)
    {
    //定时器事件动画立方体...
    _redraw = TRUE;

    //旋转对象看到它在运动
    双昂,C,S;

    ANG = 5.0 *度; C = COS(ANG​​); S = SIN(ANG); //每一步计时器5度旋转baroundž
    双RZ [16] = {C,S,0,0,
                    -s,C,0,0,
                     0,0,1,0,
                     0,0,0,1};

    ANG = 1.0 *度; C = COS(ANG​​); S = SIN(ANG); //旋转baround x除以每个定时器步骤1度
    双RX [16] = {1,0,0,0,
                     0,C,S,0,
                     0,-s,C,0,
                     0,0,0,1};
    matrix_mul(代表,代表,RZ);
    matrix_mul(代表,代表,RX);

    绘制();
    }
// ------------------------------------------------ ---------------------------
 

下面是它的样子:

[注意事项]

  • 如果您有更多的问题则评论我...

I am trying to program a 3d rotation of a cube(and other shapes)(this only happens once every 20 seconds or more, so computation cost isnt an issue here) without having access to open GL functions to do it for me because this is done in one draw call every time something changes in the display list.

The one thing that has always hindered me from doing 3d programming is failing to understand how math works. I can go along with math fine in programming flow using methods and functions, then its all clear and logical to me, but in mathematical notation, I just can't make heads or tails from it.

I have been reading websites, watching video's of institutes trying to explain this, but they all use mathematical notation and I simply get lost in it... my mind won't translate it to something understandable. I might have a defect there.

I will add a bounty to this question, so if you provide me with a usuable answer, many points to you!(And google it, really doesnt work for someone who doesn't translate math because I never got a firm basis of it at school/the teachers failed to be able to explain it in understandable terms) Also, Just using someone's code isn't my interest, I want to understand the mechanics behind it, the logic. I'd be happy to use someone elses code, but I really want to understand how it works.

The question

Can you explain to me in simple terms without mathematical notation, just programming notation/functions/psuedocode, how to implement a matrix transform along all 3 axis. Ideally what I want is the material/understanding to write a method/object where I can define the angles of 3 axes similar to glRotate to rotate the collection of quads/triangles I have.

What have I done?

I have attempted at making a 90 degrees transform function to get the hang of the math but failed utterly in making a proper matrix which in theory should have been the simplest to do. You can see my failed attempt in all its glory on http://jsfiddle.net/bLfg0tj8/5/ The green text is the orignal triangle, the white point the center point, the red points the failed transformation(I think, because it isn't aligned around the center point). The tutorial I was in thought me how to combine matrices into a combined matrix, but I guess i screwed up somewhere. As I said... its really really hard for me to understand mathematical notation and speak. And not helping is that most teachers skip parts of the explanation. Took me 2 hours alone to understand when multiplying matrices you need to add each step together instead of just keep on mutiplying. yay for explanations.

A practical example what I work with/want to work with

For example I have a cube, loaded from a wavefront obj file located in the world at

x = 50
y = 100
z = 200

The cube is drawn using quads and some uv mapping. no problems here. it renders beautifully with all the textures showing correctly. These are the location coordinates for each "face" of the cube which is drawn using a quad.

// Front face
-1.0, -1.0,  1.0,
 1.0, -1.0,  1.0,
 1.0,  1.0,  1.0,
-1.0,  1.0,  1.0,

// Back face
-1.0, -1.0, -1.0,
-1.0,  1.0, -1.0,
 1.0,  1.0, -1.0,
 1.0, -1.0, -1.0,

// Top face
-1.0,  1.0, -1.0,
-1.0,  1.0,  1.0,
 1.0,  1.0,  1.0,
 1.0,  1.0, -1.0,

// Bottom face
-1.0, -1.0, -1.0,
 1.0, -1.0, -1.0,
 1.0, -1.0,  1.0,
-1.0, -1.0,  1.0,

// Right face
 1.0, -1.0, -1.0,
 1.0,  1.0, -1.0,
 1.0,  1.0,  1.0,
 1.0, -1.0,  1.0,

// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0,  1.0,
-1.0,  1.0,  1.0,
-1.0,  1.0, -1.0

So this works all great. But what if i want this cube rotated 90 degrees along the x axis and 45 degrees around the z axis? I cannot use glRotate because at the moment I pass the data to the tesselator object I cannot do any matrix transforms to it via the opengl functions because it's just taking in the data, not actually rendering it per se.

The way the data is stored is as following:

WaveFrontObject()
   |
   |-> Groups(String groupname)
        |
        |-> Faces()
              |
              |-> Vertex(float x, float y, float z)[] 
              |-> Float UVmap[] corresponding to each vertex
              |-> drawFace() // Draws the face as a quad or triangle

So each of the above coordinates I gave is stored as a face of the wavefront object in the group "cube".

When the cube is added to the tesselator it is translated to the right coordinates in the world and it renders normal.

It always renders the same however. If i would want it to render at an angle i would have to make a seperate wavefront object at this moment to be able to do that. In my opnion that is madness to do when it can be solved with some math.

Needed in the answer

  1. Explanation step by step how to build a translation matrix and an attempt to explain the math to me.
  2. Explanation how to apply the translation matrix to the quads/triangles in the faces whist they keep oriented around the center of their location

    x = 50.5 y = 100.5 z = 200.5

  3. Some example/pseudo code to go along with the explanation.

The used programming language used to explain isn't really relevant as long as its in the C family

Please try to stay away from mathematical notation/speak... I dont know what alpha beta, thetha is, I do know what x axis, y axis and z axis is. I do know what angles are, but I do not know the names mathematicians find for it. If you wish to use math names, please explain to me what they are in the 3d world/code and how they are formed/calculated.

I simply want to make a method/object along the lines of

Matrix.transformVertices(vertices[], 90deg x, 45 deg y, 0 deg z);

解决方案

So the question really is Understanding 4x4 homogenous transform matrices

well without the math behind the only thing that left is geometric representation/meaning which is far better for human abstraction/understanding.

1. So what the 4x4 matrix is?

It is representation of some Cartesian coordinate system and it is composed of:

  1. 3 basis vectors (one for each axis) red,green,blue

    So if the red,green,blue vectors are perpendicular to each other then the coordinate system is orthogonal. If they are also unit vectors then it is orthonormal (like for example unit matrix).

  2. origin point gray

  3. projection and homogenous side (unmarked bottom rest of the matrix)

    This part is there only for enabling rotation and translation at once, therefore point used must be homogenous that means in form (x,y,z,w=1). If it was just (x,y,z) then the matrix would be 3x3 and that is not enough for translation. I will not use any projections they are uneasy to explain geometrically.

This layout is from OpenGL notation there are also transposed representation out there (vectors are rows not columns)

now how to transform any point to/from this coordinate system:

g=M*l;
l=Inverse(M)*g;

where:

  • M is transform matrix
  • l is M local coordinate system point (LCS)
  • g is global coordinate system point (GCS)

for the transposed version (DirectX) it is:

l=M*g;
g=Inverse(M)*l;

That is because transposed orthogonal matrix is also inverse of itself

2. how to visualize it

Yes you can draw the matrix numbers but they do not make sense at first look especially if the numbers are changing so draw the axises vectors as on image above. Where each axis is a line from origin to origin + line_size*axis_vector

3. how to construct it

Just compute axis vectors and origin and put them inside matrix. To ensure orthogonality exploit cross product (but be careful with order of multiplicants to use the right direction)

4. effects

  • rotation is done by rotating the axises so you can compute each axis by parametric circle equation ...
  • scaling is done by multiplying axises by scale factor
  • skewing is just using non perpendicular axises

5. rotation

For most cases the incremental rotation is used. There are two types

  • local rotation M'=M*rotation_matrix it rotates around local coordinate axises like you will control plane or car or player ... Most engines/games do not use these and fake it with euler angles instead which is a cheap solution (have many quirks and problems) because most people who using OpenGL do not even know this is possible and rather stack list of glRotate/glTranslate calls...

  • global rotation M'=Inverse(Inverse(M)*rotation_matrix) it rotates around global coordinate system axises.

where rotation_matrix is any standard rotation transform matrix.

If you have different matrix layout (transposed) then the rotations local and global are computed the other way around ...

You can also compute your rotation_matrix from 3 angles like:

rotation_matrix=rotation_around_x(ax)*rotation_around_y(ay)*rotation_around_z(az);

see Wiki rotation matrices the 3D Rx,Ry,Rz from Basic rotations are what you need. As you can see they are just unit circle parametric equation really. The order of multiplication change how the angles converge to target position. This is called Euler angles and I do not use it (I integrate step changes instead which has no restrictions if done properly not to mention it is simpler).

6. glRotate

If you want glRotate then you should use quaternions instead because that is rotation around axis not by 3 angles! There is workaround:

  1. create transform matrix N for that axis
  2. then transform your matrix M to it
  3. rotate N by angle
  4. then transform M back from N to global coordinates

To transform Matrix to/from Matrix in this case just transform axises as points and leave the origin as is but the origin of N must be (0,0,0)!!!

7. usage

Transformations are cumulative that means:

  • p'=M1*M2*M3*M4*p; is the same as M=M1*M2*M3*M4; p'=M*p

So if you have many points to transform then you precompute all transformations to single matrix and use just it. Do not need to multiply points by all subsequent matrices. OK now the concept:

you should have 3 coordinate systems:

  • camera C
  • world (usually unit matrix)
  • object O (each object have its own matrix)

so if you have cube with 8 vertexes p0,...,p7 then you have to perform transformation on each point from object local coordinates to camera local coordinates. Some gfx api do some of it so you apply only what you have to so you really need:

  • p(i)'=inverse(C)*unit*M*p(i);

the transforms are cumulative and unit matrix does not change anything so:

  • Q=inverse(C)*M; p(i)'=Q*p(i);

so before drawing compute Q for drawed object then take each point p(i) of the object and compute the transformed p(i)' and draw/use the transformed one ... The p(i)' is in local camera coordinate system (x,y of the screen) but there is no perspective there so before drawing you can also add any of the projection matrices and divide by z cordinate at the end ... The projection is also cumulative so it can be also inside Q

[edit1] C++ example

//$$---- Form CPP ----
//---------------------------------------------------------------------------
// apart from math.h include you can ignore this machine generated VCL related code
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
#include <math.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main; // pointer to main window ...
//---------------------------------------------------------------------------
// Here is the important stuff some math first
//---------------------------------------------------------------------------
const double deg=M_PI/180.0;
double divide(double x,double y);
void  matrix_mul       (double *c,double *a,double *b); // c[16] = a[16] * b[16]
void  matrix_mul_vector(double *c,double *a,double *b); // c[ 4] = a[16] * b[ 4]
void  matrix_subdet    (double *c,double *a);           // c[16] = all subdets of a[16]
double matrix_subdet   (          double *a,int r,int s);//      = subdet(r,s) of a[16]
double matrix_det      (          double *a);           //       = det of a[16]
double matrix_det      (          double *a,double *b); //       = det of a[16] and subdets b[16]
void  matrix_inv       (double *c,double *a);           // c[16] = a[16] ^ -1
//---------------------------------------------------------------------------
double divide(double x,double y)
        {
        if (!y) return 0.0;
        return x/y;
        }
void  matrix_mul       (double *c,double *a,double *b)
        {
        double q[16];
        q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
        q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
        q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
        q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
        q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
        q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
        q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
        q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
        q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
        q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
        q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
        q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
        q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
        q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
        q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
        q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
        for(int i=0;i<16;i++) c[i]=q[i];
        }
void  matrix_mul_vector(double *c,double *a,double *b)
        {
        double q[3];
        q[0]=(a[ 0]*b[0])+(a[ 1]*b[1])+(a[ 2]*b[2])+(a[ 3]);
        q[1]=(a[ 4]*b[0])+(a[ 5]*b[1])+(a[ 6]*b[2])+(a[ 7]);
        q[2]=(a[ 8]*b[0])+(a[ 9]*b[1])+(a[10]*b[2])+(a[11]);
        for(int i=0;i<3;i++) c[i]=q[i];
        }
void  matrix_subdet    (double *c,double *a)
        {
        double   q[16];
        int     i,j;
        for (i=0;i<4;i++)
         for (j=0;j<4;j++)
          q[j+(i<<2)]=matrix_subdet(a,i,j);
        for (i=0;i<16;i++) c[i]=q[i];
        }
double matrix_subdet    (         double *a,int r,int s)
        {
        double   c,q[9];
        int     i,j,k;
        k=0;                            // q = sub matrix
        for (j=0;j<4;j++)
         if (j!=s)
          for (i=0;i<4;i++)
           if (i!=r)
                {
                q[k]=a[i+(j<<2)];
                k++;
                }
        c=0;
        c+=q[0]*q[4]*q[8];
        c+=q[1]*q[5]*q[6];
        c+=q[2]*q[3]*q[7];
        c-=q[0]*q[5]*q[7];
        c-=q[1]*q[3]*q[8];
        c-=q[2]*q[4]*q[6];
        if (int((r+s)&1)) c=-c;       // add signum
        return c;
        }
double matrix_det       (         double *a)
        {
        double c=0;
        c+=a[ 0]*matrix_subdet(a,0,0);
        c+=a[ 4]*matrix_subdet(a,0,1);
        c+=a[ 8]*matrix_subdet(a,0,2);
        c+=a[12]*matrix_subdet(a,0,3);
        return c;
        }
double matrix_det       (         double *a,double *b)
        {
        double c=0;
        c+=a[ 0]*b[ 0];
        c+=a[ 4]*b[ 1];
        c+=a[ 8]*b[ 2];
        c+=a[12]*b[ 3];
        return c;
        }
void  matrix_inv       (double *c,double *a)
        {
        double   d[16],D;
        matrix_subdet(d,a);
        D=matrix_det(a,d);
        if (D) D=1.0/D;
        for (int i=0;i<16;i++) c[i]=d[i]*D;
        }
//---------------------------------------------------------------------------
// now the object representation
//---------------------------------------------------------------------------
const int pnts=8;
double pnt[pnts*3]=     // Vertexes for 100x100x100 cube centered at (0,0,0)
    {
    -100.0,-100.0,-100.0,
    -100.0,+100.0,-100.0,
    +100.0,+100.0,-100.0,
    +100.0,-100.0,-100.0,
    -100.0,-100.0,+100.0,
    -100.0,+100.0,+100.0,
    +100.0,+100.0,+100.0,
    +100.0,-100.0,+100.0,
    };
const int facs=6;
int fac[facs*4]=        // faces (index of point used) no winding rule
    {
    0,1,2,3,
    4,5,6,7,
    0,1,5,4,
    1,2,6,5,
    2,3,7,6,
    3,0,4,7,
    };
double rep[16]=        // 4x4 transform matrix of object (unit from start) at (0,0,+100)
    {
    1.0,0.0,0.0,  0.0,
    0.0,1.0,0.0,  0.0,
    0.0,0.0,1.0,100.0,
    0.0,0.0,0.0,1.0,
    };
double eye[16]=        // 4x4 transform matrix of camera at (0,0,-150)
    {
    1.0,0.0,0.0,   0.0,
    0.0,1.0,0.0,   0.0,
    0.0,0.0,1.0,-150.0,
    0.0,0.0,0.0,1.0,
    };
//---------------------------------------------------------------------------
// this is how to draw it
//---------------------------------------------------------------------------
void obj(double *pnt,int pnts,int *fac,int facs,double *rep,double *ieye)
    {
    // variables for drawing
    int i;
    double p0[3],p1[3],p2[3],p3[3],m[16],d;
    // gfx api variables (change to your stuff) Main is the main form of this application
    TCanvas *scr=Main->bmp->Canvas;
    double xs2=Main->ClientWidth/2,ys2=Main->ClientHeight/2;
    double v=xs2*tan(30.0*deg); // 60 degree viewing angle perspective projection

    matrix_mul(m,ieye,rep);             // cumulate all needed transforms

    for (i=0;i<facs*4;)                 // go through all faces
        {
        // convert all points of face
        matrix_mul_vector(p0,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p1,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p2,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p3,m,&pnt[fac[i]*3]); i++;
        // here goes perspective divide by z coordinate if needed
        d=divide(v,p0[2]); p0[0]*=d; p0[1]*=d;
        d=divide(v,p1[2]); p1[0]*=d; p1[1]*=d;
        d=divide(v,p2[2]); p2[0]*=d; p2[1]*=d;
        d=divide(v,p3[2]); p3[0]*=d; p3[1]*=d;
        // here is viewport transform (just translate (0,0) to middle of screen in this case
        p0[0]+=xs2; p0[1]+=ys2;
        p1[0]+=xs2; p1[1]+=ys2;
        p2[0]+=xs2; p2[1]+=ys2;
        p3[0]+=xs2; p3[1]+=ys2;
        // draw quad
        // I use VCL GDI TCanvas you use what you have ...
        // and wireframe only to keep this simple (no Z buffer,winding culling,...)
        scr->Pen->Color=clAqua;     // perimeter wireframe
        scr->MoveTo(p0[0],p0[1]);
        scr->LineTo(p1[0],p1[1]);
        scr->LineTo(p2[0],p2[1]);
        scr->LineTo(p3[0],p3[1]);
        scr->LineTo(p0[0],p0[1]);
//      scr->Pen->Color=clBlue;     // face cross to visualy check if I correctly generate the fac[]
//      scr->MoveTo(p0[0],p0[1]);
//      scr->LineTo(p2[0],p2[1]);
//      scr->MoveTo(p1[0],p1[1]);
//      scr->LineTo(p3[0],p3[1]);
        }
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TMain::draw()
    {
    if (!_redraw) return;
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    // compute inverse of camera need to compute just once for all objects
    double ieye[16];
    matrix_inv(ieye,eye);
    // draw all objects
    obj(pnt,pnts,fac,facs,rep,ieye);

    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    // window constructor you can ignore this ... (just create a backbuffer bitmap here)
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    // window destructor release memory ... also ignoe this
    if (pyx) delete pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    // on resize event ... just resize/redraw backbuffer also can ignore this
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    // repaint event can ignore
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    // timer event to animate the cube ...
    _redraw=true;

    // rotate the object to see it in motion
    double ang,c,s;

    ang=5.0*deg; c=cos(ang); s=sin(ang);    // rotate baround z by 5 degrees per timer step
    double rz[16]= { c, s, 0, 0,
                    -s, c, 0, 0,
                     0, 0, 1, 0,
                     0, 0, 0, 1 };

    ang=1.0*deg; c=cos(ang); s=sin(ang);    // rotate baround x by 1 degrees per timer step
    double rx[16]= { 1, 0, 0, 0,
                     0, c, s, 0,
                     0,-s, c, 0,
                     0, 0, 0, 1 };
    matrix_mul(rep,rep,rz);
    matrix_mul(rep,rep,rx);

    draw();
    }
//---------------------------------------------------------------------------

here is how it looks like:

[notes]

  • if you have more questions then comment me ...

这篇关于我如何缀以从头人类可读的角度旋转矩阵的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆