2013-11-22 78 views

回答

5

你有什么是2D在屏幕上的位置。要做的第一件事是将该点从像素转换为标准化的设备坐标 - -1为1.然后,您需要找到该点代表的3D空间中的线条。为此,您需要3D应用程序用于创建投影和相机的转换矩阵/ ces。

通常,您有3个指标:投影,视图和模型。当您为一个对象指定顶点时,它们位于“对象空间”中。乘以模型矩阵给出“世界空间”中的顶点。视图矩阵再次乘以“眼睛/相机空间”。投影再次乘以“剪辑空间”。剪辑空间具有非线性深度。将Z分量添加到鼠标坐标将它们放入剪辑空间。您可以在任何线性空间中执行线/对象相交测试,因此您必须至少将鼠标坐标移至眼图空间,但在世界空间(或对象空间取决于场景图)中执行相交测试更方便。

要将剪辑空间中的鼠标坐标移至世界空间,请添加Z分量并乘以逆投影矩阵,然后乘以逆摄像机/视图矩阵。要创建一条线,将计算沿着Z的两个点 - fromto

enter image description here

在以下示例中,我有对象的列表,每一个位置和边界半径。当然,十字路口绝对不会完美匹配,但现在它的效果已经足够好了。这不是伪代码,但它使用我自己的矢量/矩阵库。你必须在自己的地方替代你自己。

vec2f mouse = (vec2f(mousePosition)/vec2f(windowSize)) * 2.0f - 1.0f; 
mouse.y = -mouse.y; //origin is top-left and +y mouse is down 

mat44 toWorld = (camera.projection * camera.transform).inverse(); 
//equivalent to camera.transform.inverse() * camera.projection.inverse() but faster 

vec4f from = toWorld * vec4f(mouse, -1.0f, 1.0f); 
vec4f to = toWorld * vec4f(mouse, 1.0f, 1.0f); 

from /= from.w; //perspective divide ("normalize" homogeneous coordinates) 
to /= to.w; 

int clickedObject = -1; 
float minDist = 99999.0f; 

for (size_t i = 0; i < objects.size(); ++i) 
{ 
    float t1, t2; 
    vec3f direction = to.xyz() - from.xyz(); 
    if (intersectSphere(from.xyz(), direction, objects[i].position, objects[i].radius, t1, t2)) 
    { 
     //object i has been clicked. probably best to find the minimum t1 (front-most object) 
     if (t1 < minDist) 
     { 
      minDist = t1; 
      clickedObject = (int)i; 
     } 
    } 
} 

//clicked object is objects[clickedObject] 

相反的intersectSphere,你可以使用一个边框或其他隐式几何体,或者交叉网状的三角形(这可能需要建立一个kd树的性能原因)。

[编辑]
这里是line/sphere intersect的实现(基于上面的链接)。它假设球体位于原点,因此不要将from.xyz()作为p传递给from.xyz() - objects[i].position

//ray at position p with direction d intersects sphere at (0,0,0) with radius r. returns intersection times along ray t1 and t2 
bool intersectSphere(const vec3f& p, const vec3f& d, float r, float& t1, float& t2) 
{ 
    //http://wiki.cgsociety.org/index.php/Ray_Sphere_Intersection 
    float A = d.dot(d); 
    float B = 2.0f * d.dot(p); 
    float C = p.dot(p) - r * r; 

    float dis = B * B - 4.0f * A * C; 

    if (dis < 0.0f) 
     return false; 

    float S = sqrt(dis);  

    t1 = (-B - S)/(2.0f * A); 
    t2 = (-B + S)/(2.0f * A); 
    return true; 
} 
+0

这是一个很好的解释,谢谢你告诉了这么久。我会尝试这种方法。你能否也请分享函数的实现:'intersectSphere()'? –

+0

@ibrahimdemir谢谢!完成。祝你好运:) – jozxyqk

+0

'mat44 toWorld =(camera.projection * camera.transform).inverse();' 我不明白camera.projection和camera.transform矩阵是什么。你能解释一下还是在ninevehgl框架中写出等价矩阵? –