首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 移动开发 > 移动开发 >

从零开始学习OpenGL ES之7 – 变换和矩阵

2012-06-26 
从零开始学习OpenGL ES之七 – 变换和矩阵?今天的主题是我一度谈之色变的。概念上讲,它是3D编程中最为困难的

从零开始学习OpenGL ES之七 – 变换和矩阵

?

今天的主题是我一度谈之色变的。概念上讲,它是3D编程中最为困难的部分。


















既然我们有将矩阵相乘的能力,我们就可以合并多个矩阵。由于我们的矩阵相乘的方法是硬件加速的,而OpenGL ES不支持矩阵与矩阵相乘的硬件加速,所以我们的方法应该比内嵌的变换方法要快一些3?。我们现在开始进行转移变换。


我们可以将其转换为函数:

static inline void Matrix3DSetTranslation(Matrix3D matrix, GLfloat xTranslate,
??? GLfloat yTranslate, GLfloat zTranslate)
{
??? matrix[0] = matrix[5] =? matrix[10] = matrix[15] = 1.0;
??? matrix[1] = matrix[2] = matrix[3] = matrix[4] = 0.0;
??? matrix[6] = matrix[7] = matrix[8] = matrix[9] = 0.0;
??? matrix[11] = 0.0;
??? matrix[12] = xTranslate;
??? matrix[13] = yTranslate;
??? matrix[14] = zTranslate;
}

现在,我们怎样将其引入到 drawView: 方法中? 我们可以删除 glTranslatef(),替换为定义另一个矩阵并赋予适当的转移值,然后将其与当前矩阵相乘最后将结果加载到OpenGL的代码,对吗?

??? static Matrix3D??? identityMatrix;
??? Matrix3DSetIdentity(identityMatrix);
??? static Matrix3D??? translateMatrix;
??? Matrix3DSetTranslation(translateMatrix, 0.0, yPos, -3.0);
??? static Matrix3D??? resultMatrix;
??? Matrix3DMultiply(identityMatrix, translateMatrix, resultMatrix);
??? glLoadMatrixf(resultMatrix);

是的,这可以工作,但它做了一些不必要的工作。记住,如果你将矩阵与单元矩阵相乘,其结果一定是矩阵本身。所以当使用自定义矩阵时,如果要进行任何变换,我们不再需要首先加载单元矩阵。我们只需要创建变换矩阵并加载它:

??? static Matrix3D??? translateMatrix;
??? Matrix3DSetTranslation(translateMatrix, 0.0, yPos, -3.0);
??? glLoadMatrixf(translateMatrix);

由于不需要加载单元矩阵,此方法可以省去一些工作。另外,注意我将Matrix3D定义为static。我们不希望经常分配和解除内存分配。我们知道当程序运行时每一秒钟都需要用到此矩阵许多次,所以将其定义为static可以省去分配和解除内存分配的开销。


自定义尺寸变换一个用于物体尺寸变换的矩阵像这样:

从零开始学习OpenGL ES之7 – 变换和矩阵

x, y, 或 z 为1.0表示在相应方向上尺寸无变化。3个值都为1.0代表单元矩阵。如果你赋值为2.0,则物在相应轴上尺寸加倍。我们可以将尺寸变换矩阵转化为OpenGL ES矩阵,像这样:

static inline void Matrix3DSetScaling(Matrix3D matrix, GLfloat xScale,
??? GLfloat yScale, GLfloat zScale)
{
??? matrix[1] = matrix[2] = matrix[3] = matrix[4] = 0.0;
??? matrix[6] = matrix[7] = matrix[8] = matrix[9] = 0.0;
??? matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;
??? matrix[0] = xScale;
??? matrix[5] = yScale;
??? matrix[10] = zScale;
??? matrix[15] = 1.0;
}

现在,因为我们需要使用超过一种变换,我们将使用矩阵乘法。要进行尺寸变换和旋转,我们需要将这两个矩阵相乘。删除先前代码中的 glScalef() 替换为下列代码:

??? static Matrix3D??? translateMatrix;
??? Matrix3DSetTranslation(translateMatrix, 0.0, yPos, -3.0);
??? static Matrix3D??? scaleMatrix;
??? Matrix3DSetScaling(scaleMatrix, scale, scale, scale);
??? static Matrix3D???? resultMatrix;
??? Matrix3DMultiply(translateMatrix, scaleMatrix, resultMatrix);
??? glLoadMatrixf(resultMatrix);

我们创建一个矩阵并赋予其适当的转移值。然后创建尺寸变换矩阵并赋值。然后将这两个矩阵相乘将结果加载到模型视口矩阵中。

?

自定义旋转旋转稍微有点难度。我们可以创建绕各轴旋转的矩阵。我们已经知道绕Z轴旋转的矩阵像这样:

从零开始学习OpenGL ES之7 – 变换和矩阵

绕X轴像这样:

从零开始学习OpenGL ES之7 – 变换和矩阵

绕 Y轴旋转:

从零开始学习OpenGL ES之7 – 变换和矩阵

这三种旋转写成函数:

static inline void Matrix3DSetXRotationUsingRadians(Matrix3D matrix, GLfloat degrees){    matrix[0] = matrix[15] = 1.0;    matrix[1] = matrix[2] = matrix[3] = matrix[4] = 0.0;    matrix[7] = matrix[8] = 0.0;    matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;    matrix[5] = cosf(degrees);    matrix[6] = -fastSinf(degrees);    matrix[9] = -matrix[6];    matrix[10] = matrix[5];}static inline void Matrix3DSetXRotationUsingDegrees(Matrix3D matrix, GLfloat degrees){    Matrix3DSetXRotationUsingRadians(matrix, degrees * M_PI / 180.0);}static inline void Matrix3DSetYRotationUsingRadians(Matrix3D matrix, GLfloat degrees){    matrix[0] = cosf(degrees);    matrix[2] = fastSinf(degrees);    matrix[8] = -matrix[2];    matrix[10] = matrix[0];    matrix[1] = matrix[3] = matrix[4] = matrix[6] = matrix[7] = 0.0;    matrix[9] = matrix[11] = matrix[13] = matrix[12] = matrix[14] = 0.0;    matrix[5] = matrix[15] = 1.0;}static inline void Matrix3DSetYRotationUsingDegrees(Matrix3D matrix, GLfloat degrees){    Matrix3DSetYRotationUsingRadians(matrix, degrees * M_PI / 180.0);}static inline void Matrix3DSetZRotationUsingRadians(Matrix3D matrix, GLfloat degrees){    matrix[0] = cosf(degrees);    matrix[1] = fastSinf(degrees);    matrix[4] = -matrix[1];    matrix[5] = matrix[0];    matrix[2] = matrix[3] = matrix[6] = matrix[7] = matrix[8] = 0.0;    matrix[9] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;    matrix[10] = matrix[15] = 1.0;}static inline void Matrix3DSetZRotationUsingDegrees(Matrix3D matrix, GLfloat degrees){    Matrix3DSetZRotationUsingRadians(matrix, degrees * M_PI / 180.0);}

对于各轴的旋转有两种方法,一种是使用弧度,另一种是使用角度。这三个矩阵被称为Eular angle?。有一个问题是我们必须按顺序对多个轴进行旋转,当我们对这三个角度进行旋转时,我们可能会遇到一种被称作gimbal lock(框架自锁)的现像,它导致其中一个轴的旋转被锁定。为防止这种现象的发生,我们必须创建一个可以处理多轴旋转的矩阵。除了可以消除自锁的问题,它还能在物体需要绕多轴旋转时节省处理器的开销。


此矩阵要求向量是以单元向量(即法线)方式被传递的,所以我们在为矩阵赋值前必须保证这点。表示为OpenGL矩阵,像这样:

static inline void Matrix3DSetRotationByRadians(Matrix3D matrix, GLfloat angle,    GLfloat x, GLfloat y, GLfloat z){    GLfloat mag = sqrtf((x*x) + (y*y) + (z*z));    if (mag == 0.0)    {        x = 1.0;        y = 0.0;        z = 0.0;    }    else if (mag != 1.0)    {        x /= mag;        y /= mag;        z /= mag;    }    GLfloat c = cosf(angle);    GLfloat s = fastSinf(angle);    matrix[3] = matrix[7] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;    matrix[15] = 1.0;    matrix[0] = (x*x)*(1-c) + c;    matrix[1] = (y*x)*(1-c) + (z*s);    matrix[2] = (x*z)*(1-c) - (y*s);    matrix[4] = (x*y)*(1-c)-(z*s);    matrix[5] = (y*y)*(1-c)+c;    matrix[6] = (y*z)*(1-c)+(x*s);    matrix[8] = (x*z)*(1-c)+(y*s);    matrix[9] = (y*z)*(1-c)-(x*s);    matrix[10] = (z*z)*(1-c)+c;}static inline void Matrix3DSetRotationByDegrees(Matrix3D matrix, GLfloat angle,    GLfloat x, GLfloat y, GLfloat z){    Matrix3DSetRotationByRadians(matrix, angle * M_PI / 180.0, x, y, z);}


下面是代码:

static inline void Matrix3DSetShear(Matrix3D matrix, GLfloat xShear, GLfloat yShear){    matrix[0] = matrix[5] =  matrix[10] = matrix[15] = 1.0;    matrix[1] = matrix[2] = matrix[3] = 0.0;    matrix[6] = matrix[7] = matrix[8] = matrix[9] = 0.0;    matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0.0;    matrix[1] = xShear;    matrix[4] = yShear;}

如果我们应用剪切矩阵,我们将得到:

从零开始学习OpenGL ES之7 – 变换和矩阵

尝试一下用内嵌函数实现。你可以组合矩阵调用。例如,创建一个函数不需要任何矩阵乘法生成转移和尺寸变换。


结论


矩阵是一个很大而且经常让人误解的课题,许多人(包括我自己)都在殚精竭虑去理解它。希望本文能给你足够的帮助,而且还提供了一个矩阵相关的库,你可以用在自己的项目中。如果你希望下载自己尝试一下,请下载feePart6Projectl。我定义了两个常量,一个用来打开/关闭自定义变换,另一个用来打开/关闭剪切变换。它们在GLViewController.h?中:

#define USE_CUSTOM_MATRICES 1#define USE_SHEAR_TRANSFORM 1

1为打开,0为关闭。另外我还更新了?OpenGL ES Xcode TemEmpty%20OpenGL%20ES%20Application?项目使其包括了新的矩阵函数,包括向量化矩阵乘法函数。如果你无法完全消化,你也无需担心。对于开发OpenGL ES程序的99%的人来说,你不需要完全理解透视空间,同质坐标或线性变换,只需大体上的了解即可。


感谢?Snappy Touch?的?Noel Llopis。

注脚

  1. 实际上,当你调用glLoadIdentity()时, 你加载了一个 4×4 的单元矩阵。
  2. 如果你希望使用 struct 而不是? typedef 时,你必须记住要以引用方式传递参数。struct并不会像数组一样自动以引用方式被传递。
  3. 使用Shark测试,drawView: 方法从.7% 的处理时间降低为 .1% 的处理时间。对于此方法而言,速度有本质的提高,但对程序总体而言,影响不大。

热点排行