OpenGL 光线投射(拾取):考虑对象的变换

OpenGL ray casting (picking): account for object's transform

为了拾取对象,我实现了一种类似于所描述的 here 的光线投射算法。将鼠标点击转换为射线(具有原点和方向)后,下一个任务是使该射线与场景中的所有三角形相交,以确定每个网格的命中点。

我还根据所描述的 here 实现了三角形相交测试算法。我的问题是,在执行交集时我们应该如何考虑对象的变换?显然,我不想将变换矩阵应用于所有顶点然后进行交集测试(太慢了)。

编辑:
这是我正在使用的 UnProject 实现(顺便说一句,我正在使用 OpenTK)。我比较了结果,它们符合 GluUnProject 给我的结果:

private Vector3d UnProject(Vector3d screen)
{
    int[] viewport = new int[4];
    OpenTK.Graphics.OpenGL.GL.GetInteger(OpenTK.Graphics.OpenGL.GetPName.Viewport, viewport);

    Vector4d pos = new Vector4d();

    // Map x and y from window coordinates, map to range -1 to 1 
    pos.X = (screen.X - (float)viewport[0]) / (float)viewport[2] * 2.0f - 1.0f;
    pos.Y = 1 - (screen.Y - (float)viewport[1]) / (float)viewport[3] * 2.0f;
    pos.Z = screen.Z * 2.0f - 1.0f;
    pos.W = 1.0f;

    Vector4d pos2 = Vector4d.Transform(pos, Matrix4d.Invert(GetModelViewMatrix() * GetProjectionMatrix()));
    Vector3d pos_out = new Vector3d(pos2.X, pos2.Y, pos2.Z);

    return pos_out / pos2.W;
}  

然后我使用此函数创建一条射线(具有原点和方向):

private Ray ScreenPointToRay(Point mouseLocation)
{
    Vector3d near = UnProject(new Vector3d(mouseLocation.X, mouseLocation.Y, 0));
    Vector3d far = UnProject(new Vector3d(mouseLocation.X, mouseLocation.Y, 1));

    Vector3d origin = near;
    Vector3d direction = (far - near).Normalized();
    return new Ray(origin, direction);
} 

您可以将每个对象的反向变换应用于光线。

我不知道这是否是 best/most 有效的方法,但我最近实现了类似这样的方法:

在世界space中,光线的原点是相机位置。为了获得光线的方向,我假设用户点击了相机的近平面并因此应用了 'reverse transformation' - 从屏幕 space 到世界 space - 到屏幕space 位置

( mouseClick.x, viewportHeight - mouseClick.y, 0 )

然后从中减去光线的原点,即相机位置 现在变换的鼠标点击位置。

在我的例子中,没有特定于对象的转换,这意味着一旦我在世界中有了光线,我就完成了 space。然而,之后用逆模型矩阵变换原点和方向就很容易了。

您提到您尝试应用逆向转换,但没有成功 - 也许其中有错误?为此,我使用了 GLM - 即 glm::unProject。