现在的位置: 首页 > 综合 > 正文

libgdx 引擎camera

2013年04月16日 ⁄ 综合 ⁄ 共 4206字 ⁄ 字号 评论关闭

 原文链接:http://www.badlogicgames.com/wordpress/?p=1550

 

Camera是通过在3D空间中的位置定义的, 方向由指向特定方位的单位向量组成。想象一个箭头从你的头顶指向天空,这个就是指向上的方向向量。同样的,把你的头指向左边和右边。你能想象出方向的改变吗? 方向向量(direction vector)能够让我们告诉OpenGL 如何设置Camera的方位。而位置向量(position vector)则定义了Camera 在3D世界中的位置。
位置和方位只是Camera其中的一部分。Camera 另个一重要的属性就是视锥(view frustum)。在上面的图片中,可以看到一个椎体在视点(图中眼睛的位置)被截去了顶端,这个就是视椎。所有在这个截头锥体(frustum)里面的东西都会被显示在屏幕上。这个截头锥体被6个切面(clipping planes)所定义,分别为:near, far, left, right, top and bottom。在上面的图片中的所看到截头锥体只是整个椎体的一侧。对于近切面它有一个特殊的作用:你可以把它想象成一个Camera
生成图画的表面(surface),这个把3D空间转换为2D平面过程,称为投影(projection)。

正交投影(Orthographic projection) 通常被用于2D图形。不管物体距离Camera有多远,在屏幕都会被显示成一样的大小。
而透视投影(Perspective projection)是真实世界中的投影:距离我们眼睛越远的物体,看起来会越小。
这种有趣的现象,在我们Camera属性中,其实就是改变视椎的形状。在透视投影中,视椎就像上面图片中的一样;而在正交投影中,视锥其实是一个盒子。真正的投影过程其实非常简单:对于物体上的每个点,我们画一条线连接至Camera的位置,并计算出哪里触碰到视锥的近切面。下面的图片展示了两种投影的原理:
                            透视投影                                                                正交投影
(绿色的部分就是看到的投影后的物体)

你可以看出在透视投影中,物体在投影后缩小了,而在正交投影中物体的尺寸并没有改变,你知道这是为什么吗? 没错,造成这种差异的就是视锥的形状!在OpenGL里不管你用的是SpriteBatch还是绘制一些字体,你永远都是在3D的世界中。技巧就在于,当你使用正交相机(orthographic camera)时,你就假装Z-axis并不存在。下面是一个在3D空间中的2DSprite:

 

  

所以请忘记2D 与 3D 的区别吧。其实它们没有区别。当你通过SpirteBatch绘制图形,并且没有设置任何的矩阵(matrices)时,那么你的2D 视锥就像下面的图片所示的那样:

 

 

就是一个简单的盒子!我们所有的Sprite都忽略了z-axis,只在X/Y平面上移动,从而产生好像在2D空间中的错觉。
透视相机(perspective camera)使用两个属性来定义自身的投影: 视野(the field of view) 和 宽高比(aspect ratio)。
视野其实是一个定义 视锥应该开多大的角度,如下右图:
而宽高比就是视窗(viewport)的宽度和高度的比值。 视窗就是Camera渲染图像的矩形区域(如上左图)。所以如果的屏幕分辨率为480*320 pixels,那么你的宽高比就是 480/320。

 

对于正交相机(orthographic camera)的投影仅仅就是由视窗的尺寸所决定,就像之前的图片所表示那样(看上去像个盒子那张)。
接下来让我们看看 Camera类是怎么实现的。这里一共有三个类: Camera , OrthographicCamera ,PerspectiveCamera 。其中,后面两个类都是继承自Camera的子类,共享Camera类的属性和方法。先看一下Camera 类:
public abstract class Camera {      
public final Vector3 position = new Vector3(); 
 public final Vector3 direction = new Vector3(0, 0, -1);  
 public final Vector3 up = new Vector3(0, 1, 0);
首先三个共有的成员变量分别为: position, direction, up 。 它们定义了Camera的默认状态;:位置在原点(0,0,0) ,垂直于(X,y)平面,指向向 z 轴的负方向。当然你可以修改成你想要的值。
public final Matrix4 projection = new Matrix4();
public final Matrix4 view = new Matrix4(); 
public final Matrix4 combined = new Matrix4(); 
public final Matrix4 invProjectionView = new Matrix4();
接下来是一系列的矩阵。这些矩阵只有当你是有OpenGL ES 2.0的时候,你才会对其感兴趣。第一和第二个矩阵分别保存了投影矩阵(projection matirx) 和 视图模型矩阵(model-view matrix) 。第三个则保存了 它们两个的阶乘矩阵,而第四个则是阶乘矩阵的逆矩阵。正如我所有说的,你并不喜欢直接接触它们。
public float near = 1;
public float far = 100;
public float viewportWidth = 0; 
public float viewportHeight = 0;
接着是对于Camera的近切面和远切面距离大小的定义,以及视窗的宽度 和高度。近切面和远切面必须保持在 0 <= near < far 的范围。本来是应该为它们提供setter方法,不过我决定直接暴露它们。默认情况下, 近切面是距离相机的位置一个单位。而对于正交相机的近切面距离你会经常设置成接近0(当你在构造正交相机时,它已经自动这么做了)。 视窗的宽度和高度被用作计算 投影相机的宽高比 以及定义 正交相机的 盒子视锥。
public final Frustum frustum = new Frustum();
最后一个成员变量是Frustum。它组合6个切面形成Camera的视锥。 这个Frustum被用作挑选的功能: 它检测一个物体是否在视锥里面。为了避免你不会使用它,Frustum 用于一系列的方法,你可以通过这些方法定义自己Frustum,具体请参考javadocs。
public abstract void update(); 
public void apply(GL10 gl);
接下来是一对非常完美的方法。 updata()方法将会 重新计算Camera的一系列矩阵。不管你改变了Camera的什么属性,像是位置或者近/远切面等,你都应该在随后调用这个方法。 apply()方法会根据当前Camera的矩阵来设置 GL_PROJECTION 矩阵 和 GL_MODELVIEW 矩阵。当然这在 OpenGL ES 2.0里是不起作用的。
public void lookAt(float x, float y, float z);
public void rotate(float angle, float axisX, float axisY, float axisZ);
public void translate(float x, float y, float z);
接着是一系列的方法,使用它们可以将Camera指向空间中的一点,或是饶某个轴旋转一点的角度,或是移动一段距离。这些只是一些帮助型方法,你也可以通过直接操作 position/direction/up 向量来达到同样的效果。
public void unproject(Vector3 vec); 
 public void project(Vector3 vec);
 public Ray getPickRay(float x, float y);}
最后我们有一些方法供你实现一些比较高级的需求。 unproject()方法将一个窗口坐标(屏幕坐标)的点转换成一个3D 的点。对于X,Y轴的坐标是可以被触碰的,而在你传递进去的参数中,Z轴的坐标应该在 0 至 1 之间。 0表示在近切面生成一个点,而1表示在远切面生成一个点。
project()方法做到是相反的事情:它将一个3D 的点 转换成屏幕上的一个 2D 点。
getPikcRay()方法将返回一束采集射线(a Ray for ray picking)。把它想象成在3D世界中的一根棍子(stick),起始与你Camera的位置。你会经常传递触碰坐标,然后根据这个射线和组件类来判断,它是不是集中了某个几何形状或者物体。

至此,Camera类就已经介绍完了。下面让我们看一下OrthograhpicCamera:
public class OrthographicCamera extends Camera { 
 public float zoom = 1;   
 public OrthographicCamera(float viewportWidth, float viewportHeight);
没错,就是这么简单。它只是额外多一个名为zoom的成员变量。同时,构造函数需要宽度和高度初始化Camera。如果你想得完美的显示效果,这里可以通过调用 GRaphics.getWidth()/getHeight()来设置宽度和高度。 如果你想和Box2D一起使用,那么需要使用一个不同的缩放单位,名为meters(e.g 42,32)。 当然,你也可以在3D空间中使用它。

同样的PerspectiveCamera也非常的简单:
public class PerspectiveCamera extends Camera {   
 public float fieldOfView = 67;  
 public PerspectiveCamera(float fieldOfView, float viewportWidth, float viewportHeight);
它也只是多了一个成员变量用以定义视野(field of view)。 而宽高比则是通过Viewport的宽度和高度自动计算(在构造方法中已经传入)。

抱歉!评论已关闭.