使用elm-webgl中的Ray cast进行对象拾取

时间:2017-09-11 19:19:31

标签: webgl elm

演示几乎(?)工作示例:https://ellie-app.com/4h9F8FNcRPya1/1
对于演示:单击以绘制光线,并向左和向右旋转相机以查看光线。 (由于原点来自相机,您无法从创建它的位置看到它)

上下文
我正在研究榆树和榆树elm-webgl项目,我想知道当点击鼠标时鼠标是否在对象上。要做的是我尝试实现一个简单的光线投射。我需要的是两件事:
1)摄像机的坐标(这很容易)
2)点击了哪里的3D空间中的坐标/方向

问题
根据我的理解,从2D视图空间到3D世界空间的步骤是:
a)使坐标相对于视口
在-1到1的范围内 b)反转投影矩阵和透视矩阵
c)乘以投影和透视矩阵
d)从标准化的鼠标坐标创建Vector4 e)将组合矩阵与Vector4相乘 f)归一化结果

到目前为止尝试
我已经创建了一个函数来将Mouse.Position转换为坐标以绘制一条线:

getClickPosition : Model -> Mouse.Position -> Vec3
getClickPosition model pos =
    let
        x =
            toFloat pos.x

        y =
            toFloat pos.y

        normalizedPosition =
            ( (x * 2) / 1000 - 1, (1 - y / 1000 * 2) )

        homogeneousClipCoordinates =
            Vec4.vec4
                (Tuple.first normalizedPosition)
                (Tuple.second normalizedPosition)
                -1
                1

        inversedProjectionMatrix =
            Maybe.withDefault Mat4.identity (Mat4.inverse (camera model))

        inversedPerspectiveMatrix =
            Maybe.withDefault Mat4.identity (Mat4.inverse perspective)

        inversedMatrix2 =
            Mat4.mul inversedProjectionMatrix inversedPerspectiveMatrix

        to =
            Vec4.vec4
                (Tuple.first normalizedPosition)
                (Tuple.second normalizedPosition)
                1
                1

        toInversed =
            mulVector inversedMatrix2 to

        toNorm =
            Vec4.normalize toInversed

        toVec3 =
            vec3 (Vec4.getX toNorm) (Vec4.getY toNorm) (Vec4.getZ toNorm)
    in
        toVec3

结果
这个功能的结果是光线太中心到我点击的位置。我添加了一个截图,我点击了立方体的所有四个顶面。如果单击视口的中心,则光线将正确定位。

感觉很接近,但还没到那里,我无法弄清楚我做错了什么!

Example image of current situation

1 个答案:

答案 0 :(得分:1)

在尝试其他方法后,我找到了解决方案:

getClickPosition : Model -> Mouse.Position -> Vec3
getClickPosition model pos =
    let
        x =
            toFloat pos.x

        y =
            toFloat pos.y

        normalizedPosition =
            ( (x * 2) / 1000 - 1, (1 - y / 1000 * 2) )

        homogeneousClipCoordinates =
            Vec4.vec4
                (Tuple.first normalizedPosition)
                (Tuple.second normalizedPosition)
                -1
                1

        inversedViewMatrix =
            Maybe.withDefault Mat4.identity (Mat4.inverse (camera model))

        inversedProjectionMatrix =
            Maybe.withDefault Mat4.identity (Mat4.inverse perspective)

        vec4CameraCoordinates = mulVector inversedProjectionMatrix homogeneousClipCoordinates

        direction = Vec4.vec4 (Vec4.getX vec4CameraCoordinates) (Vec4.getY vec4CameraCoordinates) -1 0

        vec4WorldCoordinates = mulVector inversedViewMatrix direction

        vec3WorldCoordinates = vec3 (Vec4.getX vec4WorldCoordinates) (Vec4.getY vec4WorldCoordinates) (Vec4.getZ vec4WorldCoordinates)

        normalizedVec3WorldCoordinates = Vec3.normalize vec3WorldCoordinates

        origin = model.cameraPos

        scaledDirection = Vec3.scale  20 normalizedVec3WorldCoordinates

        destination = Vec3.add origin scaledDirection

    in
        destination

我尽可能地保留了详细信息,如果有人发现我使用了不正确的术语,请发表评论,我会更新答案。

我确信有很多可能的优化(在反转或组合某些步骤之前乘以矩阵。)

在此处更新了ellie应用:https://ellie-app.com/4hZ9s8S92PSa1/0