现代 OpenGL 平移函数在透视图中使用世界空间原点



我正在使用OpenTK和C#在3D透视图中实现一个平移函数。这个想法是用鼠标右键单击具有直观的"单击和拖动"功能。一个明显的复杂问题是场景中的哪个深度用于单击/拖动,因为位移量取决于深度。

我在场景中大部分是空白的,所以期望从指针下方的对象获得深度似乎不是一个好的解决方案。因此,我的想法是使用模型空间原点(也是旋转中心(作为参考,并用鼠标移动。

我使用了下面的代码。它与原始缩放级别一样工作,但是一旦我放大或缩小,位移就会变得太多(放大时(或太少(缩小时(。

方法是这样的,当按下右键时:

  1. 获取移动开始和结束时的屏幕坐标。
  2. 获取世界空间原点 (0,0,0( 的屏幕 z(场景中的深度(。
  3. 将该 z 值应用于开始/结束屏幕坐标,并将其转换为模型空间。
  4. 获取两者之间的向量。
  5. 将该矢量作为平移应用于模型矩阵,发送到顶点着色器并渲染场景。

我的问题是:

  1. 我在这里计算过度了吗?该方法可以简化吗?
  2. 为什么一旦缩放级别更改,我的鼠标指针就不会按预期与原点保持固定距离?

.

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;
}

最新更新