首页 > 代码库 > opengl编程 : 利用鼠标的位置跟踪一条在世界坐标系中的射线

opengl编程 : 利用鼠标的位置跟踪一条在世界坐标系中的射线

 

如何用鼠标与三维世界的物体交互呢?

想了一种方法:将鼠标在二维屏幕的位置转化为三维世界坐标系的位置,记该位置为pm(mouse position)。我们在窗口中看到的内容都是基于观察坐标系的,

鼠标的位置可以看作是观察坐标系中,投影可见体的前平面中的一个位置,如何找到这样的一个位置呢?

技术分享

 

可以肯定,pm·.z的值就是近平面的值,这点是没有疑问的,因为pm·就在近平面上。

我们假定投影平面和窗口有相同的宽高比,这些不会有失真出现。

接下来找pm·.x和pm·.y

1.将屏幕 0 - window_height-1变换到 1 到 -1

   将屏幕 0 - window_width-1变换到 -1 到  1

2.找到投影平面的宽度,和投影平面的高度,可以这样计算 h = tan(Fov)*near  ,  w = ar*h;       (ar为宽高比,一般是事先定义好的,Fov为观察角度)

   1 到 -1变化为 h 到 -h     

   -1到   1 变化 为-w 到 w

        float scale_h = tanf(Fov)*Near;   //投影窗口高度的一半
        float scale_w = ar*scale_h;

        Vector3f<GLfloat> aim = Vector3f<GLfloat>(scale_w*(static_cast<float>(m_mousePos_x) / static_cast<float>(m_windowWidth)*2.f - 1.f), 
            scale_h*(1.f-static_cast<float>(m_mousePos_y) / static_cast<float>(m_windowHeight)*2.f), Near);

如上代码变可以找到2维鼠标在3维投影平面的位置pm`

接下来要将pm`由观察坐标系变化到世界坐标系,通过乘以观察矩阵的逆就ok了,接下来贴出观察矩阵的逆的计算过程

    void SetView(const Vector3f<T> & eye, const Vector3f<T> & z_target, const Vector3f<T> & up, Mat4<T>& ViewTranspose)
    {
        Vector3f<T> N = z_target;
        N.Normalize();
        Vector3f<T> U = up;
        U.Normalize();
        U = U.Cross(z_target);
        Vector3f<T> V = N.Cross(U);

        /*以下四行是坐标系旋转矩阵,将物体先进行坐标系平移,再进行坐标系旋转*/
        data[0][0] = U._Vector3f.x;  data[0][1] = U._Vector3f.y;  data[0][2] = U._Vector3f.z; data[0][3] = 0.0f;
        data[1][0] = V._Vector3f.x;  data[1][1] = V._Vector3f.y;  data[1][2] = V._Vector3f.z; data[1][3] = 0.0f;
        data[2][0] = N._Vector3f.x;  data[2][1] = N._Vector3f.y;  data[2][2] = N._Vector3f.z; data[2][3] = 0.0f;
        data[3][0] = 0.0f; data[3][1] = 0.0f; data[3][2] = 0.0f; data[3][3] = 1.0f;

        Mat4<T> trans;
        trans.SetTranlate(Vector3f<T>( -eye._Vector3f.x, -eye._Vector3f.y, -eye._Vector3f.z));//坐标系平移矩阵

        Mat4<T> transTranspose;
        transTranspose.SetTranlate(Vector3f<T>(eye._Vector3f.x, eye._Vector3f.y, eye._Vector3f.z));//坐标系平移矩阵的逆
        Mat4<T> vi = *this;
        vi.transpose();//生成坐标系旋转矩阵的逆
        
        ViewTranspose = transTranspose*vi; //观察矩阵的逆 view-1 = (view)-1 = (trans*(*this))-1 = (*this)-1*(trans)-1

        *this = *this*trans;//观察矩阵
    }

将pm`应用观察矩阵的逆就可以得到鼠标在世界坐标系中的坐标了。

眼睛在世界坐标中的位置已经是知道的了,那么眼睛和鼠标的位置一起就可以构成一条射线,应用这条射线与世界中的物体求交,这样就可以方便交互了.

 

技术分享

 

 类似上图的效果,移动鼠标可以实时地显示在哪个方格上。

opengl编程 : 利用鼠标的位置跟踪一条在世界坐标系中的射线