Open GL ES ->模型矩阵、视图矩阵、投影矩阵等变换矩阵数学推导以及方法接口说明

Open GL ES 变换矩阵详解

一、坐标空间变换流程

局部空间 ->Model Matrix(模型矩阵)-> 世界空间

世界空间->View Matrix(视图矩阵)->观察空间

观察空间 ->Projection Matrix(投影矩阵)->裁剪空间

裁剪空间 ->ViewPort Transform(视口变换)>屏幕空间

二、变换矩阵及计算

1. 模型矩阵Model Matrix

  • 方法: Matrix.rotateM(), Matrix.translateM(), Matrix.scaleM()
  • 公式:
  • 位移、缩放计算过程
bash 复制代码
[ 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             ]
  • 旋转计算过程
bash 复制代码
[ 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:
bash 复制代码
normalize(v) = v / |v|
  • 其中|v|是向量v的长度(模):
bash 复制代码
|v| = √(v.x² + v.y² + v.z²)
  • 向量叉积cross product:
bash 复制代码
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:
bash 复制代码
dot(a, b) = a.x·b.x + a.y·b.y + a.z·b.z
  • 计算过程:
  • 计算前方向向量: Front = normalize(Center - Eye)
bash 复制代码
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))
bash 复制代码
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)
bash 复制代码
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已经是单位向量,因为RightFront都是单位向量且相互垂直)

公式:

bash 复制代码
[ 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                          ]

其中:

bash 复制代码
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
    nearfar通常为正值,但某些实现中可以为负值
    坐标范围取决于场景大小,常见如(-10,10,-10,10,1,100)
  • 公式:
bash 复制代码
[ 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,取决于场景大小
  • 公式:
bash 复制代码
[ 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 ]
  • 结果:
bash 复制代码
[ 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()
  • 计算流程:
java 复制代码
// 模型视图矩阵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
相关推荐
友人.2276 分钟前
Android 底部导航栏 (BottomNavigationView) 制作教程
android
努力学习的小廉33 分钟前
初识MYSQL —— 事务
android·mysql·adb
阿里云云原生1 小时前
深度解析 Android 崩溃捕获原理及从崩溃到归因的闭环实践
android
.豆鲨包1 小时前
【Android】Android内存缓存LruCache与DiskLruCache的使用及实现原理
android·java·缓存
JulyYu2 小时前
【Android】针对非SDK接口的限制解决方案
android·客户端
猪哥帅过吴彦祖2 小时前
Flutter 系列教程:应用导航 - Navigator 1.0 与命名路由
android·flutter·ios
2501_916008893 小时前
iOS 跨平台开发实战指南,从框架选择到开心上架(Appuploader)跨系统免 Mac 发布全流程解析
android·macos·ios·小程序·uni-app·iphone·webview
stevenzqzq4 小时前
Android Hilt教程_构造函数
android
鹏多多4 小时前
flutter图片选择库multi_image_picker_plus和image_picker的对比和使用解析
android·flutter·ios