Inside out: Camera
丁欧南
Keyword:[OpenGL Camera][FPS 场景][第一人称射击场景]
我是一名OpenGL初学者,刚刚弄懂Camera的实作方法,愿与初学者共享,与高手探讨.如需要此文完整代码,或要转载,请发邮件给我.
A.约定
1. sin cos 等的引数全用弧度制
2. 使用OpenGL
右手定则
3. 矩阵(Matrix)书写规范:
平移(Translation)部分位于最后一行,你可以不加修改地把矩阵写入自己的OpenGL 代码
例:
[A11 A12 A13 A14]
[A21 A22 A23 A24]
[A31 A32 A33 A34]
[A41 A42 A43 A44]
float matrix[]={A11,A12,A13,A14,A21,A22,A23,A24,A31,A32,A33,A34,A41,A42,A43,A44};
4. *:点积(Dot
Product) ×:叉积(Cross
Product)
B.概念
大家都知道gluLookAt,它有三类(每类3个)共9个引数:
void gluLookAt(GLdouble eyex,GLdouble
eyey,GLdouble eyez,
GLdouble centerx,GLdouble
centery,GLdouble centerz,
GLdouble upx,GLdouble
upy,GLdouble upz);
你需要分别指定这三类引数,才能完成Camera变换.
1.视线:你不用在gluLookAt中指定视线(从眼到被视物体的向量),但在变换视点(尤其是旋转时),所作的许多 操作都是针对它进行的,
后面将给出如何计算视线向量的公式.
a.视点:即gluLookAt中eye*的那三个引数,视点指的是一条从原点到眼位置的向量.
从你的座位上站起来,这时你改变的就是视点.
b.视目标:即gluLookAt中center*的那三个引数,视目标指的是一条从原点到被视物体的向量.
水平转动你的脖子,这时你所作的就是改变视目标.
c.仰视向量:即gluLookAt中up*的那三个引数,仰视向量指的是头顶的朝向.
竖直抬起头(‘向上看’),这时改变的就是仰视向量.
[一般来讲,仰视向量用(0,1,0)就可以了,不用再仔细考虑它]
2.很明显,由向量减法 视线 view,视点 pos,视目标
tar
view=tar-pos;
3.Camera的变换也就是改变视线位置(前进或后退),方向(转头)
4.Camera变换Pipeline:
Ⅰ 由pos,tar
计算出 view
Ⅱ 变换view
Ⅲ 将view的x,y,z分量传回pos和tar
Ⅳ 将pos,tar,up传给gluLookAt
C.平移
平移是最简单的,它起的作用是游戏者的前进与后退,你会发现平移其实就是平行移动视线,方向不变.
如下图:
代码讲解:
void Move(float speed) { //speed:指定每按一下键前进多少,我取的是0.3
Vector view=tar-pos; //取得view,Vector不是std::vector
pos.x+=speed*view.x;
pos.z+=speed*view.z;
tar.x+=speed*view.x;
tar.z+=speed*view.z;
}
//我只希望在平面上移动,所以没有y分量的事.
D.旋转
旋转就是转头操作,比较复杂,一点一点来.
D1.绕任意轴旋转一条向量
Vup⊥Vperp |Vup|=|Vperp|=|Vaux| Vproj⊥Vperp n∥Vproj n为单位向量
绕n旋转V θ度到V’,
求V’
Vproj=|Vproj|/|n| n
n*V=|n||V|cosβ
cosβ=|Vproj|/|V| |Vproj|=|V|cosβ
n*V=|n||Vproj| |Vproj|=(n*V)/|n|
Vproj=(n*V)/|n|2
n 因为
n 为单位向量
所以
Vproj=n*V n
Vperp=V-Vproj=V-n*V
n
Vup=Vperp×n=(V-n*V n)×n=V×n-n*V(n×n)=V×n
Vaux=-sin(θ-90)Vperp+cos(θ-90)Vup=sin(90-θ)Vperp+cos(90-θ)Vup
=cosθVperp+sinθVup=cosθ(V-n*V n)+sinθ(V×n)
V’=Vperp+Vaux=n*v
n+cosθ(V-n*V n)+sinθ(V×n)
D2.水平转头
水平转头即在XZ平面上绕竖直方向旋转view向量
可看出pos无变化,y分量亦无变化.
XZ平面的旋转矩阵:
[ cosθ
0 -sinθ ]
[ 0 1
0 ]
[ sinθ 0
cosθ ]
取得view: view=tar-pos;
旋转view:
[cosθ
0 -sinθ ]
[view.x view.y view.z]
[0 1 0 ]
[sinθ
0 cosθ]
=[view.x*cosθ+view.z*sinθ
view.y -view.x*sinθ+view.z*cosθ]
传回tar值:
tar’=pos+view’;
D3.竖直转头
竖直转头即在YZ平面上以垂直于view的直线为轴旋转view向量
侧视示意图:
鸟瞰示意图:
取得view:
view=tar-pos;
取得
axis: 即旋转view (-π/2)
[0 0 1]
[view.x
view.y view.z] [0 1 0]
[-1 0 0]
=[-view.z view.y
view.x]
为了用上D1中求得的公式,把axis归一化(Normalize)
绕axis旋转view: (套用D1最后得出的公式)
view’=axis*view axis+cosθ(view-axis*view axis)+sinθ(view×axis);
更新tar值:
tar’=pos+view;
E.鼠标滑动角度计算
我们注意到进行旋转Camera时,需要向程序提供转过的角度(2个)
一个是水平转动角度(转头),一个是竖直转动角度(抬头或低头)
这样计算:
鼠标滑过的距离其实是view向量终点滑过的距离
用滑过的距离(s),view向量的长度(l),求得水平角(a),竖直角(b)
a=arc tan s.x/l b=arc tan s.y/l