六、OpenGL 中的矩阵

243 阅读5分钟

一、OpenGL 向量的 API 介绍

向量

在 3D 笛卡尔坐标系,基本上,一个顶点就是坐标系空间中的一个点。而在空间中给定一个位置,是由一个单独的 xyz 定义的,而这样的 xyz 就是向量。

长度为1的向量,称为单位向量。

向量长度,即向量的模,计算公式 百度百科

如果一个向量不是单位向量,而我们把它缩放到 1,这个过程叫做标准化,也叫做单位化向量

math3d 库

三维向量: M3DVector3f (x, y, z)
四维向量: M3DVector4f (x, y, z, w) w 缩放因子

    // 声明三维向量
    M3DVector3f vVertor;
    // 声明四维向量
    M3DVector4f vVertex = {0, 0, 1, 1};

向量:点乘

点乘只能发生在两个向量之间。

两个单位向量进行点乘运算,将的到一个标量。它表示两个向量之间的夹角。
其范围为[-1, 1]. 这个值就是夹角的 cos 值(余弦值)。

如果要求两个普通向量的夹角,将两个向量单位化,然后进行点乘。 单位化向量:xyz -> (x/|xyz|, y/|xyz|, z/|xyz|)。

math3d 提供 API:

// 获得连个向量的点乘结果(即夹角)
float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v)
// 获得两个向量的夹角的弧度
float m3dGetAngleBetweenVectors3(const M3DVector3f u, const M3DVector3f v)

向量:叉乘

两个向量之间叉乘,就可以得到另外一个向量,新的向量会与原阿里 2 个向量定义的平面垂直。 叉乘不必为单位向量。

math3d 提供 API:

// 向量叉乘:结果向量,向量1,向量2
m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v)

二、OpenGL 中的矩阵

// 三维矩阵
typedef float M3DMatrix33f[9];
// 四维矩阵
typedef float M3DMatrix44f[16];

OpenGL 倾向使用一维数组。
因为 OpenGL 使用的是 Column-Major(以列为主)矩阵排序的约定。

OpenGL 使用矩阵表示空间中的一个特定位置。
这四列中,每一列都是四个元素组成的向量。

图片来源:码农家园

如果将一个对象所有的顶点向量,乘以这个矩阵,就能让这个对象变换到空间中给定的位置和方向。

OpenGL 初始化矩阵

// 第一种
GLfloat m[] = {
    1,0,0,0, // x Column
    0,1,0,0, // y Column
    0,0,1,0, // z Column
    0,0,0,1  // translation Column
}
// 第二种
M3DMatrix44f m {
    1,0,0,0, // x Column
    0,1,0,0, // y Column
    0,0,1,0, // z Column
    0,0,0,1  // translation Column
}
// 第三种
void m3dLoadIdentity44(M3DMatrix44f m);

单元矩阵

    1,0,0,0,
    0,1,0,0,
    0,0,1,0,
    0,0,0,1 

一个向量与单元矩阵相乘,不会发生任何改变。

矩阵左乘是行变换,矩阵右乘是列变换。

由于 OpenGL 是以列为主,因此当我们要对一个顶点矩阵进行变化的时候,采用矩阵右乘。

三、OpenGL 里的变换

变换 解释
视图变换 指定观察者位置
模型变换 在场景中移动物体
模型视图 描述视图/模型变换的二元性
投影 改变视景体大小和设置它的投影方式
视口 伪变化,对窗口上最终输入进行缩放

视图变换 将观察者放在你希望的任何位置,并允许在任何方向上观察场景,确定视图变换就像在场景中防止观察者,并让它指向某一个方向。

在应用任何其他模型变换之前,必须先应用视图变换。因为视图变换移动了当前的工作的坐标系;后续变换都会基于新调整的坐标系进行。

模型变换
对模型进行旋转、缩放、平移,实现需要的变化。

模型平移

void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)

模型旋转

// angle 旋转弧度, 可以使用 m3dDegToRad 宏进行度数->弧度转换
void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z) 

模型缩放

void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale)

模型变换-综合变换

void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);

四、矩阵堆栈的使用

矩阵堆栈基本API

// 初始化
GLMatrixStack::GLMatrixStack(int iStackDepth = 64);

// 在堆栈顶部载入一个单元矩阵
void GLMatrixStack::LoadIdentity(void);

// 在堆栈顶部载入任何矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);

// 矩阵乘以矩阵堆栈顶部矩阵,相乘结果存在堆栈的顶部
void GLMatrixStack::MultMatrix(const M3DMatrix44f);

// 获取矩阵堆栈顶部的值 GetMatrix 函数
const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
// 也可以获取顶部矩阵副本
void glMatrixStack::GetMatrix(M3DMatrix44f mMatrix);

// 将当前矩阵压入堆栈
void GLMatrixStack::PushMatrix(void);

// 将 M3DMatrix44f 矩阵对象压入当前矩阵堆栈
void PushMatrix(const M3DMatrix44f mMatrix);

// 将 GLFrame 对象压入矩阵堆栈
void PushMatrix(GLFrame &frame);

// 出栈, 移除栈顶矩阵对象
void GLMatrixStack::PopMatrix(void);

仿射变换

// angle 是角度,非弧度
void MatrixStack::Rotate(GLfloat angle, GLfloat x, glfloat y, glfloat z);

void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z);

void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);

GLFrame 使用

// 将堆栈的顶部压入任何矩阵
void GLMatrixStack::LoadMatrix(GLFrame &frame);

// 矩阵乘以矩阵堆栈顶部的矩阵。相乘结果存储在堆栈的顶部
void GLMatrixStack::MultMatrix(GLFrame &frame);

// 将当前的矩阵压栈
void GLMatrixStack::PushMatrix(GLFrame &frame);

// 检索条件合适的观察者矩阵
void GetCameraMatrix(M3DMatrix44f m, bool bRotationOnly = false);