笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,国家专利发明人,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社 和《Unity3D实战核心技术详解》电子工业出版社等书籍。

在3D游戏开发中,固定流水线是不可回避的问题,大部分人都是像被公式一样的记住了固定流水线的步骤,但是对其实现原理并不是很清楚,这样对其深入学习3D技术是非常不利的。笔者在此给读者对相机的投影矩阵做了一下推导,帮助读者理解矩阵的实现过程。这样以后自己封装底层接口时也能很容易的做到。下面主要实现了透视投影矩阵和正交投影矩阵的推导过程,如果大家对于矩阵的计算不是很清楚,可以翻阅一下大学课本《线性代数》。这个矩阵的推导也是为了我正在CSDN讲的视频课程《算法与游戏实战技术》做的补充内容。

在OpenGL或者DirectX中,在3D摄像机空间中的点需要映射到视锥体近裁剪面上(也就是投影面)才能在屏幕上看到。假设在相机空间中的点(xe, ye, ze)映射在近裁剪面后的坐标是(xp,yp,zp),从顶视图的角度显示的效果如下图所示:

在x轴方向看的示意图如下:

从视锥体的顶视图看,在相机空间的x轴,xe映射后的值为xp,可以通过相似三角形的比值计算得到,公式如下所示:

同理,通过侧视图,yp同样可以求得:

通过这两个算式,很容易发现,xp和yp都依赖ze的值,换句话说,它们都是除以-ze ,这样非常容易构建投影矩阵,相机裁剪坐标可以通过相机坐标与投影矩阵相乘得到,裁剪坐标也是一个齐次坐标,它最终会通过与w相除被转化成标准设备坐标(NDC),这也就是我们通常说的视口(0,0,1,1)转换公式如下:

在这里我们可以设置裁剪坐标的w分量为-ze ,从而投影矩阵的第四行数值为(0,0,-1,0),将上述投影矩阵公式转化如下:

接下来,我们设置投影xp 和yp到NDC的xn和yn的关系为:[l, r] ⇒[-1, 1] 和[b, t] ⇒[-1, 1]。先看一下从xp映射到xn的图示如下:

根据上图进行计算公式如下:

映射yp到yn的效果图如下所示:

从而得到计算公式如下所示:

我们将已经计算好的xp和yp算式替换掉上面公式将得到新的公式,如下所示:

通过以上公式计算,我们可以得到投影矩阵的第一行和第二行,继续投影矩阵公式的填充如下:

截止到目前,我们只剩了投影矩阵的第三行还没解决,我们发现zn有一点不同于其他的计算,因为ze在相机空间一直投影在-n的近裁剪面上。但是我们需要独一无二的z值对于裁剪和深度测试。我们知道z值不依赖于x或者y值, 这样我们就能指定投影矩阵的第三行如下所示:

在相机空间,we等于1,因而方程可以表示如下:

为了找到系数A和B,我们使用(ze,zn),分别表示为(-n,-1)和(-f,1),并且把它们带入到上述公式中:

为了解方程将上述公式(1)用B表示为如下公式:

将公式(1’)带入到(2)中后得出A的表达式:

再将A带入(1)求得B公式如下:

求得A和B后,我们可以得到ze和zn的关系如下所示:

将其带入公式可以得到投影矩阵的所有元素,矩阵如下所示:

这个投影矩阵终于完成了,当然我们也可以考虑到特殊情况,如果这个视口是对称的,这样矩阵还能简化,对称满足的条件是r = -l 和t = -b,简化的公式如下:

下面再计算一下正交摄像机的投影矩阵,相对透视摄像机矩阵,这个简单的多,正交摄像机的视口说白了就是一个长方体,效果如下图所示:

它们的计算公式如下所示:

对于正交摄像机来说,w值不是必须的,也就是说投影矩阵的第四行仍然是(0,0,0,1)。因而,正交摄像机的投影矩阵公式如下所示:

至此,矩阵的推导就全部结束了,3D引擎的底层都是采用矩阵的方式进行了封装,但是作为开发者一定要清楚其实现原理。不论开发者使用任何引擎,它的底层封装都需要矩阵计算,为了能够帮助读者理解,下面把封装矩阵的代码给读者展示一下:

class Matrix4
{
public:// constructorsMatrix4();  // init with identityMatrix4(const float src[16]);Matrix4(float xx, float xy, float xz, float xw,float yx, float yy, float yz, float yw,float zx, float zy, float zz, float zw,float wx, float wy, float wz, float ww);void        set(const float src[16]);void        set(float xx, float xy, float xz, float xw,float yx, float yy, float yz, float yw,float zx, float zy, float zz, float zw,float wx, float wy, float wz, float ww);void        setRow(int index, const float row[4]);void        setRow(int index, const Vector4& v);void        setRow(int index, const Vector3& v);void        setColumn(int index, const float col[4]);void        setColumn(int index, const Vector4& v);void        setColumn(int index, const Vector3& v);const float* get() const;const float* getTranspose();                        // return transposed matrixfloat        getDeterminant();Matrix4&    identity();Matrix4&    transpose();                            // transpose itself and return referenceMatrix4&    invert();                               // check best inverse method before inverseMatrix4&    invertEuclidean();                      // inverse of Euclidean transform matrixMatrix4&    invertAffine();                         // inverse of affine transform matrixMatrix4&    invertProjective();                     // inverse of projective matrix using partitioningMatrix4&    invertGeneral();                        // inverse of generic matrix// transform matrixMatrix4&    translate(float x, float y, float z);   // translation by (x,y,z)Matrix4&    translate(const Vector3& v);            //Matrix4&    rotate(float angle, const Vector3& axis); // rotate angle(degree) along the given axixMatrix4&    rotate(float angle, float x, float y, float z);Matrix4&    rotateX(float angle);                   // rotate on X-axis with degreeMatrix4&    rotateY(float angle);                   // rotate on Y-axis with degreeMatrix4&    rotateZ(float angle);                   // rotate on Z-axis with degreeMatrix4&    scale(float scale);                     // uniform scaleMatrix4&    scale(float sx, float sy, float sz);    // scale by (sx, sy, sz) on each axis// operatorsMatrix4     operator+(const Matrix4& rhs) const;    // add rhsMatrix4     operator-(const Matrix4& rhs) const;    // subtract rhsMatrix4&    operator+=(const Matrix4& rhs);         // add rhs and update this objectMatrix4&    operator-=(const Matrix4& rhs);         // subtract rhs and update this objectVector4     operator*(const Vector4& rhs) const;    // multiplication: v' = M * vVector3     operator*(const Vector3& rhs) const;    // multiplication: v' = M * vMatrix4     operator*(const Matrix4& rhs) const;    // multiplication: M3 = M1 * M2Matrix4&    operator*=(const Matrix4& rhs);         // multiplication: M1' = M1 * M2bool        operator==(const Matrix4& rhs) const;   // exact compare, no epsilonbool        operator!=(const Matrix4& rhs) const;   // exact compare, no epsilonfloat       operator[](int index) const;            // subscript operator v[0], v[1]float&      operator[](int index);                  // subscript operator v[0], v[1]friend Matrix4 operator-(const Matrix4& m);                     // unary operator (-)friend Matrix4 operator*(float scalar, const Matrix4& m);       // pre-multiplicationfriend Vector3 operator*(const Vector3& vec, const Matrix4& m); // pre-multiplicationfriend Vector4 operator*(const Vector4& vec, const Matrix4& m); // pre-multiplicationfriend std::ostream& operator<<(std::ostream& os, const Matrix4& m);protected:private:float       getCofactor(float m0, float m1, float m2,float m3, float m4, float m5,float m6, float m7, float m8);float m[16];float tm[16];                                       // transpose m};

它的源码实现如下所示:

Matrix4& Matrix4::transpose()
{std::swap(m[1],  m[4]);std::swap(m[2],  m[8]);std::swap(m[3],  m[12]);std::swap(m[6],  m[9]);std::swap(m[7],  m[13]);std::swap(m[11], m[14]);return *this;
}///
// inverse 4x4 matrix
///
Matrix4& Matrix4::invert()
{// If the 4th row is [0,0,0,1] then it is affine matrix and// it has no projective transformation.if(m[12] == 0 && m[13] == 0 && m[14] == 0 && m[15] == 1)this->invertAffine();else{this->invertGeneral();/*@@ invertProjective() is not optimized (slower than generic one)if(fabs(m[0]*m[5] - m[1]*m[4]) > 0.00001f)this->invertProjective();   // inverse using matrix partitionelsethis->invertGeneral();      // generalized inverse*/}return *this;
}Matrix4& Matrix4::invertEuclidean()
{// transpose 3x3 rotation matrix part// | R^T | 0 |// | ----+-- |// |  0  | 1 |float tmp;tmp = m[1];  m[1] = m[4];  m[4] = tmp;tmp = m[2];  m[2] = m[8];  m[8] = tmp;tmp = m[6];  m[6] = m[9];  m[9] = tmp;// compute translation part -R^T * T// | 0 | -R^T x |// | --+------- |// | 0 |   0    |float x = m[3];float y = m[7];float z = m[11];m[3]  = -(m[0] * x + m[1] * y + m[2] * z);m[7]  = -(m[4] * x + m[5] * y + m[6] * z);m[11] = -(m[8] * x + m[9] * y + m[10]* z);// last row should be unchanged (0,0,0,1)return *this;
}///Matrix4& Matrix4::invertAffine()
{// R^-1Matrix3 r(m[0],m[1],m[2], m[4],m[5],m[6], m[8],m[9],m[10]);r.invert();m[0] = r[0];  m[1] = r[1];  m[2] = r[2];m[4] = r[3];  m[5] = r[4];  m[6] = r[5];m[8] = r[6];  m[9] = r[7];  m[10]= r[8];// -R^-1 * Tfloat x = m[3];float y = m[7];float z = m[11];m[3]  = -(r[0] * x + r[1] * y + r[2] * z);m[7]  = -(r[3] * x + r[4] * y + r[5] * z);m[11] = -(r[6] * x + r[7] * y + r[8] * z);// last row should be unchanged (0,0,0,1)//m[12] = m[13] = m[14] = 0.0f;//m[15] = 1.0f;return * this;
}///
// inverse matrix using matrix partitioning (blockwise inverse)
// It devides a 4x4 matrix into 4 of 2x2 matrices. It works in case of where
// det(A) != 0. If not, use the generic inverse method
// inverse formula.
// M = [ A | B ]    A, B, C, D are 2x2 matrix blocks
//     [ --+-- ]    det(M) = |A| * |D - ((C * A^-1) * B)|
//     [ C | D ]
//
// M^-1 = [ A' | B' ]   A' = A^-1 - (A^-1 * B) * C'
//        [ ---+--- ]   B' = (A^-1 * B) * -D'
//        [ C' | D' ]   C' = -D' * (C * A^-1)
//                      D' = (D - ((C * A^-1) * B))^-1
//
// NOTE: I wrap with () if it it used more than once.
//       The matrix is invertable even if det(A)=0, so must check det(A) before
//       calling this function, and use invertGeneric() instead.
///
Matrix4& Matrix4::invertProjective()
{// partitionMatrix2 a(m[0], m[1], m[4], m[5]);Matrix2 b(m[2], m[3], m[6], m[7]);Matrix2 c(m[8], m[9], m[12], m[13]);Matrix2 d(m[10], m[11], m[14], m[15]);// pre-compute repeated partsa.invert();             // A^-1Matrix2 ab = a * b;     // A^-1 * BMatrix2 ca = c * a;     // C * A^-1Matrix2 cab = ca * b;   // C * A^-1 * BMatrix2 dcab = d - cab; // D - C * A^-1 * B// check determinant if |D - C * A^-1 * B| = 0//NOTE: this function assumes det(A) is already checked. if |A|=0 then,//      cannot use this function.float determinant = dcab[0] * dcab[3] - dcab[1] * dcab[2];if(fabs(determinant) <= 0.00001f){return identity();}// compute D' and -D'Matrix2 d1 = dcab;      //  (D - C * A^-1 * B)d1.invert();            //  (D - C * A^-1 * B)^-1Matrix2 d2 = -d1;       // -(D - C * A^-1 * B)^-1// compute C'Matrix2 c1 = d2 * ca;   // -D' * (C * A^-1)// compute B'Matrix2 b1 = ab * d2;   // (A^-1 * B) * -D'// compute A'Matrix2 a1 = a - (ab * c1); // A^-1 - (A^-1 * B) * C'// assemble inverse matrixm[0] = a1[0];  m[1] = a1[1];  m[2] = b1[0];  m[3] = b1[1];m[4] = a1[2];  m[5] = a1[3];  m[6] = b1[1];  m[2] = b1[3];m[8] = c1[0];  m[9] = c1[1];  m[10]= d1[0];  m[11]= d1[1];m[12]= c1[2];  m[13]= c1[3];  m[14]= d1[2];  m[15]= d1[3];return *this;
}///
// compute the inverse of a general 4x4 matrix using Cramer's Rule
// If cannot find inverse, return indentity matrix
// M^-1 = adj(M) / det(M)
///
Matrix4& Matrix4::invertGeneral()
{// get cofactors of minor matricesfloat cofactor0 = getCofactor(m[5],m[6],m[7], m[9],m[10],m[11], m[13],m[14],m[15]);float cofactor1 = getCofactor(m[4],m[6],m[7], m[8],m[10],m[11], m[12],m[14],m[15]);float cofactor2 = getCofactor(m[4],m[5],m[7], m[8],m[9], m[11], m[12],m[13],m[15]);float cofactor3 = getCofactor(m[4],m[5],m[6], m[8],m[9], m[10], m[12],m[13],m[14]);// get determinantfloat determinant = m[0] * cofactor0 - m[1] * cofactor1 + m[2] * cofactor2 - m[3] * cofactor3;if(fabs(determinant) <= 0.00001f){return identity();}// get rest of cofactors for adj(M)float cofactor4 = getCofactor(m[1],m[2],m[3], m[9],m[10],m[11], m[13],m[14],m[15]);float cofactor5 = getCofactor(m[0],m[2],m[3], m[8],m[10],m[11], m[12],m[14],m[15]);float cofactor6 = getCofactor(m[0],m[1],m[3], m[8],m[9], m[11], m[12],m[13],m[15]);float cofactor7 = getCofactor(m[0],m[1],m[2], m[8],m[9], m[10], m[12],m[13],m[14]);float cofactor8 = getCofactor(m[1],m[2],m[3], m[5],m[6], m[7],  m[13],m[14],m[15]);float cofactor9 = getCofactor(m[0],m[2],m[3], m[4],m[6], m[7],  m[12],m[14],m[15]);float cofactor10= getCofactor(m[0],m[1],m[3], m[4],m[5], m[7],  m[12],m[13],m[15]);float cofactor11= getCofactor(m[0],m[1],m[2], m[4],m[5], m[6],  m[12],m[13],m[14]);float cofactor12= getCofactor(m[1],m[2],m[3], m[5],m[6], m[7],  m[9], m[10],m[11]);float cofactor13= getCofactor(m[0],m[2],m[3], m[4],m[6], m[7],  m[8], m[10],m[11]);float cofactor14= getCofactor(m[0],m[1],m[3], m[4],m[5], m[7],  m[8], m[9], m[11]);float cofactor15= getCofactor(m[0],m[1],m[2], m[4],m[5], m[6],  m[8], m[9], m[10]);// build inverse matrix = adj(M) / det(M)// adjugate of M is the transpose of the cofactor matrix of Mfloat invDeterminant = 1.0f / determinant;m[0] =  invDeterminant * cofactor0;m[1] = -invDeterminant * cofactor4;m[2] =  invDeterminant * cofactor8;m[3] = -invDeterminant * cofactor12;m[4] = -invDeterminant * cofactor1;m[5] =  invDeterminant * cofactor5;m[6] = -invDeterminant * cofactor9;m[7] =  invDeterminant * cofactor13;m[8] =  invDeterminant * cofactor2;m[9] = -invDeterminant * cofactor6;m[10]=  invDeterminant * cofactor10;m[11]= -invDeterminant * cofactor14;m[12]= -invDeterminant * cofactor3;m[13]=  invDeterminant * cofactor7;m[14]= -invDeterminant * cofactor11;m[15]=  invDeterminant * cofactor15;return *this;
}///
// return determinant of 4x4 matrix
///
float Matrix4::getDeterminant()
{return m[0] * getCofactor(m[5],m[6],m[7], m[9],m[10],m[11], m[13],m[14],m[15]) -m[1] * getCofactor(m[4],m[6],m[7], m[8],m[10],m[11], m[12],m[14],m[15]) +m[2] * getCofactor(m[4],m[5],m[7], m[8],m[9], m[11], m[12],m[13],m[15]) -m[3] * getCofactor(m[4],m[5],m[6], m[8],m[9], m[10], m[12],m[13],m[14]);
}///
// compute cofactor of 3x3 minor matrix without sign
// input params are 9 elements of the minor matrix
// NOTE: The caller must know its sign.
///
float Matrix4::getCofactor(float m0, float m1, float m2,float m3, float m4, float m5,float m6, float m7, float m8)
{return m0 * (m4 * m8 - m5 * m7) -m1 * (m3 * m8 - m5 * m6) +m2 * (m3 * m7 - m4 * m6);
}///
// translate this matrix by (x, y, z)
///
Matrix4& Matrix4::translate(const Vector3& v)
{return translate(v.x, v.y, v.z);
}Matrix4& Matrix4::translate(float x, float y, float z)
{m[0] += m[12]*x;   m[1] += m[13]*x;   m[2] += m[14]*x;   m[3] += m[15]*x;m[4] += m[12]*y;   m[5] += m[13]*y;   m[6] += m[14]*y;   m[7] += m[15]*y;m[8] += m[12]*z;   m[9] += m[13]*z;   m[10]+= m[14]*z;   m[11]+= m[15]*z;return *this;
}///
// uniform scale
///
Matrix4& Matrix4::scale(float s)
{return scale(s, s, s);
}Matrix4& Matrix4::scale(float x, float y, float z)
{m[0] = m[0]*x;   m[1] = m[1]*x;   m[2] = m[2]*x;   m[3] = m[3]*x;m[4] = m[4]*y;   m[5] = m[5]*y;   m[6] = m[6]*y;   m[7] = m[7]*y;m[8] = m[8]*z;   m[9] = m[9]*z;   m[10]= m[10]*z;  m[11]= m[11]*z;return *this;
}///
// build a rotation matrix with given angle(degree) and rotation axis, then
// multiply it with this object
///
Matrix4& Matrix4::rotate(float angle, const Vector3& axis)
{return rotate(angle, axis.x, axis.y, axis.z);
}Matrix4& Matrix4::rotate(float angle, float x, float y, float z)
{float c = cosf(angle * DEG2RAD);    // cosinefloat s = sinf(angle * DEG2RAD);    // sinefloat xx = x * x;float xy = x * y;float xz = x * z;float yy = y * y;float yz = y * z;float zz = z * z;// build rotation matrixMatrix4 m;m[0] = xx * (1 - c) + c;m[1] = xy * (1 - c) - z * s;m[2] = xz * (1 - c) + y * s;m[3] = 0;m[4] = xy * (1 - c) + z * s;m[5] = yy * (1 - c) + c;m[6] = yz * (1 - c) - x * s;m[7] = 0;m[8] = xz * (1 - c) - y * s;m[9] = yz * (1 - c) + x * s;m[10]= zz * (1 - c) + c;m[11]= 0;m[12]= 0;m[13]= 0;m[14]= 0;m[15]= 1;// multiply it*this = m * (*this);return *this;
}Matrix4& Matrix4::rotateX(float angle)
{float c = cosf(angle * DEG2RAD);float s = sinf(angle * DEG2RAD);float m4 = m[4], m5 = m[5], m6 = m[6],  m7 = m[7],m8 = m[8], m9 = m[9], m10= m[10], m11= m[11];m[4] = m4 * c + m8 *-s;m[5] = m5 * c + m9 *-s;m[6] = m6 * c + m10*-s;m[7] = m7 * c + m11*-s;m[8] = m4 * s + m8 * c;m[9] = m5 * s + m9 * c;m[10]= m6 * s + m10* c;m[11]= m7 * s + m11* c;return *this;
}Matrix4& Matrix4::rotateY(float angle)
{float c = cosf(angle * DEG2RAD);float s = sinf(angle * DEG2RAD);float m0 = m[0], m1 = m[1], m2 = m[2],  m3 = m[3],m8 = m[8], m9 = m[9], m10= m[10], m11= m[11];m[0] = m0 * c + m8 * s;m[1] = m1 * c + m9 * s;m[2] = m2 * c + m10* s;m[3] = m3 * c + m11* s;m[8] = m0 *-s + m8 * c;m[9] = m1 *-s + m9 * c;m[10]= m2 *-s + m10* c;m[11]= m3 *-s + m11* c;return *this;
}Matrix4& Matrix4::rotateZ(float angle)
{float c = cosf(angle * DEG2RAD);float s = sinf(angle * DEG2RAD);float m0 = m[0], m1 = m[1], m2 = m[2],  m3 = m[3],m4 = m[4], m5 = m[5], m6 = m[6],  m7 = m[7];m[0] = m0 * c + m4 *-s;m[1] = m1 * c + m5 *-s;m[2] = m2 * c + m6 *-s;m[3] = m3 * c + m7 *-s;m[4] = m0 * s + m4 * c;m[5] = m1 * s + m5 * c;m[6] = m2 * s + m6 * c;m[7] = m3 * s + m7 * c;return *this;
}

矩阵实现了,接下来就需要调用其接口实现视景体裁剪和投影矩阵了,代码如下所示:

///
// compute 8 vertices of frustum
///
void ModelGL::computeFrustumVertices(float l, float r, float b, float t, float n, float f)
{float ratio;float farLeft;float farRight;float farBottom;float farTop;// perspective modeif(projectionMode == 0)ratio     = f / n;// orthographic modeelseratio = 1;farLeft   = l * ratio;farRight  = r * ratio;farBottom = b * ratio;farTop    = t * ratio;// compute 8 vertices of the frustum// near top rightfrustumVertices[0].x = r;frustumVertices[0].y = t;frustumVertices[0].z = -n;// near top leftfrustumVertices[1].x = l;frustumVertices[1].y = t;frustumVertices[1].z = -n;// near bottom leftfrustumVertices[2].x = l;frustumVertices[2].y = b;frustumVertices[2].z = -n;// near bottom rightfrustumVertices[3].x = r;frustumVertices[3].y = b;frustumVertices[3].z = -n;// far top rightfrustumVertices[4].x = farRight;frustumVertices[4].y = farTop;frustumVertices[4].z = -f;// far top leftfrustumVertices[5].x = farLeft;frustumVertices[5].y = farTop;frustumVertices[5].z = -f;// far bottom leftfrustumVertices[6].x = farLeft;frustumVertices[6].y = farBottom;frustumVertices[6].z = -f;// far bottom rightfrustumVertices[7].x = farRight;frustumVertices[7].y = farBottom;frustumVertices[7].z = -f;// compute normalsfrustumNormals[0] = (frustumVertices[5] - frustumVertices[1]).cross(frustumVertices[2] - frustumVertices[1]);frustumNormals[0].normalize();frustumNormals[1] = (frustumVertices[3] - frustumVertices[0]).cross(frustumVertices[4] - frustumVertices[0]);frustumNormals[1].normalize();frustumNormals[2] = (frustumVertices[6] - frustumVertices[2]).cross(frustumVertices[3] - frustumVertices[2]);frustumNormals[2].normalize();frustumNormals[3] = (frustumVertices[4] - frustumVertices[0]).cross(frustumVertices[1] - frustumVertices[0]);frustumNormals[3].normalize();frustumNormals[4] = (frustumVertices[1] - frustumVertices[0]).cross(frustumVertices[3] - frustumVertices[0]);frustumNormals[4].normalize();frustumNormals[5] = (frustumVertices[7] - frustumVertices[4]).cross(frustumVertices[5] - frustumVertices[4]);frustumNormals[5].normalize();
}///
// update projection matrix
///
void ModelGL::updateProjectionMatrix()
{if(projectionMode == 0)         // perspective{setFrustum(projectionLeft, projectionRight,projectionBottom, projectionTop,projectionNear, projectionFar);}else if(projectionMode == 1)    // orthographic{setOrthoFrustum(projectionLeft, projectionRight,projectionBottom, projectionTop,projectionNear, projectionFar);}
}
///
// set a perspective frustum with 6 params similar to glFrustum()
// (left, right, bottom, top, near, far)
// Note: this is for row-major notation. OpenGL needs transpose it
///
void ModelGL::setFrustum(float l, float r, float b, float t, float n, float f)
{matrixProjection.identity();matrixProjection[0]  =  2 * n / (r - l);matrixProjection[2]  =  (r + l) / (r - l);matrixProjection[5]  =  2 * n / (t - b);matrixProjection[6]  =  (t + b) / (t - b);matrixProjection[10] = -(f + n) / (f - n);matrixProjection[11] = -(2 * f * n) / (f - n);matrixProjection[14] = -1;matrixProjection[15] =  0;
}///
// set a symmetric perspective frustum with 4 params similar to gluPerspective
// (vertical field of view, aspect ratio, near, far)
///
void ModelGL::setFrustum(float fovY, float aspectRatio, float front, float back)
{float tangent = tanf(fovY/2 * DEG2RAD);   // tangent of half fovYfloat height = front * tangent;           // half height of near planefloat width = height * aspectRatio;       // half width of near plane// params: left, right, bottom, top, near, farsetFrustum(-width, width, -height, height, front, back);
}///
// set a orthographic frustum with 6 params similar to glOrtho()
// (left, right, bottom, top, near, far)
// Note: this is for row-major notation. OpenGL needs transpose it
///
void ModelGL::setOrthoFrustum(float l, float r, float b, float t, float n, float f)
{matrixProjection.identity();matrixProjection[0]  =  2 / (r - l);matrixProjection[3]  =  -(r + l) / (r - l);matrixProjection[5]  =  2 / (t - b);matrixProjection[7]  =  -(t + b) / (t - b);matrixProjection[10] = -2 / (f - n);matrixProjection[11] = -(f + n) / (f - n);
}

最终实现的透视投影和正交投影效果图如下所示:

关于投影矩阵的推导和实现就给读者推导到这里,后面会把固定流水线的其它矩阵推导也发布出来,供大家参考。

3D游戏之投影矩阵算法技术实现相关推荐

  1. 3d游戏开发――辐射度算法

    Hugo Elias 何咏 译 声明:本文原文由Hugo Elias 撰写,由何咏 翻译.本文仅供学习交流之用. 任何人未经本人同意不得私自转载,任何人不得将本文用于任何商业活动. 简 介:这篇文章是 ...

  2. Android+3D游戏开发技术详解与典型案例

    内容导读 本书共分两篇,第一篇介绍了Android 3D游戏开发的基础知识,主要对OpenGL ES的相关内容进行了介绍. 章 名主 要 内 容 第1章 英雄还看今朝-Android简介本章介绍了市场 ...

  3. 3d游戏编程(转帖)

    3d游戏编程(转帖) 我先声明,我不是编程高手,我还只是个初学者,但我觉得我所知道的对刚入门3D游戏编程的新手,应该能让他们少走弯路,我也很想用朝语来写,但是朝语的词 汇库很久没有更新过了,有些专业的 ...

  4. 计算机视觉 图像形成 几何图形和变换 3D到2D投影

    一.正交和平行透视法 现在我们知道如何表示2D和3D几何图元以及如何在空间上转换它们,我们需要指定如何将 3D图元投影到图像平面上. 我们可以使用线性3D到2D投影矩阵来做到这一点.最简单的模型是正交 ...

  5. quake3 android,Android 3D游戏引擎研发(基于Quake3开源引擎移植)

    摘要: 随着移动设备的性能提高和3D图形技术在移动设备领域的应用,在移动设备上研发3D游戏.3D场景将会成为一个新的亮点.3D游戏引擎是研发3D游戏.3D场景的核心技术.目前Android系统发展迅速 ...

  6. 转:3D游戏引擎技术剖析

     转自 http://blog.csdn.net/jbjwpzyl3611421/article/details/12681047 3D游戏引擎技术剖析 分类: Unity3D2013-10-13 1 ...

  7. Android 3D游戏开发技术详解与典型案例

    下载地址 <Android3D游戏开发技术详解与典型案例>主要以Android平台下3D游戏的开发为主题,并结合真实的案例向读者详细介绍了OpenGL ES的基础 知识及3D游戏程序开发的 ...

  8. 3D游戏技术 - 大型3D地图优化渲染技术

    技术简介: 如果需要渲染一个大型3D地图,由于数据量,需要渲染的东西非常多,所以尤其一些慢一点的机器就会变得非常卡. 如下面这些会造成帧率(FPS)下降的图: 这样的图: 还有这样的魔兽世界的图: 还 ...

  9. 3D 投影矩阵学习1

    先看一下名词: 在矩阵数学中,也有一个名词叫投影矩阵,其定义为:     若矩阵A既是对称矩阵,又是幂等矩阵,则称A为投影矩阵. 这里说的是3D世界中的投影矩阵:其作用是帮助把3D物体显示在2维计算机 ...

最新文章

  1. WP8模拟器启动失败解决方法
  2. zabbix mysql.status_Zabbix 监控 Mysql 状态
  3. 摇滚bono_Java比以往任何时候都摇滚
  4. 如何改变控件内的字体颜色?
  5. delphi ini文件的基础读写。
  6. c语言 json_dumps,关于json.dumps中的参数,例如ensure_ascii
  7. 黑马程序猿————OC在Foundation框架结构和字符串
  8. 【7gyy】让Win7系统下的硬盘不在狂闪的诀窍
  9. 将标准的EclipseWTP项目转化成具有Gradle功能的EclipseWTP项目
  10. 普通程序员如何转向人工智能方向?
  11. CAFFE源码学习之优化方法solver
  12. 2022年全球与中国电缆悬挂夹市场现状及未来发展趋势
  13. 华为——宏达电最需要害怕的新对手
  14. linux颜色吸取工具,深度商店应用QElectroTech、Krita、深度取色器、深度截图
  15. 一个可以绑定多个天翼云网盘的目录列表程序,支持视频播放
  16. 红蓝对抗之蓝队防守:ATTCK框架的应用
  17. 传智播客python视频百度云盘下载_传的解释|传的意思|汉典“传”字的基本解释...
  18. 关于php的笑话,10个小笑话(太精辟!)
  19. 童年记忆中的金银花露
  20. Linux 进程状态D Disk Sleep

热门文章

  1. 金地农村土地承包经营权证打证系统
  2. 奥运比赛电视直播一览表
  3. PiaolinPlatformV3.0.0 - 调用手机或电脑摄像头进行拍摄(拍照模块上线)
  4. linux设置r语言环境,R语言 环境设置
  5. 分布式技术原理与实战45讲--05 第05讲:共识问题:区块链如何确认记账权?
  6. Hive-beeline启动脚本
  7. 集合中某几个数字之和等于一个固定值 java
  8. 魅族mx5无法连接远程服务器,魅族mx5手机无法连接电脑怎么办
  9. 天宝营养冲刺深交所:年营收12.5亿 拟募资7亿
  10. 【C语言】三子棋(智能下棋 + 阻拦玩家)