【问题标题】:Computer Graphics: Moving in the world计算机图形学:移动世界
【发布时间】:2016-05-26 21:37:08
【问题描述】:

我已经实现的内容

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

我首先应用模型视图矩阵,然后是投影到二维矩阵,然后是视口矩阵。

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

我现在想要实现的目标

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

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

就这么简单吗?

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

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

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

信息丰富的答案以及其他相关链接也会很有帮助。

【问题讨论】:

  • 通常你只是围绕你的位置移动世界。我见过的几乎所有 opengl 教程都讨论了这个话题。无论语言如何,该方法都是相似的。
  • 如果您使用的是lookAt,那么“移动”会同时改变相机位置和注视点(当然,除非您想一直注视同一个点)。转身以一种有点复杂的方式改变了观察点。您可能不想使用 lookAt 进行转向。
  • 真实游戏最有可能使用平移和旋转操作来计算视图矩阵。不看。 (但这取决于游戏,我假设这里是 FPS,但如果你想到神话时代或塞尔达之类的东西,它很可能会使用lookAt)
  • 所以你想告诉我移动世界更可取吗?在移动和转身中,我只移动世界?

标签: java math graphics 3d


【解决方案1】:

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

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

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

,其中Translate(a, b, c) 是一个平移矩阵。 -x 代表左/右方向的移动。 -y 代表上/下。 -z 代表前进/后退。

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

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

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

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

【讨论】:

  • 太棒了。如何计算旋转角度?
  • 使用常量(按帧时间缩放)。
  • 什么?这怎么可能是一个常数?这取决于用户滚动相机的角度...
  • 旋转速度。通常,您使用恒定的旋转速度。
  • 下面问题中的第 9 条评论,我解释了我不完全理解的内容,除了速度/时间方面
【解决方案2】:

先看看Understanding 4x4 homogenous transform matrices

  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 位键码中的每一个都持有一位布尔值,允许我一次处理多个按下的键。 eye.rep 是我的班级,同时拥有直接和逆变换矩阵。它的成员lpos_set 采用一个 3D 矢量将其从局部坐标转换为全局坐标,并将其设置为矩阵原点。它与我提到的上述过程具有相同的效果。 deg 只是恒定的 1 度弧度,lrot? 是局部旋转,请参阅下一个项目符号。

  3. 轮换

    我不使用欧拉角,因为我讨厌它们。他们遇到了需要以不同方式处理电线杆的故障,并且经常引起问题。您是否玩过任何游戏并且相机角度旋转卡住了,无法旋转到您想要的位置?甚至逆转?然后是由于相机使用的欧拉角。

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

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

您还可以添加鼠标处理程序以进行转动...如果您需要操纵杆,请参阅

但我怀疑它是否适用于 JAVA,因为它适用于 Windows。

【讨论】:

  • 感谢您的详细回答,但我很难从中获得任何有用的信息,因为我有该领域的基本知识。附加的答案很长,但我也读过,但没有人给我简单的答案,告诉我一旦我能够通过对象的基本变换静态渲染它,如何在世界中实现运动。
  • 这里总结了两个子问题:如何实现移动和如何实现视图的旋转。我必须以简单的动作移动世界或相机吗?在旋转中,我是旋转世界还是旋转相机,以及如何计算角度?
  • @Jjang 我移动相机..(eye 是我的相机矩阵)移动世界没有任何意义,因为这意味着你必须移动其中的所有对象,这太疯狂了。您可以使用lookat 作为相机的起点(在初始化时仅调用一次),然后在移动时整合它们。您可以将运动和转弯视为增量步骤。
  • @Jjang 我得到了我世界中每个对象的obj 矩阵和相机视图的单个eye 矩阵。它们都只初始化一次。在那之后,他们都在及时整合他们的动作。我没有任何世界矩阵见stackoverflow.com/a/21100338/2521214(世界坐标系是常数单位矩阵)。您还可以通过简单地在其后面本地平移对象矩阵并反转结果矩阵来制作跟随相机。
  • 你能看看上面的答案吗?我将如何计算需要旋转世界的角度?到目前为止,我知道我需要实际平移/旋转世界,以及如何应用平移矩阵,但我一直在计算旋转矩阵的角度。
猜你喜欢
  • 2017-07-07
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 2014-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-03-27
相关资源
最近更新 更多