计算机图形学:在世界上移动



我已经实现了什么

我已经实现了一个应用程序,它能够在屏幕上呈现多边形列表,并具有特定的静态相机设置(位置、视线和向上矢量),所有这些都是在没有OpenGL的纯Java AWT中实现的。

我首先应用模型视图矩阵,然后应用投影到2D矩阵,然后再应用视口矩阵。

我还实现了一些关于世界的基本变换矩阵,例如平移、X/Y/Z轴围绕lookAt点的旋转和围绕lookAt点的缩放。

我现在想要实现的目标

我希望能够在这个世界上"移动"。具体来说,我想用键盘箭头向前、向后、向左和向右导航,并能够用鼠标查看不同的点。就像在真实的游戏中一样。

我想这是通过每次更改相机参数并再次渲染世界来完成的。

就这么简单吗?

在世界上移动只是将x,y值添加到相机位置并观察一个点?

此外,移动鼠标是否只是将x、y添加到某个点上?

在任何情况下,我都必须触摸上矢量吗?

有信息性的答案,加上其他相关链接,也会很有帮助。

您可以通过矩阵的结构化管理来实现这种行为。尤其是视图矩阵。在大多数情况下,向x/y位置添加某些内容是不正确的,因为相机可能会旋转。

让我们用V来表示当前视图矩阵。如果你想移动相机,你必须计算一个新的视图变换:

V := Translate(-x, -y, -z) * V

,其中Translate(a, b, c)是平移矩阵。CCD_ 3表示在左/右方向上的移动。-y表示上行/下行。CCD_ 5表示向前/向后。

如果你想旋转相机,这可以类似地完成:

V := Rotate(-angle, axis) * V

,其中Rotate(-angle, axis)是旋转矩阵。绕x轴旋转可向上/向下查看,绕y轴旋转可向左/向右查看。通常不需要绕z轴旋转,因为它会给相机带来滚动。

大多数参数被否定的原因是视图变换是逆变换。也就是说,他们不定位相机对象,但他们重新定位世界,就好像相机在原点一样。

首先来看一下理解4x4齐次变换矩阵

  1. 提取相机轴向量

    如果你看链接答案中的第一张图片,你会发现向量存储在哪里。注意,如果你得到了转置布局,那么向量也会被转置。

  2. 移动

    移动很容易,只需add/sub方向向量到相机矩阵的原点。例如,我的相机将Z-作为前进方向。因此,我从矩阵中取Z-axis向量(在我的情况下,它已经是单位),将其与速度和时间(运动代码运行的计时器间隔)相乘。下面是我的移动代码通常看起来的示例:

    //---------------------------------------------------------------------------
    void __fastcall Twin_main::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift)
    {
    // Keyboard event called on any key press
    keys.set(Key,Shift); // set key in my keymap as pressed
    _redraw=true; // key press means something in scene might change so redraw
    }
    //---------------------------------------------------------------------------
    void __fastcall Twin_main::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift)
    {
    // Keyboard event called on any key release
    keys.rst(Key,Shift); // set key in my keymap as not pressed
    }
    //---------------------------------------------------------------------------
    void __fastcall Twin_main::tim_updateTimer(TObject *Sender)
    {
    // Timer runing every tim_update->Interval [ms]
    double   dt=double(tim_update->Interval)/1000.0;
    // set speeds and transitions between view modes
    double   alfa=2.0*deg;       // angular speed
    double   v=100000.0/3.6;     // movement speed [km/hod] -> [m/s]
    // precision control
    if (keys.Shift.Contains(ssAlt )) { v*= 0.1; alfa*= 0.1; }
    if (keys.Shift.Contains(ssCtrl)) { v*=10.0; alfa*=10.0; }
    // rotations (local to camera space)
    if (keys.get(104)) { _redraw=true; eye.rep.lroty(+alfa); }                              // num8
    if (keys.get(105)) { _redraw=true; eye.rep.lroty(-alfa); }                              // num9
    if (keys.get(100)) { _redraw=true; eye.rep.lrotx(+alfa); }                              // num4
    if (keys.get( 97)) { _redraw=true; eye.rep.lrotx(-alfa); }                              // num1
    if (keys.get(111)) { _redraw=true; eye.rep.lrotz(+alfa); }                              // num/
    if (keys.get(106)) { _redraw=true; eye.rep.lrotz(-alfa); }                              // num*
    if (keys.get( 37)) { _redraw=true; eye.rep.lroty(+alfa); }                              // left
    if (keys.get( 39)) { _redraw=true; eye.rep.lroty(-alfa); }                              // right
    // movements
    if (keys.get( 96)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,-v*dt)); }        // num0
    if (keys.get(110)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,+v*dt)); }        // num.
    if (keys.get( 98)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,-v*dt,0.0)); }        // num2
    if (keys.get(101)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,+v*dt,0.0)); }        // num5
    if (keys.get(102)) { _redraw=true; eye.rep.lpos_set(vector_ld(-v*dt,0.0,0.0)); }        // num6
    if (keys.get( 99)) { _redraw=true; eye.rep.lpos_set(vector_ld(+v*dt,0.0,0.0)); }        // num3
    if (keys.get( 38)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,-v*dt)); }        // up
    if (keys.get( 40)) { _redraw=true; eye.rep.lpos_set(vector_ld(0.0,0.0,+v*dt)); }        // down
    keys.rfskey(); // just do some stuff for advanced keyboard things not used here
    if (_redraw) { draw(); }
    }
    //---------------------------------------------------------------------------
    

    keys是我的类,它为每一个16位键代码都持有单位bool,允许我一次处理多个按键。eye.rep是我的类,它同时持有直接变换矩阵和逆变换矩阵。其成员lpos_set采用3D矢量将其从局部坐标转换为全局坐标,并将其设置为矩阵原点。它与我提到的上述过程具有相同的效果。deg只是弧度为1度的常数,lrot?是局部旋转,见下一个项目符号。

  3. 旋转

    我不使用欧拉角,因为我讨厌它们。他们遇到了故障,需要以不同的方式处理电线杆,而且经常会引发问题。你有没有玩过游戏,相机的角度旋转被卡住了,不允许你旋转到你想要的地方?甚至反过来?然后是由于相机使用的欧拉角度。

    我使用局部旋转lrot?(参见链接的答案它们是如何工作的)。它们没有边界或故障。你唯一需要记住的就是准确性。若你们多次旋转矩阵,你们就失去了精度。所以每隔几次旋转就保证了它的正交性或正交性。为此,您只需要使用叉积使轴再次垂直,并将其长度设置为1或您使用的任何值。我在矩阵类中有一个计数器,每次更改都会递增,当它达到一个三重态时,正交性就会恢复。

正如你所看到的,我使用数字键盘进行移动,箭头用于向左/向右和向前/向后。现在大多数人都使用WSAD,但这对我来说非常不舒服。代码取自我的一个VCL应用程序,所以你需要更改事件以匹配你的。

您还可以添加鼠标操纵器来转动。。。如果您需要操纵杆,请参阅

  • 如何在C中获取JoyStick Z旋转消息++

但我怀疑它是否像适用于Windows那样适用于JAVA。

相关内容

  • 没有找到相关文章

最新更新