2017-03-09 61 views
0

我已經嘗試了很多來自openframeworks論壇和文檔的代碼示例,但我無法得到一個來做我想做的事情。檢查ofRay是否與MeshFace相交

我有一個ofRay(來自ofxRay)和一個of3dPrimitive的列表。我試圖弄清楚光線是否與原始圖像相交,如果是的話,要知道光線與「第一個」相交的原始圖像(如哪一個與屏幕最接近)。

void renderer::selectPrimitive(int x, int y, bool shiftHeld) 
{ 
    ofVec3f screenToWorld = (**cam).screenToWorld(ofVec3f(x, y, 0.0)); 

    primitive* intersectPrim = nullptr; 
    int distanceClosest = std::numeric_limits<int>::max(); 

    ofVec3f vectNow = (screenToWorld - (**cam).getPosition()); 

    vectNow = vectNow.normalize(); 

    ofRay ray((**cam).getPosition(), vectNow, true); 
    // To draw the ray on screen, for debugging 
    // rays.push_back(ray); 

    for (primitive& p : *scn) 
    { 
     if (!shiftHeld) 
     { 
      p.setSelected(false); 
     } 

     float* distance = new float(0); 

     bool found = p.checkIntersectionPlaneAndLine(ray, distance); 
     if (found)// && *distance >= 0 && *distance < distanceClosest) 
     { 
      intersectPrim = &p; 
      //distanceClosest = *distance; 
     } 
    } 

    if (distanceClosest < (std::numeric_limits<int>::max() - 1)) 
    { 
     intersectPrim->setSelected(!intersectPrim->getSelected()); 
     std::cout << "Selected Primitive" << std::endl; 
    } 
    else 
    { 
     std::cout << "Selected Nothing" << std::endl; 
    } 
} 

這裏有不同的方法我試過,從很多例子在許多網站拼湊起來的,但他們沒有正常工作。

第一次嘗試:

bool primitive3d::calcTriangleIntersection(ofRay ray, float *result) const { 

    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) { 

     ofMeshFace face = *i; 

     ofVec3f edge1, edge2, tvec, pvec, qvec; 
     float det; 
     float u, v; 
     const float EPSILON = 0.000001f; 

     edge1 = face.getVertex(1) - face.getVertex(0); 
     edge2 = face.getVertex(2) - face.getVertex(0); 

     pvec = ray.t.getCrossed(edge2); 
     det = edge1.dot(pvec); 

#if 0 // we don't want to backface cull 
     if (det >= EPSILON) 
     { 
      tvec = getOrigin() - vert0; 

      u = tvec.dot(pvec); 
      if (!((u < 0.0f) || (u > det))) 
      { 

       qvec = tvec.getCrossed(edge1); 
       v = getDirection().dot(qvec); 
       if (!(v < 0.0f || u + v > det)) 
       { 

        *result = edge2.dot(qvec)/det; 
        return true; 
       } 
      } 
     } 
#else 
     if (!(det > -EPSILON && det < EPSILON)) 
     { 
      float inv_det = 1.0f/det; 
      tvec = ray.s - face.getVertex(0); 
      u = tvec.dot(pvec) * inv_det; 
      if (!(u < 0.0f || u > 1.0f)) 
      { 

       qvec = tvec.getCrossed(edge1); 

       v = ray.t.dot(qvec) * inv_det; 
       if (!(v < 0.0f || u + v > 1.0f)) 
       { 

        *result = edge2.dot(qvec) * inv_det; 
        return true; 
       } 
      } 
     } 
#endif 
    } 
    return false; 
} 

第二次嘗試:

bool primitive3d::checkIntersectionPlaneAndLine(ofRay ray, float *result) const { 

    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) 
    { 

     ofMeshFace face = *i; 

     ofVec3f P1, P2; 
     P1 = ray.getStart(); 
     P2 = ray.getEnd(); 

     ofVec3f p1, p2, p3; 
     p1 = face.getVertex(0); 
     p2 = face.getVertex(1); 
     p3 = face.getVertex(2); 

     ofVec3f v1 = p1 - p2; 
     ofVec3f v2 = p3 - p2; 

     float a, b, c, d; 

     a = v1.y * v2.z - v1.z * v2.y; 
     b = -(v1.x * v2.z - v1.z * v2.x); 
     c = v1.x * v2.y - v1.y * v2.x; 
     d = -(a * p1.x + b * p1.y + c * p1.z); 

     ofVec3f O = P1; 
     ofVec3f V = P2 - P1; 

     float t; 

     t = -(a * O.x + b * O.y + c * O.z + d)/(a * V.x + b * V.y + c * V.z); 

     ofVec3f p = O + V * t; 

     float xmin = std::min(P1.x, P2.x); 
     float ymin = std::min(P1.y, P2.y); 
     float zmin = std::min(P1.z, P2.z); 

     float xmax = std::max(P1.x, P2.x); 
     float ymax = std::max(P1.y, P2.y); 
     float zmax = std::max(P1.z, P2.z); 


     if (inside(p, xmin, xmax, ymin, ymax, zmin, zmax)) { 
      *result = p.length(); 
      return true; 
     } 
    } 
    return false; 
} 

bool primitive3d::inside(ofVec3f p, float xmin, float xmax, float ymin, float ymax, float zmin, float zmax) const { 

    if (p.x >= xmin && p.x <= xmax && p.y >= ymin && p.y <= ymax && p.z >= zmin && p.z <= zmax) 
     return true; 

    return false; 

} 

第三次嘗試:

#define SMALL_NUM 0.00000001 // anything that avoids division overflow 
// dot product (3D) which allows vector operations in arguments 
#define dot(u,v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z) 

bool primitive3d::checkIntersectionTriangleRay(ofRay ray, ofPoint* inter) 
{ 
    ofMesh mesh = prim->getMesh(); 
    std::vector<ofMeshFace> indices = mesh.getUniqueFaces(); 

    for (std::vector<ofMeshFace>::iterator i = indices.begin(); i != indices.end(); ++i) 
    { 
     ofMeshFace triangle = *i; 

     ofVec3f u, v, n;    // Vecs of triangle 
     ofVec3f dir, w0, w;   // Vecs of ofRay 
     float  r, a, b;    // params to calc ray-plane intersect 

             // get triangle edge vectors and plane normal 
     u = triangle.getVertex(1) - triangle.getVertex(0); 
     v = triangle.getVertex(2) - triangle.getVertex(0); 
     n = u * v;    // cross product 
     if (!(n == ofVec3f(0, 0, 0)))   // if triangle is not degenerate 
     { 

      dir = ray.getEnd() - ray.getStart();    // ray direction vector 
      w0 = ray.getStart() - triangle.getVertex(0); 
      a = -dot(n, w0); 
      b = dot(n, dir); 
      if (!(fabs(b) < SMALL_NUM)) 
      {  // if ray is not parallel to triangle 

       // get intersect point of ray with triangle plane 
       r = a/b; 
       if (!(r < 0.0))     // ray goes toward the triangle 
       { 
        // for a segment, also test if (r > 1.0) => no intersect 

        *inter = ray.getStart() + r * dir;   // intersect point of ray and plane 

                // is I inside T? 
        float uu, uv, vv, wu, wv, D; 
        uu = dot(u, u); 
        uv = dot(u, v); 
        vv = dot(v, v); 
        w = *inter - triangle.getVertex(0); 
        wu = dot(w, u); 
        wv = dot(w, v); 
        D = uv * uv - uu * vv; 

        // get and test parametric coords 
        float s, t; 
        s = (uv * wv - vv * wu)/D; 
        if (!(s < 0.0 || s > 1.0))   // I is inside T 
        { 
         t = (uv * wu - uu * wv)/D; 
         if (!(t < 0.0 || (s + t) > 1.0)) // I is inside T 
          return true;      // I is in T 
        } 
       } 
      } 
     } 
    } 
    return false; 
} 

我試過這麼多的事情,但他們沒有工作。我也在畫我的光線到屏幕上,所以我知道一個事實,他們確實創建了正確的方向,走向無限遠處

爲了清楚起見,我刪除了很多代碼使其易於閱讀。我只在第二種方法中缺少 //這裏檢測 部分,因爲我不知道如何使其工作。

+0

問題已編輯,以添加我所有失敗的嘗試 –

回答

1

假設在互聯網上有大量的三角形/網格光線投射代碼,你嘗試過就能解決你的問題,我認爲你的問題在第一種方法中,你設置一個「找到」變量,但不要從方法如果找到或選擇最小距離的對象。

您提供的代碼將僅返回最後一個基元的命中測試結果。

如果您的代碼過於簡化,請再次發佈更多詳細信息。

編輯:

你從網格的臉上得到的座標是對象空間。在進行任何計算之前,您需要將它們轉換爲世界空間,或者更好地將光線轉換爲對象空間。

的工作實現見下面的代碼: https://github.com/mrdoob/three.js/blob/master/src/objects/Mesh.js

注意,應用世界矩陣的applyMatrix4呼籲給他們帶來同樣的空間。

+0

行「bool found = findintersection()」基本上只是爲了調試目的。我知道(從繪製到屏幕的原始和光線)光線穿過原始。但在調試中,這個布爾值保持爲假。因此,我並不需要添加任何更多的代碼,因爲它會出現在「if(found){}」中,所以它不會被觸發。我需要確認布爾值在應用之前是否爲真。 –

+0

我編輯了這個問題,添加了所有失敗的嘗試 –

+0

我試過了,但在openframeworks中找不到任何方法將網格的面的座標轉換爲世界空間,或將射線轉換爲對象空間。你怎麼做到這一點? –