如何组成一个矩阵来执行世界坐标的等距(dimetric)投影? [英] How to compose a matrix to perform isometric (dimetric) projection of a world coordinate?

查看:66
本文介绍了如何组成一个矩阵来执行世界坐标的等距(dimetric)投影?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个2D单位矢量,其中包含一个世界坐标(某个方向),我想将其转换为屏幕坐标(经典等距图块).

I have a 2D unit vector containing a world coordinate (some direction), and I want to convert that to screen coordinates (classic isometric tiles).

我知道我可以通过绕相关轴旋转来实现此目的,但是我想了解并理解如何使用纯矩阵方法来做到这一点?一方面是因为我正在学习现代OpenGL"(v2 +),另一方面是因为我想在其他事情上使用相同的技术,因此需要扎实的理解,我的数学能力有些不足.

I'm aware I can achieve this by rotating around the relevant axis but I want to see and understand how to do this using a purely matrix approach? Partly because I'm learning 'modern OpenGL' (v2+) and partly because I will want to use this same technique for other things so need a solid understanding and my math ability is a little lacking.

如果需要,我的屏幕坐标系的原点位于左上角,带有+ x& + y分别指向右和向下.另外,如果相关的话,我的顶点位置会转换为我的顶点着色器中的NDC范围.

If needed my screen's coordinate system has it's origin at top left with +x & +y pointing right and down respectively. Also, my vertex positions are converted to the NDC range in my vertex shader if that's relevant.

语言是C ++,没有支持的库.

Language is C++ with no supporting libraries.

推荐答案

我从回顾等距投影<开始/a>在Wikipedia中.而是提供了完整的烹饪指南(在数学部分中).

所以,我有点困惑仍然可能缺少的东西.然后,我想起我实际上不是在大学的数学课程(应该在那儿学习)中学习矩阵知识"的,而是后来从一个耐心的学院(获得数学文凭)那里学到的.

So, I'm a little bit puzzled what still could be missing. Then, I remembered that I learnt the "matrix stuff" actually not in the math course at university (where I should have) but much later from a patient colleage (with Math diploma).

因此,我将尝试在C ++中提供最少的演示:

Thus, I will try to provide a most minimal demonstration in C++:

首先,其中包括:

#include <iostream>
#include <iomanip>
#include <cmath>

using namespace std;

...便捷功能:

inline float degToRad(float angle) { return 3.141593f * angle / 180.f; }

... 2d向量类":

... a 2d vector "class":

struct Vec2 {
  float x, y;
  Vec2(float x, float y): x(x), y(y) { }
};

ostream& operator<<(ostream &out, const Vec2 &v)
{
  return out << "( " << v.x << ", " << v.y << " )";
}

... 3d向量类":

... a 3d vector "class":

struct Vec3 {
  float x, y, z;
  Vec3(float x, float y, float z): x(x), y(y), z(z) { }
  Vec3(const Vec2 &xy, float z): x(xy.x), y(xy.y), z(z) { }
};

ostream& operator<<(ostream &out, const Vec3 &v)
{
  return out << "( " << v.x << ", " << v.y << ", " << v.z << " )";
}

... 3×3矩阵类:

... a 3×3 matrix class:

enum ArgInitRotX { InitRotX };
enum ArgInitRotY { InitRotY };

struct Mat3x3 {
  float _00, _01, _02, _10, _11, _12, _20, _21, _22;

  // constructor to build a matrix by elements
  Mat3x3(
    float _00, float _01, float _02,
    float _10, float _11, float _12,
    float _20, float _21, float _22)
  {
    this->_00 = _00; this->_01 = _01; this->_02 = _02;
    this->_10 = _10; this->_11 = _11; this->_12 = _12;
    this->_20 = _20; this->_21 = _21; this->_22 = _22;
  }
  // constructor to build a matrix for rotation about x axis
  Mat3x3(ArgInitRotX, float angle)
  {
    this->_00 = 1.0f; this->_01 = 0.0f;        this->_02 = 0.0f;
    this->_10 = 0.0f; this->_11 = cos(angle);  this->_12 = sin(angle);
    this->_20 = 0.0f; this->_21 = -sin(angle); this->_22 = cos(angle);
  }
  // constructor to build a matrix for rotation about y axis
  Mat3x3(ArgInitRotY, float angle)
  {
    this->_00 = cos(angle); this->_01 = 0.0f; this->_02 = -sin(angle);
    this->_10 = 0.0f;       this->_11 = 1.0f; this->_12 = 0.0f;
    this->_20 = sin(angle); this->_21 = 0.0f; this->_22 = cos(angle);
  }
  // multiply matrix with matrix -> matrix
  Mat3x3 operator * (const Mat3x3 &mat) const
  {
    return Mat3x3(
      _00 * mat._00 + _01 * mat._10 + _02 * mat._20,
      _00 * mat._01 + _01 * mat._11 + _02 * mat._21,
      _00 * mat._02 + _01 * mat._12 + _02 * mat._22,
      _10 * mat._00 + _11 * mat._10 + _12 * mat._20,
      _10 * mat._01 + _11 * mat._11 + _12 * mat._21,
      _10 * mat._02 + _11 * mat._12 + _12 * mat._22,
      _20 * mat._00 + _21 * mat._10 + _22 * mat._20,
      _20 * mat._01 + _21 * mat._11 + _22 * mat._21,
      _20 * mat._02 + _21 * mat._12 + _22 * mat._22);
  }
  // multiply matrix with vector -> vector
  Vec3 operator * (const Vec3 &vec) const
  {
    return Vec3(
      _00 * vec.x + _01 * vec.y + _02 * vec.z,
      _10 * vec.x + _11 * vec.y + _12 * vec.z,
      _20 * vec.x + _21 * vec.y + _22 * vec.z);
  }
};

ostream& operator<<(ostream &out, const Mat3x3 &mat)
{
  return out
    << mat._20 << ", " << mat._21 << ", " << mat._22 << endl
    << mat._10 << ", " << mat._11 << ", " << mat._12 << endl
    << mat._20 << ", " << mat._21 << ", " << mat._22;
}

...和main()函数将所有内容放在一起进行实际演示:

... and the main() function to put all together to the actual demonstration:

int main()
{
  // some 2D vector samples (for a quad)
  Vec2 quad[] = {
    { 0.0f, 0.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }
  };
  /* Something like this:
   * ^ y
   * |
   * v[3] ---- v[2]
   * |         |
   * |         |
   * |         |
   * v[0] ---- v[1] --> x
   */
  // the rotation matrix for isometric view build by multiplying the rotations
  Mat3x3 matIso = Mat3x3(InitRotX, degToRad(30.0)) * Mat3x3(InitRotY, degToRad(45.0));
  // prepare output formatting
  cout << fixed << setprecision(5);
  // the rotation matrix for isometric view:
  cout << "The matrix for isometric projection:" << endl
    << matIso << endl;
  // prepare output formatting
  cout << fixed << setprecision(3);
  // do it for all sample 2D vectors:
  cout << "Isometric projection of the 2d quad:" << endl;
  for (const Vec2 &v : quad) {
    // 2D vector -> 3D vector
    Vec3 v_(v, 0.0f);
    // project v_ to iso view
    v_ = matIso * v_;
    // print the result:
    cout << v << " -> " << v_ << endl;
  }
  // doing it again with a 3d cube (centered)
  Vec3 cube[] = {
    { -0.5f, -0.5f, -0.5f }, { +0.5f, -0.5f, -0.5f }, { +0.5f, +0.5f, -0.5f }, { -0.5f, +0.5f, -0.5f },
    { -0.5f, -0.5f, +0.5f }, { +0.5f, -0.5f, +0.5f }, { +0.5f, +0.5f, +0.5f }, { -0.5f, +0.5f, +0.5f }
  };
  cout << "Isometric projection of the centered 3d cube:" << endl;
  for (const Vec3 &v : cube) {
    // project v to iso view
    Vec3 v_ = matIso * v;
    // print the result:
    cout << v << " -> " << v_ << endl;
  }
  // done
  return 0;
}

这是我在测试中得到的:

This is what I got in my test:

The matrix for isometric projection:
0.61237, -0.50000, 0.61237
0.35355, 0.86603, 0.35355
0.61237, -0.50000, 0.61237
Isometric projection of the 2d quad:
( 0.000, 0.000 ) -> ( 0.000, 0.000, 0.000 )
( 0.000, 1.000 ) -> ( 0.000, 0.866, -0.500 )
( 1.000, 1.000 ) -> ( 0.707, 1.220, 0.112 )
( 1.000, 0.000 ) -> ( 0.707, 0.354, 0.612 )
Isometric projection of the centered 3d cube:
( -0.500, -0.500, -0.500 ) -> ( -0.707, -0.787, -0.362 )
( 0.500, -0.500, -0.500 ) -> ( 0.000, -0.433, 0.250 )
( 0.500, 0.500, -0.500 ) -> ( 0.000, 0.433, -0.250 )
( -0.500, 0.500, -0.500 ) -> ( -0.707, 0.079, -0.862 )
( -0.500, -0.500, 0.500 ) -> ( -0.000, -0.433, 0.250 )
( 0.500, -0.500, 0.500 ) -> ( 0.707, -0.079, 0.862 )
( 0.500, 0.500, 0.500 ) -> ( 0.707, 0.787, 0.362 )
( -0.500, 0.500, 0.500 ) -> ( -0.000, 0.433, -0.250 )

我将整个样本上传到 ideone .

I uploaded the whole sample on ideone.

以上Wikipedia链接的数学"部分还提到了到xy平面的投影.恕我直言,忽略结果向量的z坐标甚至更简单.但是,正如问题中提到的OpenGL一样,值得保留z坐标(例如,用于深度缓冲).

The Mathematics section of the above Wikipedia link mentions also a projection to xy plane. IMHO, it is even simpler to just ignore the z coordinates of the result vectors. However, as OpenGL was mentioned in the question, it can be worth to keep the z-coordinates (e.g. for depth buffering).

在OpenGL中,使用4×4矩阵.引入这些是为了支持同质坐标.简化:均质坐标用于将点和方向强制进入同一空间"或涉及将3d转换为3d变换(这对于3×3矩阵是不可能的).同类坐标要复杂一些(因此值得提出另一个问题).

In OpenGL, 4×4 matrices are used. These, are introduced for the support of Homogeneous Coordinates. Simplified: Homogeneous coordinates are used to "force points and directions into the same space" or to involve 3d translation into 3d transformations (which is not possible with 3×3 matrices). Homogeneous coordinates are a little bit more complicated (and hence worth another question).

幸运的是,等轴测投影仅由旋转构建(并且可能是xy平面上的投影,为保持深度缓冲值,我省略了此投影).因此,3×3的矩阵就足够了.

For my luck, the isometric projection is built from rotations only (and, may be, the projection to xy plane which I left out to keep the depth buffer values). Thus, 3×3 matrices are sufficient.

但是,我想至少提及一下矩阵在OpenGL中的外观(如4×4矩阵):

However, I want to mention at least how the matrix would look in OpenGL (as 4×4 matrix):

float matIso[] = {
  0.61237f, -0.50000f, 0.61237f, 0.0f,
  0.35355f,  0.86603f, 0.35355f, 0.0f,
  0.61237f, -0.50000f, 0.61237f, 0.0f,
  0.0f,      0.0f,     0.0f,     1.0f
};

最后一列表示翻译,在这种情况下为(0,0,0).

The last column denotes the translation which is (0, 0, 0) in this case.

有一些开源库可用于在CPU端进行数学运算.除了其他我想提及的内容:

There are some open source libraries available to do the math stuff on CPU side. Among others I want to mention:

  • Eigen library
  • OpenGL Mathematics

在GPU方面(至少对于GLSL我知道),它已经内置.

On GPU side (I know, at least, for GLSL), it's already built in.

更新

交谈后,我恢复了图形可视化的计划.我的目的是简短而简单,而又不将数学细节隐藏在像OpenGL这样的API中.因此,我决定将其作为仅Qt的样本进行处理.这就是它的外观:

After having some conversation, I revived my plan for a graphical visualization. My intention was to keep it short and simple without hiding the mathematical details in an API like OpenGL. Thus, I decided to do it as Qt only sample. This is, how it looks:

Qt应用程序test-QIsoView.cc的源代码:

The source code for the Qt application test-QIsoView.cc:

#include <QtWidgets>
#include "linmath.h"

typedef unsigned int uint; // for the convenience

struct Wireframe {
  Vec3f *points; // coordinates
  uint nPoints; // number of points (i.e. values in indices)
  uint *indices;
  Vec3f color;
};

class WireframeView: public QWidget {
  public:
    const size_t nWireframes;
    const Wireframe *wireframes;
    const Mat4x4f matProj;
  private:
    Mat4x4f _matView;
  public:
    WireframeView(
      size_t nWireframes = 0, const Wireframe *wireframes = nullptr,
      const Mat4x4f &matProj = Mat4x4f(InitIdent),
      QWidget *pQParent = nullptr):
      QWidget(pQParent),
      nWireframes(nWireframes), wireframes(wireframes),
      matProj(matProj), _matView(InitIdent)
    { }
  protected:
    virtual void resizeEvent(QResizeEvent *pQEvent) override;
    virtual void paintEvent(QPaintEvent *pQEvent) override;
};

void WireframeView::resizeEvent(QResizeEvent*)
{
  float w_2 = 0.5f * width(), h_2 = 0.5f * height();
  float s = w_2 < h_2 ? w_2 : h_2;
  _matView
    = Mat4x4f(InitTrans, Vec3f(w_2, h_2, 0.0f))
    * Mat4x4f(InitScale, s, -s, 1.0f);
}

void WireframeView::paintEvent(QPaintEvent *pQEvent)
{
  const int w = width(), w_2 = w / 2, h = height(), h_2 = h / 2;
  int m = w_2 < h_2 ? w_2 : h_2;
  QPainter qPainter(this);
  // clear background
  QPalette::ColorGroup colGrp = isEnabled()
    ? QPalette::Active : QPalette::Disabled;
  qPainter.setBrush(QApplication::palette().brush(colGrp, QPalette::Base));
  qPainter.drawRect(0, 0, width(), height());
  // draw grid
  const QBrush &mid = QApplication::palette().brush(colGrp, QPalette::Mid);
  qPainter.setPen(QPen(mid.color(), 1));
  qPainter.drawRect(w_2 - m, h_2 - m, 2 * m, 2 * m);
  qPainter.drawLine(0, h_2, w, h_2);
  qPainter.drawLine(w_2, 0, w_2, h);
  // draw wireframes
  Mat4x4f matView = _matView * matProj;
  for (size_t i = 0; i < nWireframes; ++i) {
    const Wireframe &wireframe = wireframes[i];
    QColor qColor(
      wireframe.color.x * 255, wireframe.color.y * 255,
      wireframe.color.z * 255);
    qPainter.setPen(QPen(qColor, 2));
    for (uint i = 1; i < wireframe.nPoints; i += 2) {
      Vec4f p0(wireframe.points[wireframe.indices[i - 1]], 1.0f);
      Vec4f p1(wireframe.points[wireframe.indices[i]], 1.0f);
      Vec2f p0V = Vec2f(matView * p0);
      Vec2f p1V = Vec2f(matView * p1);
      qPainter.drawLine((int)p0V.x, (int)p0V.y, (int)p1V.x, (int)p1V.y);
    }
  }
}

int main(int argc, char **argv)
{
  QApplication app(argc, argv);
  // build models
  Vec3f pointsPyramid[] = {
    Vec3f(0.0f, 0.0f, 0.0f),
    Vec3f(1.0f, 0.0f, 0.0f),
    Vec3f(0.0f, 1.0f, 0.0f),
    Vec3f(0.0f, 0.0f, 1.0f)
  };
  uint indicesPyramid[] = {
    0, 1, 0, 2, 0, 3, 1, 2, 2, 3, 3, 1
  };
  enum {
    nPointsPyramid = sizeof indicesPyramid / sizeof *indicesPyramid
  };
  Vec3f pointsCube[] = {
    Vec3f(-0.5f, -0.5f, -0.5f), Vec3f(+0.5f, -0.5f, -0.5f),
    Vec3f(-0.5f, +0.5f, -0.5f), Vec3f(+0.5f, +0.5f, -0.5f),
    Vec3f(-0.5f, -0.5f, +0.5f), Vec3f(+0.5f, -0.5f, +0.5f),
    Vec3f(-0.5f, +0.5f, +0.5f), Vec3f(+0.5f, +0.5f, +0.5f)
  };
  uint indicesCube[] = {
    0, 1, 1, 3, 3, 2, 2, 0, // front
    4, 5, 5, 7, 7, 6, 6, 4, // back
    0, 4, 1, 5, 3, 7, 2, 6 // sides
  };
  enum {
    nPointsCube = sizeof indicesCube / sizeof *indicesCube
  };
  Wireframe wireframes[] = {
    { pointsPyramid, nPointsPyramid, indicesPyramid,
      Vec3f(0.8f, 0.0f, 0.0f)
    },
    { pointsCube, nPointsCube, indicesCube,
      Vec3f(0.0f, 0.8f, 0.0f)
    }
  };
  enum { nWireframes = sizeof wireframes / sizeof *wireframes };
  // the view projection matrices
  Mat4x4f matViewFront(InitIdent);
  Mat4x4f matViewTop(InitRotX, degToRad(90.0f));
  Mat4x4f matViewLeft(InitRotY, degToRad(-90.0f));
  Mat4x4f matViewIso
    = Mat4x4f(InitRotX, degToRad(30.0f))
    * Mat4x4f(InitRotY, degToRad(45.0));
  // build GUI
  QWidget win;
  QGridLayout qGrid;
  QLabel qLblTop(QString::fromUtf8("<b>Top View</b>"));
  qLblTop.setTextFormat(Qt::RichText);
  qLblTop.setAlignment(Qt::AlignCenter);
  qGrid.addWidget(&qLblTop, 0, 0);
  WireframeView viewTop(nWireframes, wireframes, matViewTop);
  qGrid.addWidget(&viewTop, 1, 0);
  QLabel qLblFront(QString::fromUtf8("<b>Front View</b>"));
  qLblFront.setTextFormat(Qt::RichText);
  qLblFront.setAlignment(Qt::AlignCenter);
  qGrid.addWidget(&qLblFront, 2, 0);
  WireframeView viewFront(nWireframes, wireframes, matViewFront);
  qGrid.addWidget(&viewFront, 3, 0);
  QLabel qLblIso(QString::fromUtf8("<b>Isometric View</b>"));
  qLblIso.setTextFormat(Qt::RichText);
  qLblIso.setAlignment(Qt::AlignCenter);
  qGrid.addWidget(&qLblIso, 0, 1);
  WireframeView viewIso(nWireframes, wireframes, matViewIso);
  qGrid.addWidget(&viewIso, 1, 1);
  QLabel qLblLeft(QString::fromUtf8("<b>Left View</b>"));
  qLblLeft.setTextFormat(Qt::RichText);
  qLblLeft.setAlignment(Qt::AlignCenter);
  qGrid.addWidget(&qLblLeft, 2, 1);
  WireframeView viewLeft(nWireframes, wireframes, matViewLeft);
  qGrid.addWidget(&viewLeft, 3, 1);
  qGrid.setRowStretch(1, 1); qGrid.setRowStretch(3, 1);
  win.setLayout(&qGrid);
  win.show();
  // exec. application
  return app.exec();
}

对于每个视图,投影都分为两个矩阵.

For each view, the projection is separated into two matrices.

  1. 实际投影提供给WireframeView的构造函数.

WireframeView在内部管理从NDC(归一化设备坐标)到屏幕空间的第二次转换.这包括缩放(在考虑当前的宽高比的情况下),y坐标的镜像以及原点(0,0,0)到视图中心的平移.

The class WireframeView manages internally a second transformation from NDC (Normalized Device Coordinates) to screen space. This includes the scaling (under consideration of the current aspect ratio), the mirroring of y coordinates, and the translation of origin (0, 0, 0) to center of view.

在实际渲染开始之前将这两个矩阵相乘.在渲染循环中,每个点都与组合的视图矩阵相乘,以将其从(模型)世界坐标转换为屏幕坐标.

These two matrices are multiplied before the actual rendering starts. In the rendering loop, each point is multiplied with the combined view matrix to transform it from (model) world coordinates to screen coordinates.

我将数学资料移到了单独的标题linmath.h:

I moved the mathematical stuff to a separate header linmath.h:

#ifndef LIN_MATH_H
#define LIN_MATH_H

#include <iostream>
#include <cmath>

template <typename VALUE>
inline VALUE degToRad(VALUE angle)
{
  return (VALUE)3.1415926535897932384626433832795 * angle / (VALUE)180;
}

template <typename VALUE>
struct Vec2T {
  VALUE x, y;
  Vec2T(VALUE x, VALUE y): x(x), y(y) { }
};

template <typename VALUE>
std::ostream& operator<<(std::ostream &out, const Vec2T<VALUE> &v)
{
  return out << "( " << v.x << ", " << v.y << " )";
}

typedef Vec2T<float> Vec2f;
typedef Vec2T<double> Vec2;

template <typename VALUE>
struct Vec3T {
  VALUE x, y, z;
  Vec3T(VALUE x, VALUE y, VALUE z): x(x), y(y), z(z) { }
  Vec3T(const Vec2T<VALUE> &xy, VALUE z): x(xy.x), y(xy.y), z(z) { }
  explicit operator Vec2T<VALUE>() const { return Vec2T<VALUE>(x, y); }
};

typedef Vec3T<float> Vec3f;
typedef Vec3T<double> Vec3;

template <typename VALUE>
struct Vec4T {
  VALUE x, y, z, w;
  Vec4T(VALUE x, VALUE y, VALUE z, VALUE w): x(x), y(y), z(z), w(w) { }
  Vec4T(const Vec2T<VALUE> &xy, VALUE z, VALUE w):
    x(xy.x), y(xy.y), z(z), w(w)
  { }
  Vec4T(const Vec3T<VALUE> &xyz, VALUE w):
    x(xyz.x), y(xyz.y), z(xyz.z), w(w)
  { }
  explicit operator Vec2T<VALUE>() const { return Vec2T<VALUE>(x, y); }
  explicit operator Vec3T<VALUE>() const { return Vec3T<VALUE>(x, y, z); }
};

typedef Vec4T<float> Vec4f;
typedef Vec4T<double> Vec4;

enum ArgInitIdent { InitIdent };
enum ArgInitTrans { InitTrans };
enum ArgInitRotX { InitRotX };
enum ArgInitRotY { InitRotY };
enum ArgInitRotZ { InitRotZ };
enum ArgInitScale { InitScale };

template <typename VALUE>
struct Mat4x4T {
  union {
    VALUE comp[4 * 4];
    struct {
      VALUE _00, _01, _02, _03;
      VALUE _10, _11, _12, _13;
      VALUE _20, _21, _22, _23;
      VALUE _30, _31, _32, _33;
    };
  };

  // constructor to build a matrix by elements
  Mat4x4T(
    VALUE _00, VALUE _01, VALUE _02, VALUE _03,
    VALUE _10, VALUE _11, VALUE _12, VALUE _13,
    VALUE _20, VALUE _21, VALUE _22, VALUE _23,
    VALUE _30, VALUE _31, VALUE _32, VALUE _33)
  {
    this->_00 = _00; this->_01 = _01; this->_02 = _02; this->_03 = _03;
    this->_10 = _10; this->_11 = _11; this->_12 = _12; this->_13 = _13;
    this->_20 = _20; this->_21 = _21; this->_22 = _22; this->_23 = _23;
    this->_30 = _30; this->_31 = _31; this->_32 = _32; this->_33 = _33;
  }
  // constructor to build an identity matrix
  Mat4x4T(ArgInitIdent)
  {
    _00 = (VALUE)1; _01 = (VALUE)0; _02 = (VALUE)0; _03 = (VALUE)0;
    _10 = (VALUE)0; _11 = (VALUE)1; _12 = (VALUE)0; _13 = (VALUE)0;
    _20 = (VALUE)0; _21 = (VALUE)0; _22 = (VALUE)1; _23 = (VALUE)0;
    _30 = (VALUE)0; _31 = (VALUE)0; _32 = (VALUE)0; _33 = (VALUE)1;
  }
  // constructor to build a matrix for translation
  Mat4x4T(ArgInitTrans, const Vec3T<VALUE> &t)
  {
    _00 = (VALUE)1; _01 = (VALUE)0; _02 = (VALUE)0; _03 = (VALUE)t.x;
    _10 = (VALUE)0; _11 = (VALUE)1; _12 = (VALUE)0; _13 = (VALUE)t.y;
    _20 = (VALUE)0; _21 = (VALUE)0; _22 = (VALUE)1; _23 = (VALUE)t.z;
    _30 = (VALUE)0; _31 = (VALUE)0; _32 = (VALUE)0; _33 = (VALUE)1;
  }
  // constructor to build a matrix for rotation about x axis
  Mat4x4T(ArgInitRotX, VALUE angle)
  {
    _00 = (VALUE)1; _01 = (VALUE)0;    _02 = (VALUE)0;   _03 = (VALUE)0;
    _10 = (VALUE)0; _11 = cos(angle);  _12 = sin(angle); _13 = (VALUE)0;
    _20 = (VALUE)0; _21 = -sin(angle); _22 = cos(angle); _23 = (VALUE)0;
    _30 = (VALUE)0; _31 = (VALUE)0;    _32 = (VALUE)0;   _33 = (VALUE)1;
  }
  // constructor to build a matrix for rotation about y axis
  Mat4x4T(ArgInitRotY, VALUE angle)
  {
    _00 = cos(angle); _01 = (VALUE)0; _02 = -sin(angle); _03 = (VALUE)0;
    _10 = (VALUE)0;   _11 = (VALUE)1; _12 = (VALUE)0;    _13 = (VALUE)0;
    _20 = sin(angle); _21 = (VALUE)0; _22 = cos(angle);  _23 = (VALUE)0;
    _30 = (VALUE)0; _31 = (VALUE)0;    _32 = (VALUE)0;   _33 = (VALUE)1;
  }
  // constructor to build a matrix for rotation about z axis
  Mat4x4T(ArgInitRotZ, VALUE angle)
  {
    _00 = cos(angle);  _01 = sin(angle); _02 = (VALUE)0; _03 = (VALUE)0;
    _10 = -sin(angle); _11 = cos(angle); _12 = (VALUE)0; _13 = (VALUE)0;
    _20 = (VALUE)0;    _21 = (VALUE)0;   _22 = (VALUE)1; _23 = (VALUE)0;
    _30 = (VALUE)0;    _31 = (VALUE)0;   _32 = (VALUE)0; _33 = (VALUE)1;
  }
  // constructor to build a matrix for scaling
  Mat4x4T(ArgInitScale, VALUE sx, VALUE sy, VALUE sz)
  {
    _00 = (VALUE)sx; _01 = (VALUE)0;  _02 = (VALUE)0;  _03 = (VALUE)0;
    _10 = (VALUE)0;  _11 = (VALUE)sy; _12 = (VALUE)0;  _13 = (VALUE)0;
    _20 = (VALUE)0;  _21 = (VALUE)0;  _22 = (VALUE)sz; _23 = (VALUE)0;
    _30 = (VALUE)0;  _31 = (VALUE)0;  _32 = (VALUE)0;  _33 = (VALUE)1;
  }
  // multiply matrix with matrix -> matrix
  Mat4x4T operator * (const Mat4x4T &mat) const
  {
    return Mat4x4T(
      _00 * mat._00 + _01 * mat._10 + _02 * mat._20 + _03 * mat._30,
      _00 * mat._01 + _01 * mat._11 + _02 * mat._21 + _03 * mat._31,
      _00 * mat._02 + _01 * mat._12 + _02 * mat._22 + _03 * mat._32,
      _00 * mat._03 + _01 * mat._13 + _02 * mat._23 + _03 * mat._33,
      _10 * mat._00 + _11 * mat._10 + _12 * mat._20 + _13 * mat._30,
      _10 * mat._01 + _11 * mat._11 + _12 * mat._21 + _13 * mat._31,
      _10 * mat._02 + _11 * mat._12 + _12 * mat._22 + _13 * mat._32,
      _10 * mat._03 + _11 * mat._13 + _12 * mat._23 + _13 * mat._33,
      _20 * mat._00 + _21 * mat._10 + _22 * mat._20 + _23 * mat._30,
      _20 * mat._01 + _21 * mat._11 + _22 * mat._21 + _23 * mat._31,
      _20 * mat._02 + _21 * mat._12 + _22 * mat._22 + _23 * mat._32,
      _20 * mat._03 + _21 * mat._13 + _22 * mat._23 + _23 * mat._33,
      _30 * mat._00 + _31 * mat._10 + _32 * mat._20 + _33 * mat._30,
      _30 * mat._01 + _31 * mat._11 + _32 * mat._21 + _33 * mat._31,
      _30 * mat._02 + _31 * mat._12 + _32 * mat._22 + _33 * mat._32,
      _30 * mat._03 + _31 * mat._13 + _32 * mat._23 + _33 * mat._33);
  }
  // multiply matrix with vector -> vector
  Vec4T<VALUE> operator * (const Vec4T<VALUE> &vec) const
  {
    return Vec4T<VALUE>(
      _00 * vec.x + _01 * vec.y + _02 * vec.z + _03 * vec.w,
      _10 * vec.x + _11 * vec.y + _12 * vec.z + _13 * vec.w,
      _20 * vec.x + _21 * vec.y + _22 * vec.z + _23 * vec.w,
      _30 * vec.x + _31 * vec.y + _32 * vec.z + _33 * vec.w);
  }
};

typedef Mat4x4T<float> Mat4x4f;
typedef Mat4x4T<double> Mat4x4;

#endif // LIN_MATH_H

最重要的更改(与原始版本相比):

The most significant changes (compared to the original version):

  • 从3×3矩阵升级到4×4矩阵. (还需要涉及3D翻译)

  • upgrade from 3×3 matrix to 4×4 matrix. (needed to involve 3D translations also)

模板而不是函数和类(可用于floatdouble).

templates instead of functions and classes (can be used for float and double).

这篇关于如何组成一个矩阵来执行世界坐标的等距(dimetric)投影?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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