昨天弄Camera,本来还以为不难,谁知道却被这个问题困扰了一个晚上。不知怎么地,早上突然一个猜想,然后测试,通过!因此写下本文,供大家一起学习交流。本文所用图片都是本人亲手PS的,所以转载文章、图片请注明出处。谢谢!
想自定义jMonkeyEngine中的Camera一般需要做2件事:
1、 设置摄像机视锥(ViewFrustum)的大小
2、 设置摄像机的位置和朝向
设置视锥(ViewFrustum)一般有以下2个方法:
setFrustum(float near, float far, float left, float right, float top, float bottom)
float near : 最近那个面(屏幕)离摄像机位置(你眼睛)的距离。
float far : 相当于摄像机最远那个面和你眼睛的距离。
float left : 最近那个面向左偏离你眼睛的值。
float right : 最近那个面向右偏离你眼睛的值。
float top : 最近那个面向上偏离你眼睛的值。
float bottom : 最近那个面向下偏离你眼睛的值。
该方法比较直观,也就是当你设置了一个锥体的2个截面和你距离,以及第一个截面的大小。那么你就能确定这个锥体(如下图,省略了right和bottom,原理和top、left一样)。
另一种设置视锥的方法是下面这种:
setFrustumPerspective(float fovY, float aspect, float near, float far)
float fovY : 如果一个摄像机固定,一个物体从被正视到往上移动直到看不到的角度。
float aspect : 屏幕的宽高比。
float near : 最近那个面(屏幕)离摄像机位置(你眼睛)的距离。
float far : 相当于摄像机最远那个面和你眼睛的距离。
这个方法也比较容易理解,看下面的图:
一般float fovY如果你不知道设置什么好,就设置45f,该值应该在0-90之间。你想想,如果你设置了91,那么表示摄像机上下的角度加起来将超过182,也就是你能看到自己后面的东西?而如果设置太小将形成隧道效应。
总体来说,设置视锥还是比较容易。接下来是设置位置和朝向。这很重要,决定了你能不能看到东西,就看这一步了。
设置帧(setFrame)
1、 通过setLocation(Vector3f loc)和lookAt(Vector3f pos, Vector3f worldUp)组合设置
2、 通过setFrame(Vector3f loc, Quaternion axes)
3、 通过setFrame(Vector3f loc, Vector3f left, Vector3f up, Vector3f dir)
第一种方法比较简单。大家可以试一下,就是设置Camera的位置和它看向的方向。这个我就不说了。
第二种方法我还没试过。当应该和第三种方法原理一样。
我现在就讲讲第三种,正是这种方法困扰了我一天。
先看下面这个图:
setFrame(Vector3f loc, Vector3f left, Vector3f up, Vector3f dir)
其中 loc为摄像机的位置,即视锥的顶点的在世界中的位置。而left和up则定义了camera自己的坐标系。dir为camera指向的方向。
//想在图中位置正视box,如下代码
Vector3f loc = new Vector3f(0,0,100);
Vector3f left = new Vector3f(-1,0,0);
Vector3f up = new Vector3f(0,1,0);
Vector3f dir = new Vector3f(0,0,-1);
cam.setFrame(loc, left, up, dir);
up定义了camera的上轴,由于现在该轴和世界坐标系的Y轴一样,因此为(0,1,0),由于我们想让camera在Z轴正向往Z轴负向看。因此dir应该为(0,0,-1),而由于jME中使用右手坐标系统,因此X轴此时就只能是往左。即(-1,0,0)。Z轴可以看成是视锥顶点到各个截面中心的连线所指的方向,这个很重要记住!。
注意:很多人(包括我)一开始可能会想到将Camera移动到X轴正向,然后设置dir来从右边观察位于原点的Box。如下代码:
Vector3f loc = new Vector3f(100,0,0);
Vector3f left = new Vector3f(-1,0,0);
Vector3f up = new Vector3f(0,1,0);
Vector3f dir = new Vector3f(-1,0,0);
cam.setFrame(loc, left, up, dir);
你会发现这样根本看不到box。
因为camera的坐标轴并没有改变,改变的只是dir。在坐标轴不变的情况下改变dir相当于将camera进行变形操作。而当dir和x轴或y轴为同一方向时,整个视锥相当于被压扁。它此时近似已经没有可视范围了,因此不管怎样都看不到东西了。
你还可以试试:
//想从Y轴往下看,也看不到Camera
Vector3f loc = new Vector3f(0, 100,0);
Vector3f left = new Vector3f(-1,0,0);
Vector3f up = new Vector3f(0,1,0);
Vector3f dir = new Vector3f(0, -1,0);
cam.setFrame(loc, left, up, dir);
那么怎样才能在Vector3f loc = new Vector3f(100,0,0);的情况下从右侧观察box能。很简单。确定Y轴,确定dir,让它指向世界坐标X轴负向,最后确定Camera的X轴(由右手坐标规则)。
试试:
Vector3f loc = new Vector3f(100,0, 0);
Vector3f left = new Vector3f(0,0,1);
Vector3f up = new Vector3f(0,1,0);
Vector3f dir = new Vector3f(-1,0, 0);
cam.setFrame(loc, left, up, dir);
OK了吧。
不知道怎样会不会让你更好理解一点:想要确定摄像机的朝向,那么先确定它的dir,然后确定up(因为一般都是(0,1,0)),然后把dir看成是Z轴,UP则是Y轴,这样就可以确定X轴,就是left。
注意:上面的(0,0,1)和(0,0,4)指向的是同一个方向,但是使用(0,0,4)你会发现box变形了。在这里,left、up、dir的数值除了用于计算方向,还代表单位向量的长度。因此,需要计算好。