如何从头开始编写具有人类可读角度的旋转矩阵? [英] How do I compose a rotation matrix with human readable angles from scratch?

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

问题描述

一贯阻止我进行3D编程的一件事是无法理解数学的工作原理.在使用方法和函数进行编程流程时,我可以很好地进行数学运算,然后对我来说一切都清楚而合乎逻辑,但是以数学符号来说,我只是不能从中受益.

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, a watching videos 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.

此外,仅使用某人的代码不是我的兴趣,我想了解其背后的机制和逻辑.我很乐意使用别人的代码,但我真的很想了解它的工作原理.

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 else's code, but I really want to understand how it works.

问题

您能简单地用没有的数学符号向我解释,仅编程符号/函数/伪代码,如何在所有3个轴上实现矩阵变换吗?

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 axes?

理想地,我想要的是写一个方法/对象的材料/理解,在这里我可以定义与glRotate类似的3个轴的角度来旋转我拥有的四边形/三角形的集合. (我正在尝试对立方体形状的3D旋转进行编程,而无需使用OpenGL函数来为我完成此操作,因为每次显示列表中的任何更改都在一次draw调用中完成.)

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. (I am trying to program a 3D rotation of a cube shapes without having access to OpenGL functions to do it for me because this is done in one draw call every time something changes in the display list.)

我做了什么?

我试图制作一个90度变换函数以掌握数学原理,但是完全无法制作一个理论上应该是最简单的适当矩阵.您可以在 http://jsfiddle.net/bLfg0tj8/5/

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/

Vec3 = function(x,y,z) {
    this.x = x;
    this.y = y;
    this.z = z;
}
Matrix = function Matrix() {
    this.matrixPoints = new Array();    
    this.rotationPoint = new Vec3(0,0,0);
    this.rotationAngle = 90;
}
Matrix.prototype.addVector = function(vector) {
    this.matrixPoints.push(vector);
}
Matrix.prototype.setRotationPoint = function(vector) {
    this.rotationPoint = vector; 
}
Matrix.prototype.setRotationAngle = function(angle) {
    this.rotationAngle = angle;
}
Matrix.prototype.populate = function() {
    translateToOrigin =     [[1,0,0-this.rotationPoint.x],
                                  [0,1,0-this.rotationPoint.y],
                                  [0,0,0-this.rotationPoint.z]];
    rotationMatrix =         [[0,-1,0],
                                  [0,1,0],
                                  [0,0,1]];
    translateEnd =         [[1,0,this.rotationPoint.x],
                                  [0,1,this.rotationPoint.y],
                                  [0,0,this.rotationPoint.z]];
    currentColumn = 0;
    currentRow = 0;
    this.combomatrix = this.mergeMatrices(this.mergeMatrices(translateEnd,rotationMatrix),
                                          translateToOrigin);
}
Matrix.prototype.transform = function() {
    newmatrix = new Array();
    for(c = 0;c<this.matrixPoints.length;c++) {
        newmatrix.push(this.applyToVertex(this.matrixPoints[c]));
    }
    return newmatrix;
}
Matrix.prototype.applyToVertex = function(vertex) {
    ret = new Vec3(vertex.x,vertex.y,vertex.z);
    ret.x = ret.x + this.combomatrix[0][0] * vertex.x +
            this.combomatrix[0][1] * vertex.y +
            this.combomatrix[0][2] * vertex.z;
    
    ret.y = ret.y + this.combomatrix[1][0] * vertex.x +
            this.combomatrix[1][1] * vertex.y +
            this.combomatrix[1][2] * vertex.z;
    
    ret.z = ret.z + this.combomatrix[2][0] * vertex.x +
            this.combomatrix[2][1] * vertex.y +
            this.combomatrix[2][2] * vertex.z;
    return ret;
}
Matrix.prototype.mergeMatrices = function(lastStep, oneInFront) {
    step1 = [[0,0,0],[0,0,0],[0,0,0]];
    step1[0][0] =   lastStep[0][0] * oneInFront[0][0] + 
                    lastStep[0][1] * oneInFront[1][0] + 
                    lastStep[0][2] * oneInFront[2][0];
    
    step1[0][1] =   lastStep[0][0] * oneInFront[0][1] + 
                    lastStep[0][1] * oneInFront[1][1] + 
                    lastStep[0][2] * oneInFront[2][1];
    
    step1[0][2] =   lastStep[0][0] * oneInFront[0][2] + 
                    lastStep[0][1] * oneInFront[1][2] + 
                    lastStep[0][2] * oneInFront[2][2];
    //============================================================
    step1[1][0] =   lastStep[1][0] * oneInFront[0][0] + 
                    lastStep[1][1] * oneInFront[1][0] + 
                    lastStep[1][2] * oneInFront[2][0];
    
    step1[1][1] =   lastStep[1][0] * oneInFront[0][1] + 
                    lastStep[1][1] * oneInFront[1][1] + 
                    lastStep[1][2] * oneInFront[2][1];
    
    step1[1][2] =   lastStep[1][0] * oneInFront[0][2] + 
                    lastStep[1][1] * oneInFront[1][2] + 
                    lastStep[1][2] * oneInFront[2][2];
    //============================================================
    step1[2][0] =   lastStep[2][0] * oneInFront[0][0] + 
                    lastStep[2][1] * oneInFront[1][0] + 
                    lastStep[2][2] * oneInFront[2][0];
    
    step1[2][1] =   lastStep[2][0] * oneInFront[0][1] + 
                    lastStep[2][1] * oneInFront[1][1] + 
                    lastStep[2][2] * oneInFront[2][1];
    
    step1[2][2] =   lastStep[2][0] * oneInFront[0][2] + 
                    lastStep[2][1] * oneInFront[1][2] + 
                    lastStep[2][2] * oneInFront[2][2];
    return step1;
}
Matrix.prototype.getCurrentMatrix = function() {
    return this.matrixPoints;
}
myvectors = [new Vec3(50,50,0), new Vec3(20,80,0), new Vec3(80, 80, 0)];

function drawVectors(vectors,color) {
    for(c=0;c<vectors.length;c++) {
        document.getElementById("whoa").innerHTML += '<div style="color:'+color+';position:absolute;left:'+vectors[c].x+'px; top:'+vectors[c].y+'px;z-index:'+vectors[c].z+';">('+c+').</div>';
    }
}
matrix = new Matrix();
for(c=0;c<myvectors.length;c++) {
    matrix.addVector(myvectors[c]);
}
matrix.setRotationPoint(new Vec3(50,70,0));
matrix.populate();
somematrix = matrix.transform();
drawVectors(matrix.getCurrentMatrix(),"lime"); // draw current matrix that was hand coded
drawVectors([matrix.rotationPoint],'white'); // draw rotation point
drawVectors(somematrix,"red"); // transformed matrix... somehow two points merge

<div id="whoa" style="position:relative;top:50px;left:150px;background-color:green;color:red;width:400px;height:300px;">
    &nbsp;
</div>

绿色文本是原始三角形,白色是中心点,红色是失败的转换(我想是因为它未围绕中心点对齐).在本教程中,我想到了如何将矩阵组合成组合矩阵,但是我想我搞砸了.

The green text is the original 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.

正如我所说,我真的很难理解数学符号和说话.并没有帮助的是,大多数老师都忽略了部分解释.我独自花了2个小时来了解乘法矩阵时的情况,您需要将每一步加在一起而不是继续乘法.是的,解释.

As I said, it's 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 multiplying. Yay for explanations.

与我合作/想要合作的实际示例

例如,我有一个多维数据集,是从位于世界

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

x = 50
y = 100
z = 200

使用四边形和一些uv映射绘制立方体.没问题呈现精美,所有纹理均正确显示.

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

所以这一切都很好.但是,如果我希望此立方体沿x轴旋转90度并围绕z轴旋转45度怎么办?我无法使用glRotate,因为目前将数据传递给tesselator对象时,我无法通过opengl函数对其进行任何矩阵转换,因为它只是在吸收数据,而实际上并不是在渲染它本身.

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.

数据的存储方式如下:

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.

需要答案

  1. 逐步解释如何构建翻译矩阵并尝试向我解释数学.
  2. 说明如何将平移矩阵应用于其保持围绕其位置中心定向的面中的四边形/三角形

  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

x = 50.5 y = 100.5 z = 200.5

一些示例/伪代码以及相应的说明.

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

用于解释的二手编程语言实际上并不重要,只要它在C系列中即可.

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

请尽量远离数学符号/口语.我不知道什么是alpha beta,thetha是什么,我也不知道x轴,y轴和z轴是什么.我确实知道什么角度,但是我不知道数学家为此找到的名字.

Please try to stay away from mathematical notation/speak. I don't 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.

如果您想使用数学名称,请向我解释一下它们在3D世界/代码中的含义以及它们的形成/计算方式.

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);

推荐答案

所以问题实际上是了解4x4均匀变换矩阵

在没有数学知识的情况下,剩下的唯一一件事就是几何表示/含义,这对于人类的抽象/理解要好得多.

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

1.那么4x4矩阵是什么?

它是笛卡尔坐标系的表示,由以下组成:

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

  1. 3基矢量(每个轴一个)红色,绿色,蓝色

  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).

原点灰色

投影和同侧(矩阵未标记的底部)

此部分仅用于一次启用旋转和平移,因此使用的点必须是同质的,这意味着点的形式为(x,y,z,w=1),方向矢量的形式为(x,y,z,w=0).如果只是(x,y,z),则矩阵将为3x3,不足以进行转换.我不会使用不易从几何角度进行解释的任何投影.

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) for points and (x,y,z,w=0) for direction vectors. 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.

此布局来自 OpenGL表示法,那里还有换位表示(矢量是行而不是列)

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;

其中:

  • M是变换矩阵
  • lM局部坐标系点(LCS)
  • g是全局坐标系点(GCS)
  • M is transform matrix
  • l is M local coordinate system point (LCS)
  • g is global coordinate system point (GCS)

对于转置版本( DirectX ),它是:

for the transposed version (DirectX) it is:

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

这是因为转置的正交旋转矩阵也是其自身的逆

  • 有关更多信息,请参见变换矩阵解剖

    2.如何形象化

    是的,您可以绘制矩阵数字,但是乍一看它们没有意义,尤其是在数字变化的情况下,请绘制如上图所示的轴向量.每个轴都是从originorigin + line_size*axis_vector

    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.如何构造

    只需计算轴矢量和原点,然后将它们放在矩阵中即可.为确保利用正交性跨乘积(但请谨慎选择乘积顺序以使用正确的方向)

    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.效果

    • 旋转是通过旋转轴来完成的,因此您可以通过参数圆方程来计算每个轴...
    • 缩放是通过将轴乘以比例因子来完成的
    • 倾斜只是使用非垂直轴

    5.旋转

    在大多数情况下,使用增量旋转.有两种类型

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

    • 局部旋转 M'=M*rotation_matrix它围绕局部坐标轴旋转,就像您将控制飞机,汽车或玩家一样……大多数引擎/游戏不使用它们并以进行伪造> euler angles 而不是一个便宜的解决方案(有很多古怪和问题),因为大多数使用OpenGL的人甚至都不知道这是可能的,而是glRotate/glTranslate调用的堆栈列表...

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

    全局旋转 M'=Inverse(Inverse(M)*rotation_matrix)围绕全局坐标系轴旋转.

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

    其中rotation_matrix是任何标准旋转变换矩阵.

    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 ...

    您还可以从3角度计算您的rotation_matrix,例如:

    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);
    

    请参见 Wiki旋转矩阵来自Basic rotations的3D Rx,Ry,Rz.如您所见,它们实际上只是单位圆参数方程.乘法顺序更改角度如何收敛到目标位置.这称为欧拉角(Euler angles),我不使用它(我集成了阶跃变化,如果做得好,它没有任何限制,更不用说它更简单了.)

    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).

    无论如何,如果需要,您可以相对容易地将变换矩阵转换为欧拉角:

    Anyway if you need you can convert transform matrix into euler angles relatively easily see:

    6. glRotate

    如果要使用glRotate,则应改用四元数,因为这是围绕轴旋转而不是3个角度!有解决方法:

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

    1. 为该轴创建变换矩阵N
    2. 然后将矩阵M转换为它
    3. 旋转N角度
    4. 然后将MN转换回全局坐标
    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

    或者您可以改用 Rodrigues_rotation_formula

    在这种情况下,要将Matrix与Matrix进行相互转换,只需将轴转换为点并将原点保留不变,但N 的原点必须为(0,0,0)!!! 或转换的向量必须改为w=0.

    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)!!! or the vectors transformed must have w=0 instead.

    7.用法

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

    Transformations are cumulative that means:

    • p'=M1*M2*M3*M4*p;M=M1*M2*M3*M4; p'=M*p
    • 相同
    • 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:

    您应该具有3坐标系:

    • 相机C
    • 世界(通常是单位矩阵)
    • 对象O(每个对象都有自己的矩阵)
    • camera C
    • world (usually unit matrix)
    • object O (each object have its own matrix)

    因此,如果您的立方体具有8顶点p0,...,p7,则必须在每个点上执行从对象局部坐标到相机局部坐标的转换.一些gfx api会执行其中的一些操作,因此您只需要应用所需的内容,因此您确实需要:

    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);

    因此,在为绘制的对象绘制计算Q之前,然后获取对象的每个点p(i)并计算已转换的p(i)'并绘制/使用已转换的对象... p(i)'在本地摄像机坐标系中(屏幕的x,y),但那里没有透视图,因此在绘制之前,您还可以添加任何投影矩阵,并用z坐标除以最后的坐标...投影也是累积的,因此它也可以位于内部Q

    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 ++示例

    //$$---- 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();
        }
    //---------------------------------------------------------------------------
    

    这是它的样子:

    以及背面剔除的GIF动画:

    And GIF animation with back face culling:

    [notes]

    如果您还有其他问题,请评论我...

    If you have more questions then comment me ...

    [Edit2]经常需要的基本3D矢量操作

    如果您不知道如何计算矢量运算,例如叉/点积或绝对值,请参阅:

    If you do not know how to compute vector operations like cross/dot products or absolute value see:

    // cross product: W = U x V
    W.x=(U.y*V.z)-(U.z*V.y)
    W.y=(U.z*V.x)-(U.x*V.z)
    W.z=(U.x*V.y)-(U.y*V.x)
    // dot product: a = (U.V)
    a=U.x*V.x+U.y*V.y+U.z*V.z
    // abs of vector a = |U|
    a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))
    

    这里是我的C ++向量数学:

    here my C++ vector math:

    static double vector_tmp[3];
    double divide(double x,double y) { if ((y>=-1e-30)&&(y<=+1e-30)) return 0.0; return x/y; }
    double* vector_ld(double x,double y,double z)          { double *p=vector_tmp; p[0]=x; p[1]=y; p[2]=z; return p;}
    double* vector_ld(double *p,double x,double y,double z) {                      p[0]=x; p[1]=y; p[2]=z; return p;}
    void  vector_copy(double *c,double *a)         { for(int i=0;i<3;i++) c[i]=a[i];       }
    void  vector_abs(double *c,double *a)          { for(int i=0;i<3;i++) c[i]=fabs(a[i]); }
    void  vector_one(double *c,double *a)
            {
            double l=divide(1.0,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
            c[0]=a[0]*l;
            c[1]=a[1]*l;
            c[2]=a[2]*l;
            }
    void  vector_len(double *c,double *a,double l)
            {
            l=divide(l,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
            c[0]=a[0]*l;
            c[1]=a[1]*l;
            c[2]=a[2]*l;
            }
    void  vector_neg(double *c,double *a)          { for(int i=0;i<3;i++) c[i]=-a[i];      }
    void  vector_add(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]+b[i]; }
    void  vector_sub(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]-b[i]; }
    void  vector_mul(double *c,double *a,double *b) // cross
            {
            double   q[3];
            q[0]=(a[1]*b[2])-(a[2]*b[1]);
            q[1]=(a[2]*b[0])-(a[0]*b[2]);
            q[2]=(a[0]*b[1])-(a[1]*b[0]);
            for(int i=0;i<3;i++) c[i]=q[i];
            }
    void  vector_mul(double *c,double *a,double  b) { for(int i=0;i<3;i++) c[i]=a[i]*b; }
    void  vector_mul(double *c,double  a,double *b) { for(int i=0;i<3;i++) c[i]=a*b[i]; }
    double vector_mul(         double *a,double *b) { double c=0; for(int i=0;i<3;i++) c+=a[i]*b[i]; return c; } // dot
    double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); }
    double vector_len2(double *a) { return (a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]); }
    

    [Edit3]通过键盘进行摄像机和对象控制的本地旋转

    这是最近被问到的很多示例的示例答案:

    As this has been asked a lot lately here some example answers of mine with demos:

    • stationary camera view control (partial pseudo inverse matrix)
    • camera and player control (inverse matrix)
    • How to preserve accuracy with cumulative transforms over time (full pseudo inverse matrix)
    • rotundus style simple OpenGL/C++/VCL player control example

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

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