当用户将鼠标/手指移到球体上时,我试图让我的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并在需要更好的格式时将其发送给您
感谢您提供任何帮助,我非常感谢您的帮助!
标记
答案 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事件之间有显着差异会发生什么?
在我看来,如果用户非常快速地挥动手指,可能会导致事件之间的值显着不同,这可能会导致您遇到的“跳跃”症状。