我正在使用OpenTK和C#在3D透视图中实现平移功能。我们的想法是用鼠标右键实现直观的 "点击和拖动 "功能。一个显而易见的复杂问题是,在场景中使用哪个深度来进行点击拖动,因为位移量与深度有关。
我在场景中得到的大部分是空白空间,所以期望从指针下的对象中得到深度似乎不是一个好的解决方案。因此,我的想法是使用模型空间原点(也是旋转中心)作为参考,并让它随着鼠标移动。
我使用了下面的代码。原先的缩放级别可以正常工作,但一旦我放大或缩小,位移就会变得过大(放大时)或过小(缩小时)。
方法是这样的,当按下右键时。
我的问题是
.
private Vector3 pan = new Vector3();
private float zoom = -3;
private Point mouseStartDrag;
private void GlControl1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
mouseStartDrag = new Point(e.X, e.Y);
}
}
private void GlControl1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right) {
Vector3 origin_screen = Project( new Vector3(0, 0, 0) ); // get the screen z for the world space origin
Vector4 screen1 = ScreenToViewSpace( mouseStartDrag, origin_screen.Z ); // start
Vector4 screen2 = ScreenToViewSpace( new Point(e.X, e.Y), origin_screen.Z ); // end
pan = new Vector3(screen2 - screen1);
ApplyPanZoom();
}
}
private Vector4 ScreenToViewSpace(Point MousePos, float ScreenZ) {
int[] viewport = new int[4];
OpenTK.Graphics.OpenGL.GL.GetInteger(OpenTK.Graphics.OpenGL.GetPName.Viewport, viewport);
Vector4 pos = new Vector4();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (MousePos.X - (float)viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
pos.Y = 1 - (MousePos.Y - (float)viewport[1]) / (float)viewport[3] * 2.0f;
pos.Z = ScreenZ * 2.0f - 1.0f;
pos.W = 1.0f;
return pos * view;
}
private Vector3 Project(Vector3 p)
{
Vector4 clipSpace = new Vector4(p.X, p.Y, p.Z, 1.0f) * model * view * projection; // clip space coordinates
Vector4 ndc = Vector4.Divide(clipSpace, clipSpace.W); // normalised device coordinates
return new Vector3(
glControl1.Width * (ndc.X + 1) / 2,
glControl1.Height * (ndc.Y + 1) / 2,
(ndc.Z + 1) / 2
);
}
private void ApplyPanZoom() {
view = Matrix4.CreateTranslation(pan.X, pan.Y, zoom);
SetMatrix4(Handle, "view", view);
glControl1.Invalidate();
}
回答我自己的问题。通过试验、错误和谷歌,最终得到了它。问题是我的'屏幕查看空间'的方法不太正确。我不得不乘以反投影矩阵。这样就可以了,结果就是我想实现的那种点击和拖动平移。
private Vector3 ScreenToViewSpace(Point MousePos, float ScreenZ) {
int[] viewport = new int[4];
OpenTK.Graphics.OpenGL.GL.GetInteger(OpenTK.Graphics.OpenGL.GetPName.Viewport, viewport);
Vector4 pos = new Vector4();
// Map x and y from window coordinates, map to range -1 to 1
pos.X = (MousePos.X - (float)viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
pos.Y = 1 - (MousePos.Y - (float)viewport[1]) / (float)viewport[3] * 2.0f;
pos.Z = ScreenZ * 2.0f - 1.0f;
pos.W = 1.0f;
Vector4 a = Vector4.Transform(pos, Matrix4.Invert(projection));
Vector3 b = new Vector3(a.X, a.Y, a.Z);
return b / a.W;
}