- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个包含世界坐标(某个方向)的 2D 单位 vector ,我想将其转换为屏幕坐标(经典等距图 block )。
我知道我可以通过围绕相关轴旋转来实现这一点,但我想了解并了解如何使用纯矩阵方法来实现这一点?部分原因是我正在学习“现代 OpenGL”(v2+),部分原因是我想将同样的技术用于其他事情,因此需要扎实的理解,而且我的数学能力有点欠缺。
如果需要,我的屏幕坐标系的原点位于左上角,+x 和 +y 分别指向右侧和下方。此外,如果相关,我的顶点位置将转换为我的顶点着色器中的 NDC 范围。
语言是 C++,没有支持库。
最佳答案
我开始记忆 Isometric Projection在维基百科中。它提供了完整的 cooking 指南(在 Mathematics 部分)。
所以,我有点困惑还缺少什么。然后,我想起我实际上不是在大学的数学类(class)中(我应该在那里)学到的“矩阵的东西”,而是很久以后从一个有耐心的大学(有数学文凭)那里学到的。
因此,我将尝试用 C++ 提供最简单的演示:
首先,一些包括:
#include <iostream>
#include <iomanip>
#include <cmath>
using namespace std;
...一个方便的功能:
inline float degToRad(float angle) { return 3.141593f * angle / 180.f; }
...一个二维 vector “类”:
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 vector “类”:
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 矩阵类:
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()
函数将所有内容放在一起进行实际演示:
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;
}
这是我在测试中得到的:
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 上上传了整个样本.
上述维基百科链接的数学部分还提到了到 xy 平面的投影。恕我直言,忽略结果 vector 的 z 坐标甚至更简单。但是,正如问题中提到的 OpenGL 一样,保留 z 坐标(例如,用于深度缓冲)可能是值得的。
在 OpenGL 中,使用 4×4 矩阵。这些,是为了支持Homogeneous Coordinates而推出的.简化:齐次坐标用于“将点和方向强制到同一空间”或涉及 3d 转换为 3d 转换(这对于 3×3 矩阵是不可能的)。齐次坐标有点复杂(因此值得另一个问题)。
幸运的是,等轴测投影仅由旋转构建(并且可能是到 xy 平面的投影,我遗漏了它以保留深度缓冲区值)。因此,3×3 矩阵就足够了。
但是,我想至少提一下矩阵在 OpenGL 中的外观(作为 4×4 矩阵):
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)。
有一些开源库可用于在 CPU 端执行数学运算。其中我想提一下:
在 GPU 方面(我知道,至少对于 GLSL),它已经内置。
更新
经过一番交谈后,我重新制定了图形可视化计划。我的意图是保持简短,而不在像 OpenGL 这样的 API 中隐藏数学细节。因此,我决定将它作为 Qt 的唯一示例。这是它的外观:
Qt应用程序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();
}
对于每个 View ,投影被分成两个矩阵。
实际投影提供给WireframeView
的构造函数。
WireframeView
类在内部管理从 NDC ( Normalized Device Coordinates ) 到屏幕空间的第二次转换。这包括缩放(考虑到当前纵横比)、y 坐标的镜像以及原点 (0, 0, 0) 到 View 中心的平移。
这两个矩阵在实际渲染开始之前相乘。在渲染循环中,每个点都与组合 View 矩阵相乘,以将其从(模型)世界坐标转换为屏幕坐标。
我将数学内容移到了单独的头文件 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
最显着的变化(与原始版本相比):
从 3×3 矩阵升级到 4×4 矩阵。 (还需要涉及 3D 翻译)
模板而不是函数和类(可用于float
和double
)。
关于c++ - 如何组成一个矩阵来执行世界坐标的等轴测(二轴测)投影?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44498366/
一个关于Lua和元表的初学者问题,以一个简单的Hello-World为例,涉及len事件,不幸的是它没有返回预期的结果(我使用的是从 Ubuntu 官方存储库安装的 Lua 5.1)。 案子 这是示例
我正在开发一个 OpenGL 应用程序。我需要一个地球自转模型(蓝色的水,绿色的土地)。我的问题不是处理 OpenGL/图形……而是处理数据。 我在哪里可以获得这些数据? (各个国家的边界)。如果
我试图了解在 OpenGL 中创建空间: 对象空间 世界空间 相机空间 投影空间 我对这些阶段的理解是否正确? “立方体”是在笛卡尔坐标系的中心创建的,直接在程序内部输入顶点坐标。 坐标转换为“世界”
我有一个Entity和Bundle的Components,我想附加到将成为第一个实体的子实体的实体上。我可以使用Commands生成带有我的组件的实体,但是我无法获得实际的Entity,这意味着我不能
我想创建一个游戏,它有一个无尽的(实际上是一个非常大的)世界,玩家可以在其中移动。我是否会抽出时间来实现游戏是一回事,但我发现这个想法很有趣,并且希望就如何实现它提供一些意见。 关键是要有一个所有数据
以下代码片段来自维基百科,是标准 Hello World! 的序言! Brainfuck 中的程序... 1. +++++ +++++ initialize counter (
就目前情况而言,这个问题不太适合我们的问答形式。我们期望答案得到事实、引用资料或专业知识的支持,但这个问题可能会引发辩论、争论、民意调查或扩展讨论。如果您觉得这个问题可以改进并可能重新开放,visit
我正在尝试获取特定日期剩余的确切秒数、分钟数等。这听起来可能很愚蠢,但为什么结果会翻倍呢?这看起来不对,是吗? setInterval(function() { var startDate =
如果我有两个嵌套元素,假设: Foo 然后我对外部元素应用旋转,假设顺时针旋转 45 度: Foo 现在我想将内部 div 向下移动一点。 Foo http://j
在 WindowsForms 世界中,您可以获得可用图像编码器/解码器的列表 System.Drawing.ImageCodecInfo.GetImageDecoders() / GetImageEn
因此,作为我的计算机体系结构类(class)的一部分,我需要熟悉 Assembly,或者至少足够舒适,我正在尝试读取用户的输入然后重新打印(暂时),这是我的我是如何尝试用伪代码来展示它的: 声明 ms
在 HLSL 中有很多矩阵乘法,虽然我了解如何以及在何处使用它们,但我不确定它们是如何导出的或它们的实际目标是什么。 所以我想知道是否有在线资源可以解释这一点,我特别好奇将世界矩阵乘以 View 矩阵
我正在使用 http://jvectormap.owl-hollow.net/ 中的 jVectorMap一切正常。但世界地图的标准尺寸非常小。如果有人想打例如波斯尼亚和黑塞哥维那,他需要大眼镜!有可
我在做游戏。游戏由一个无限平面组成。单位必须在一个离散的正方形上,因此可以使用简单的 Location { x :: Int, y :: Int } 来定位它们。 可能有很多种Unit s。有些可能是
所以我创建了一个顶点着色器,它接受一个角度并计算旋转。尽管模型围绕世界中心而不是它自己的轴/原点旋转,但存在一个问题。 旁注:这是 2D 旋转。 如何让模型绕着自己的轴旋转? 这是我当前的顶点着色器:
所以我创建了一个顶点着色器,它接受一个角度并计算旋转。尽管模型围绕世界中心而不是它自己的轴/原点旋转,但存在一个问题。 旁注:这是 2D 旋转。 如何让模型绕着自己的轴旋转? 这是我当前的顶点着色器:
我最近萌生了创建标签云的想法,就像地球形状的动画一样。我从 ngdc.noaa.gov 中提取了海岸线坐标,并编写了一个小脚本,将其显示在我的浏览器中。现在您可以想象,整个海岸线由大约 48919 个
我使用 3d 模式渲染我的 2d 游戏,因为相机旋转和放大/缩小比 2d 模式容易得多。 现在我遇到了一个我似乎想不出如何解决的问题: 如何使我的世界的 2d 平面以 1 个纹理像素匹配屏幕上的 1
我正在寻找一种简单快捷的方法来清除整个 Phaser 屏幕,就像在 HTML5 canvas 中,您可以通过将 Canvas 的宽度重置为自身来删除所有内容。我无法通过搜索找到任何此类方法 - 只有
是否可以为这样的游戏创建一个 2.5D 世界(双龙)http://www.youtube.com/watch?v=xb9P0YTeq5Y使用box2d? 你知道任何工作示例吗? 预先感谢您的时间。 最
我是一名优秀的程序员,十分优秀!