声明:
本系列文章使用的Libgdx版本均为0.99版本
Libgdx游戏开发交流群 323876830
在Opengl中,有许多矩阵变换,可以分为视角(Viewing),模型(Modeling)和投影(Projection)操作,这些操作可以有选择,平移,缩放,正侧投影,透视投影等。
如果我们使用照相机拍照的过程做类比,可以更好的理解3D 坐标变换的过程。
拍照时第一步是架起三角架并把相机的镜头指向需要拍摄的场景,对应到3D 变换为viewing transformation (平移或是选择Camera )然后摄影师可能需要调整被拍场景中某个物体的角度,位置,比如摄影师给架好三角架后给你拍照时,可以要让你调整站立姿势或是位置。对应到3D绘制就是Modeling transformation (调整所绘模型的位置,角度或是缩放比例)。之后摄影师可以需要调整镜头取景(拉近或是拍摄远景),相机取景框所能拍摄的场景会随镜头的伸缩而变换,对应到3D绘图则为Projection transformation(裁剪投影场景)。按下快门后,对于数码相机可以直接在屏幕上显示当前拍摄的照片,一般可以充满整个屏幕(相当于将坐标做规范化处理NDC),此时你可以使用缩放放大功能显示照片的部分。对应到3D绘图相当于viewport transformation (可以对最终的图像缩放显示等)下图为Android OpenGL ES坐标变换的过程:
Object Coordinate System: 也称作Local coordinate System,用来定义一个模型本身的坐标系。World Coordinate System: 3d 虚拟世界中的绝对坐标系,定义好这个坐标系的原点就可以用来描述模型的实现的位置,Camera 的位置,光源的位置。View Coordinate System: 一般使用用来计算光照效果。Clip Coordinate System: 对3D场景使用投影变换裁剪视锥。Normalized device coordinate System (NDC): 规范后坐标系。Windows Coordinate System: 最后屏幕显示的2D坐标系统,一般原点定义在屏幕左上角。
好了,上面只是对opengl三维变换知识的一个普及。 下面我们将要针对Libgdx中怎么处理这些东西的做一下分析。
Viewing和Modeling(MODELVIEW) 变换
为什么把这两个放在一起呢?我们可以这样想想, 当我们拉近镜头(物体不动)的时候,和物体靠近镜头(镜头不动),
或者镜头视角向上偏移(物体不动)和物体视角相对镜头向下偏移, 效果是一样的。 如下图:
view变换在Camera中给我们提供了所需字段 镜头位置,镜头指向目标位置的方向,镜头观测点方向为“上”的向量
/** the position of the camera **/
public final Vector3 position = new Vector3();
/** the unit length direction vector of the camera **/
public final Vector3 direction = new Vector3(0, 0, -1);
/** the unit length up vector of the camera **/
public final Vector3 up = new Vector3(0, 1, 0);
变换的结果存储在view矩阵中
/** the view matrix **/
public final Matrix4 view = new Matrix4();
model的变换的操作其实都已经移交到view变换中了, 所以就没有单独计算了。
投影变换Projection
在Opengl中的两种投影变换对应于Camera中的两个子类OrthographicCamera和PerspectiveCamera,分别是正投影和
透视投影。
先看看透视投影,如下图
粉色区域就是我们要得到的区域,又叫视锥。需要的数据也就有 视锥的view angle,视锥的宽高比,裁剪面的近距离,创建面的远距离
在PerspectiveCamera透视投射镜头中,我们也可以找到这些我们所需要的数据。
/** the field of view in degrees **/
public float fieldOfView = 67;
/** the near clipping plane distance, has to be positive **/
public float near = 1;
/** the far clipping plane distance, has to be positive **/
public float far = 100;
组装投射矩阵数据
float aspect = viewportWidth / viewportHeight;
projection.setToProjection(Math.abs(near), Math.abs(far), fieldOfView, aspect);
下图为正投影镜头
它的视锥为一长方体,特点是物体的大小不随到观测点的距离而变化,投影后可以保持物体之间的距离和夹角。
在OrthographicCamera中表示
this.near = 0;
projection.setToOrtho(zoom * -viewportWidth / 2, zoom * (viewportWidth / 2), zoom * -(viewportHeight / 2), zoom
* viewportHeight / 2, near, far);
在绘制的时候,我们可以使用Camera的apply方法,应用矩阵
public void apply (GL10 gl) {
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadMatrixf(projection.val, 0);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadMatrixf(view.val, 0);
}
这里要多提一下SpriteBatch中使用镜头的方式
GL10 gl = Gdx.gl10;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadMatrixf(projectionMatrix.val, 0);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadMatrixf(transformMatrix.val, 0);
大家有没有发现有点诡异?
在投射模式下使用的是projectionMatrix投影矩阵,乍一看是没什么问题, 但是这个projectionMatrix是由
投影矩阵和view变换矩阵的组合矩阵
combined.set(projection);
Matrix4.mul(combined.val, view.val);
projectionMatrix其实就是combined
而transformMatrix这个也不是我们上面的view变换矩阵,而是一个空矩阵,可以让我们来set进去的。
好了,矩阵变换就到这里了, 有什么问题大家可以多多指教。