3D地球旋转问题

时间:2009-11-03 05:18:45

标签: wpf 3d rotation transformation pixelsense

当用户将鼠标/手指移到球体上时,我试图让我的3D球体旋转。

我可以让它旋转没有问题,但是当我尝试使用Surface SDK中的Affine2DInertiaProcessor向球体添加惯性时,当我快速轻弹球体时,我会遇到跳跃问题,我不知道为什么... < / p>

这是我的初始化代码:

    private void InitializeManipulationProcessor()
    {
        manipulationProcessor = new Affine2DManipulationProcessor(
            Affine2DManipulations.Rotate | 
            Affine2DManipulations.TranslateX | 
            Affine2DManipulations.TranslateY,
            _eventSource);


        inertiaProcessor = new Affine2DInertiaProcessor();
        inertiaProcessor.Affine2DInertiaDelta += Inertia_OnManipulationDelta;
        inertiaProcessor.Affine2DInertiaCompleted += InertiaProcessor_Affine2DInertiaCompleted;

        manipulationProcessor.Affine2DManipulationStarted += OnManipulationStarted;
        manipulationProcessor.Affine2DManipulationDelta += Manipulation_OnManipulationDelta;
        manipulationProcessor.Affine2DManipulationCompleted += OnManipulationCompleted;
}

当用户移动他们的手指时,这是旋转球体的代码:

private void Manipulation_OnManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
    {
        Point currentPosition = e.ManipulationOrigin;
        // avoid any zero axis conditions
        if (currentPosition == _previousPosition2D)
            return;

        Track(currentPosition);

        _previousPosition2D = currentPosition;
    }

当用户停止移动手指时,这会启动ineria:

private void OnManipulationCompleted(object sender, Affine2DOperationCompletedEventArgs e)
{
    inertiaProcessor.InitialOrigin = e.ManipulationOrigin;
    inertiaProcessor.InitialVelocity = e.Velocity;
    inertiaProcessor.DesiredDeceleration = 0.0001;
    inertiaProcessor.Begin();
}

旋转的魔力发生在下面的跟踪方法中:

    private void Track(Point currentPosition)
    {
        Vector3D currentPosition3D = ProjectToTrackball(currentPosition);

        Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
        double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D);

        // quaterion will throw if this happens - sometimes we can get 3D positions that
        // are very similar, so we avoid the throw by doing this check and just ignoring
        // the event 
        if (axis.Length == 0)
            return;

        Quaternion delta = new Quaternion(axis, -angle);

        // Get the current orientantion from the RotateTransform3D
        Quaternion q = new Quaternion(_rotation.Axis, _rotation.Angle);

        // Compose the delta with the previous orientation
        q *= delta;

        // Write the new orientation back to the Rotation3D
        _rotation.Axis = q.Axis;
        _rotation.Angle = q.Angle;

        _previousPosition3D = currentPosition3D;
    }

_rotation var是用于3d网格上 RotateTransform3D AxisAngleRotation3D 类。

我知道这是一个特例,但我觉得这是一个计算问题,我真的不知道如何调试它。

还有一件事,一件非常有趣的事情是,如果我慢慢地挥动地球,我就不会跳跃,而且非常顺畅!所以它必须与大型计算有关,或者只是某些错误......

如果您擅长3D旋转并且真的相信您可以提供帮助,那么我很乐意将此项目打包成ZIP并在需要更好的格式时将其发送给您

感谢您提供任何帮助,我非常感谢您的帮助!

标记

2 个答案:

答案 0 :(得分:1)

我没有一个明确的答案,但看着你的代码位,很多事情对我来说都很奇怪。

首先,ProjectToTrackball究竟做了什么? 由于你使用2D惯性,我假设它将2D点(在屏幕空间中)投影到球体上并返回3D点,对吗?那么当2D点位于屏幕上的球体之外时究竟会发生什么?归还了什么?当你用手指在球体上开始移动并且惯性2D使得移动离开球体时会发生什么?

现在关于在Track方法中处理旋转的方式。我不太了解四元数,但我所知道的是,如果你想模拟3D旋转,你需要三个轴和三个角(欧拉角)。在每一步中,只用一个轴和一个角度覆盖四元数。如果您只在一个方向上移动手指,则此方法有效。如果您在移动过程中更改方向,则无法使用此功能。

在旁注上我不明白为什么你在delta四元数中直接使用“-angle”而不是“angle”,但我想如果那里有一个bug,你会立即注意到它;)

编辑:我下载了你的.rar。

我查看了ProjectToTrackball的工作原理(在SurfaceTrackballDecorator.cs中),现在对所发生的事情有了一个很好的了解。

首先,你的球体应该与整个屏幕相匹配(这意味着它应该触摸屏幕的四边,即使你的屏幕不是正方形),否则运动将不会正常运行。如果没有,你应该能够在球体和屏幕边缘之间的空间中旋转球体,这不是我想要的效果。

然后当惯性完成其工作时会发生的变化是运动将继续 2维,而不是3D,就好像你的手指一直在移动(慢慢减速)。

当运动撞击球体边缘时,将2D点投射到3D球体上的魔力可以使球体快速旋转。因此,即使你的手指没有进入球体边缘,惯性2D也可以实现这一点。

现在有趣的是,只要2D点不再在球体上投射(当穿过球体边缘时),那么运动会残酷地停止。因为所有2D点现在都投影到(z = 0)平面上。

我不知道这是否是你所说的“跳跃球”:)

现在要解决此问题,您需要使用某种惯性3D来降低3D旋转速度,而不是2D点移动。

我以前的观点仍然有效。希望这会有所帮助。

编辑:无法入睡:)

你可以试试这段代码吗?

private Vector3D ProjectToTrackball(Point point)
{
  double x = point.X / (ActualWidth / 2);    // Scale so bounds map to [0,0] - [2,2]
  double y = point.Y / (ActualHeight / 2);

  x = x - 1;                           // Translate 0,0 to the center
  y = 1 - y;                           // Flip so +Y is up instead of down
  double alpha = 1.0 / Math.Sqrt(x*x + y*y + 1);
  return new Vector3D(x*alpha, y*alpha, alpha);
}

我不保证任何东西,但这应该更平滑(可能太多)并且在球体边缘没有更多的不连续性......

虽然唯一的角度问题仍困扰着我......

编辑:新的:

  private Vector3D ProjectToTrackball(Point point)

  {
    // IMPORTANT NOTE: result should always be normalized

    double x = point.X / (ActualWidth / 2);    // Scale so bounds map to [0,0] - [2,2]

    double y = point.Y / (ActualHeight / 2);



    x = x - 1;                           // Translate 0,0 to the center

    y = 1 - y;                           // Flip so +Y is up instead of down



    double z2 = 1 - x * x - y * y;       // z^2 = 1 - x^2 - y^2
    double z =  0;

    if(z2 > 0)
      z2 = Math.Sqrt(z2); // Ok no need to normalize.
    else
    {
      // I will get rid of the discontinuity with a little trick:
      // I construct an imaginary point below the sphere.
      double length = Math.Sqrt(x * x + y * y);
      x = x / length;
      y = y / length;
      z = 1 - length;
      // Now I normalize:
      length = Math.Sqrt(x * x + y * y + z * z);
      x = x / length;
      y = y / length;
      z = z / length;
    }

    return new Vector3D(x, y, z);

  }

现在它应该像以前一样表现为手指在球体内的运动。穿过球体边缘时不再有不连续性。运动应该迅速减速。

我在第二次编辑中犯了一个错误:跳跃可能来自这样一个事实:当2D点不再投射到球体上时,ProjectToTrackball会返回一个非标准化矢量。所以一切都搞砸了。

注意:您应该选择一本OpenGL书并学习3D。

2009年11月18日的新编辑:

关于唯一角度的问题,我认为这是导致'它只围绕Z轴旋转'的问题。

首先将_rotation更改为四元数。某些代码必须在您的方法中进行更改,其中您将网格与_rotation相乘但不应过于困难。

然后你可以试试这个新的Track方法:

private void Track(Point currentPosition)
{
    Vector3D currentPosition3D = ProjectToTrackball(currentPosition);

    Vector3D axis = Vector3D.CrossProduct(_previousPosition3D, currentPosition3D);
    double angle = Vector3D.AngleBetween(_previousPosition3D, currentPosition3D);

    // quaterion will throw if this happens - sometimes we can get 3D positions that
    // are very similar, so we avoid the throw by doing this check and just ignoring
    // the event 
    if (axis.Length == 0)
        return;

    Quaternion delta = new Quaternion(axis, -angle);

    // Compose the delta with the previous orientation
    _rotation *= delta;

    _previousPosition3D = currentPosition3D;
}

对于Inertia,我放弃了......你需要某种3D旋转惯性。

答案 1 :(得分:0)

我在黑暗中采取了一次巨大的刺杀,但从您提出的代码判断,如果ManipulationOrigin属性在Manipulation_OnManipulationDelta和OnManipulationCompleted事件之间有显着差异会发生什么?

在我看来,如果用户非常快速地挥动手指,可能会导致事件之间的值显着不同,这可能会导致您遇到的“跳跃”症状。