3D游戏之投影矩阵算法技术实现
笔者介绍:姜雪伟,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游戏之投影矩阵算法技术实现相关推荐
- 3d游戏开发――辐射度算法
Hugo Elias 何咏 译 声明:本文原文由Hugo Elias 撰写,由何咏 翻译.本文仅供学习交流之用. 任何人未经本人同意不得私自转载,任何人不得将本文用于任何商业活动. 简 介:这篇文章是 ...
- Android+3D游戏开发技术详解与典型案例
内容导读 本书共分两篇,第一篇介绍了Android 3D游戏开发的基础知识,主要对OpenGL ES的相关内容进行了介绍. 章 名主 要 内 容 第1章 英雄还看今朝-Android简介本章介绍了市场 ...
- 3d游戏编程(转帖)
3d游戏编程(转帖) 我先声明,我不是编程高手,我还只是个初学者,但我觉得我所知道的对刚入门3D游戏编程的新手,应该能让他们少走弯路,我也很想用朝语来写,但是朝语的词 汇库很久没有更新过了,有些专业的 ...
- 计算机视觉 图像形成 几何图形和变换 3D到2D投影
一.正交和平行透视法 现在我们知道如何表示2D和3D几何图元以及如何在空间上转换它们,我们需要指定如何将 3D图元投影到图像平面上. 我们可以使用线性3D到2D投影矩阵来做到这一点.最简单的模型是正交 ...
- quake3 android,Android 3D游戏引擎研发(基于Quake3开源引擎移植)
摘要: 随着移动设备的性能提高和3D图形技术在移动设备领域的应用,在移动设备上研发3D游戏.3D场景将会成为一个新的亮点.3D游戏引擎是研发3D游戏.3D场景的核心技术.目前Android系统发展迅速 ...
- 转:3D游戏引擎技术剖析
转自 http://blog.csdn.net/jbjwpzyl3611421/article/details/12681047 3D游戏引擎技术剖析 分类: Unity3D2013-10-13 1 ...
- Android 3D游戏开发技术详解与典型案例
下载地址 <Android3D游戏开发技术详解与典型案例>主要以Android平台下3D游戏的开发为主题,并结合真实的案例向读者详细介绍了OpenGL ES的基础 知识及3D游戏程序开发的 ...
- 3D游戏技术 - 大型3D地图优化渲染技术
技术简介: 如果需要渲染一个大型3D地图,由于数据量,需要渲染的东西非常多,所以尤其一些慢一点的机器就会变得非常卡. 如下面这些会造成帧率(FPS)下降的图: 这样的图: 还有这样的魔兽世界的图: 还 ...
- 3D 投影矩阵学习1
先看一下名词: 在矩阵数学中,也有一个名词叫投影矩阵,其定义为: 若矩阵A既是对称矩阵,又是幂等矩阵,则称A为投影矩阵. 这里说的是3D世界中的投影矩阵:其作用是帮助把3D物体显示在2维计算机 ...
最新文章
- WP8模拟器启动失败解决方法
- zabbix mysql.status_Zabbix 监控 Mysql 状态
- 摇滚bono_Java比以往任何时候都摇滚
- 如何改变控件内的字体颜色?
- delphi ini文件的基础读写。
- c语言 json_dumps,关于json.dumps中的参数,例如ensure_ascii
- 黑马程序猿————OC在Foundation框架结构和字符串
- 【7gyy】让Win7系统下的硬盘不在狂闪的诀窍
- 将标准的EclipseWTP项目转化成具有Gradle功能的EclipseWTP项目
- 普通程序员如何转向人工智能方向?
- CAFFE源码学习之优化方法solver
- 2022年全球与中国电缆悬挂夹市场现状及未来发展趋势
- 华为——宏达电最需要害怕的新对手
- linux颜色吸取工具,深度商店应用QElectroTech、Krita、深度取色器、深度截图
- 一个可以绑定多个天翼云网盘的目录列表程序,支持视频播放
- 红蓝对抗之蓝队防守:ATTCK框架的应用
- 传智播客python视频百度云盘下载_传的解释|传的意思|汉典“传”字的基本解释...
- 关于php的笑话,10个小笑话(太精辟!)
- 童年记忆中的金银花露
- Linux 进程状态D Disk Sleep
热门文章
- 金地农村土地承包经营权证打证系统
- 奥运比赛电视直播一览表
- PiaolinPlatformV3.0.0 - 调用手机或电脑摄像头进行拍摄(拍照模块上线)
- linux设置r语言环境,R语言 环境设置
- 分布式技术原理与实战45讲--05 第05讲:共识问题:区块链如何确认记账权?
- Hive-beeline启动脚本
- 集合中某几个数字之和等于一个固定值 java
- 魅族mx5无法连接远程服务器,魅族mx5手机无法连接电脑怎么办
- 天宝营养冲刺深交所:年营收12.5亿 拟募资7亿
- 【C语言】三子棋(智能下棋 + 阻拦玩家)