Inside out:Camera

Inside out: Camera
丁欧南

Keyword:[OpenGL Camera][FPS 场景][第一人称射击场景]
我是一名OpenGL初学者,刚刚弄懂Camera的实作方法,愿与初学者共享,与高手探讨.如需要此文完整代码,请发邮件给我.
A. 约定
  1. sin cos 等的引数全用弧度制

  2. 使用OpenGL 右手定则

  3. 矩阵(Matrix)书写规范:
     平移(Translation)部分位于最后一行,你可以不加修改地把矩阵写入自己的OpenGL 代码
    
     例:
     [A 11  A 12  A 13  A 14
     [A 21  A 22  A 23  A 24]  
     [A 31  A 32  A 33  A 34]
     [A 41  A 42  A 43  A 44]

     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. 绕任意轴旋转一条向量
                            


    V up⊥V perp  |V up|=|V perp|=|V aux|  V proj⊥V perp  n∥V proj  n为单位向量

     绕n旋转V θ度到V’, 求V’

    
     V proj=|V proj|/|n| n

     n*V=|n||V|cosβ     cosβ=|Vproj|/|V|   |Vproj|=|V|cosβ

     n*V=|n||V proj|         |V proj|=(n*V)/|n|

     Vproj=(n*V)/|n|2 n       因为 n 为单位向量

     所以 Vproj=n*V n

    

     Vperp=V-Vproj=V-n*V n

     V up=V perp×n=(V-n*V n)×n=V×n-n*V(n×n)=V×n
    
     V aux=-sin(θ-90)V perp+cos(θ-90)V up=sin(90-θ)V perp+cos(90-θ)V up
        =cosθV perp+sinθV up=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
 


 
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值