在透视视图中使用世界空间原点的现代OpenGL平移功能。

问题描述 投票:1回答:1

我正在使用OpenTK和C#在3D透视图中实现平移功能。我们的想法是用鼠标右键实现直观的 "点击和拖动 "功能。一个显而易见的复杂问题是,在场景中使用哪个深度来进行点击拖动,因为位移量与深度有关。

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

我使用了下面的代码。原先的缩放级别可以正常工作,但一旦我放大或缩小,位移就会变得过大(放大时)或过小(缩小时)。

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

  1. 获取移动开始和结束时的屏幕坐标。
  2. 获取世界空间原点(0,0,0)的屏幕z(场景内深度)。
  3. 将该z值应用于startend屏幕坐标,并将其转换为模型空间。
  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();
}
c# opengl opentk
1个回答
0
投票

回答我自己的问题。通过试验、错误和谷歌,最终得到了它。问题是我的'屏幕查看空间'的方法不太正确。我不得不乘以反投影矩阵。这样就可以了,结果就是我想实现的那种点击和拖动平移。

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;

}
© www.soinside.com 2019 - 2024. All rights reserved.