Open GL ES
变换矩阵详解
一、坐标空间变换流程
局部空间 ->Model Matrix(模型矩阵)
-> 世界空间
世界空间->View Matrix(视图矩阵)
->观察空间
观察空间 ->Projection Matrix(投影矩阵)
->裁剪空间
裁剪空间 ->ViewPort Transform(视口变换)
>屏幕空间
二、变换矩阵及计算
1. 模型矩阵Model Matrix
- 方法:
Matrix.rotateM(), Matrix.translateM(), Matrix.scaleM()
- 公式:
- 位移、缩放计算过程
[ Scale_x 0 0 Translation_x ] [ x ] [ Scale_x·x + Translation_x ]
[ 0 Scale_y 0 Translation_y ] × [ y ] = [ Scale_y·y + Translation_y ]
[ 0 0 Scale_z Translation_z ] [ z ] [ Scale_z·z + Translation_z ]
[ 0 0 0 1 ] [ 1 ] [ 1 ]
- 旋转计算过程
[ cos(θ) -sin(θ) 0 0 ] [ x ] [ x·cos(θ) - y·sin(θ) ]
[ sin(θ) cos(θ) 0 0 ] × [ y ] = [ x·sin(θ) + y·cos(θ) ]
[ 0 0 1 0 ] [ z ] [ z ]
[ 0 0 0 1 ] [ 1 ] [ 1 ]
2. 视图矩阵View Matrix
- 方法:
Matrix.setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)
- 参数说明:
eyeX, eyeY, eyeZ
: 相机位置坐标
centerX, centerY, centerZ
: 物体位置坐标
upX, upY, upZ
: 相机上方向向量(通常为(0,1,0)
- 数学函数定义:
- 向量归一化
normalize
:
normalize(v) = v / |v|
- 其中
|v|
是向量v
的长度(模):
|v| = √(v.x² + v.y² + v.z²)
- 向量叉积
cross product
:
cross(a, b) = [a.y·b.z - a.z·b.y, a.z·b.x - a.x·b.z, a.x·b.y - a.y·b.x]
- 向量点积
dot product
:
dot(a, b) = a.x·b.x + a.y·b.y + a.z·b.z
- 计算过程:
- 计算前方向向量:
Front = normalize(Center - Eye)
Front.x = (centerX - eyeX) / √[(centerX - eyeX)² + (centerY - eyeY)² + (centerZ - eyeZ)²]
Front.y = (centerY - eyeY) / √[(centerX - eyeX)² + (centerY - eyeY)² + (centerZ - eyeZ)²]
Front.z = (centerZ - eyeZ) / √[(centerX - eyeX)² + (centerY - eyeY)² + (centerZ - eyeZ)²]
- 计算右方向向量:
Right = normalize(cross(Front, Up))
Right.x = (Front.y·upZ - Front.z·upY) / √[(Front.y·upZ - Front.z·upY)² + (Front.z·upX - Front.x·upZ)² + (Front.x·upY - Front.y·upX)²]
Right.y = (Front.z·upX - Front.x·upZ) / √[(Front.y·upZ - Front.z·upY)² + (Front.z·upX - Front.x·upZ)² + (Front.x·upY - Front.y·upX)²]
Right.z = (Front.x·upY - Front.y·upX) / √[(Front.y·upZ - Front.z·upY)² + (Front.z·upX - Front.x·upZ)² + (Front.x·upY - Front.y·upX)²]
- 重新计算上方向向量(确保正交):
Up = cross(Right, Front)
Up.x = Right.y·Front.z - Right.z·Front.y
Up.y = Right.z·Front.x - Right.x·Front.z
Up.z = Right.x·Front.y - Right.y·Front.x
(此处Up
已经是单位向量,因为Right
和Front
都是单位向量且相互垂直)
公式:
[ Right_x Right_y Right_z -dot(Right,Eye) ] [ x ] [ Right_x·x + Right_y·y + Right_z·z - dot(Right,Eye) ]
[ Up_x Up_y Up_z -dot(Up,Eye) ] × [ y ] = [ Up_x·x + Up_y·y + Up_z·z - dot(Up,Eye) ]
[ -Front_x -Front_y -Front_z dot(Front,Eye) ] [ z ] [ -Front_x·x - Front_y·y - Front_z·z + dot(Front,Eye) ]
[ 0 0 0 1 ] [ 1 ] [ 1 ]
其中:
dot(Right,Eye) = Right.x·eyeX + Right.y·eyeY + Right.z·eyeZ
dot(Up,Eye) = Up.x·eyeX + Up.y·eyeY + Up.z·eyeZ
dot(Front,Eye) = Front.x·eyeX + Front.y·eyeY + Front.z·eyeZ
3. 投影矩阵Projection Matrix
3.1 正交投影
- 方法:
Matrix.orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)
- 参数说明:
left, right
: 近平面左右边界坐标
bottom, top
: 近平面下上边界坐标
near, far
: 近平面和远平面到相机的距离 - 取值范围与建议:
left < right, bottom < top
,near < far
near
和far
通常为正值,但某些实现中可以为负值
坐标范围取决于场景大小,常见如(-10,10,-10,10,1,100)
- 公式:
[ 2/(right-left) 0 0 -(right+left)/(right-left) ] [ x ] [ 2x/(right-left) - (right+left)/(right-left) ]
[ 0 2/(top-bottom) 0 -(top+bottom)/(top-bottom) ] × [ y ] = [ 2y/(top-bottom) - (top+bottom)/(top-bottom) ]
[ 0 0 -2/(far-near) -(far+near)/(far-near) ] [ z ] [ -2z/(far-near) - (far+near)/(far-near) ]
[ 0 0 0 1 ] [ 1 ] [ 1 ]
- 特点:
- 保持物体原始比例,不会因距离而变形
- 视锥体是长方体形状
- 适合
2D
绘制和UI
界面
3.2 透视投影
- 方法:
Matrix.frustumM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)
- 参数说明:
left, right
: 近平面左右边界坐标
bottom, top
: 近平面上下边界坐标
near
: 近平面距离,必须为正值
far
: 远平面距离,必须为正值且大于near
- 取值范围与建议:
left=-right, bottom=-top
near
不宜过小,通常0.1-1.0
far
通常10-1000
,取决于场景大小 - 公式:
[ 2*near/(right-left) 0 (right+left)/(right-left) 0 ] [ x ]
[ 0 2*near/(top-bottom) (top+bottom)/(top-bottom) 0 ] × [ y ]
[ 0 0 -(far+near)/(far-near) -2*far*near/(far-near) ] [ z ]
[ 0 0 -1 0 ] [ 1 ]
- 结果:
[ 2*near*x/(right-left) + (right+left)*z/(right-left) ]
[ 2*near*y/(top-bottom) + (top+bottom)*z/(top-bottom) ]
[ -(far+near)*z/(far-near) - 2*far*near/(far-near) ]
[ -z ]
- 特点:
- 模拟人眼视觉,远处物体显得更小
- 适合大多数
3D
场景渲染
三、MVP
矩阵组合计算
- 方法:
Matrix.multiplyMM()
- 计算流程:
// 模型视图矩阵Model x View = 视图矩阵View × 模型矩阵Model
Matrix.multiplyMM(mvpMatrix, 0, viewMatrix, 0, modelMatrix, 0)
// MVP矩阵 = 投影矩阵Projection × 模型视图矩阵Model View = Projection × View × Model
Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, mvpMatrix, 0)
// 最终坐标变换
// [x', y', z', w'] = MVP × [x, y, z, 1]
- 注意事项:
OpenGL
中矩阵乘法是右结合的,从右到左计算- 变换顺序非常重要: 先模型变换,再视图变换,最后投影变换
- 最终变换结果应用于顶点着色器的
gl_Position