2011-11-04 92 views
2

我正試圖在Android中使用3D(OpenGL ES)實現「whack-a-mole」類型的遊戲。現在,在任何給定時間我都有一個3D形狀(旋轉立方體),代表我的「鼴鼠」。我在我的視圖中有一個觸摸事件處理程序,它在我的渲染器中隨機設置一些x,y值,導致立方體移動(使用glTranslatef())。在3D「場景」中處理觸摸事件或屏幕到3D座標

我還沒有遇到任何教程或文檔,完全橋接屏幕觸摸事件到3D場景。我已經做了很多工作,以到達我所在的位置,但我似乎無法完成剩餘的任務。

developer.andrdoid.com我用我猜可能被視爲輔助類的矩陣:MatrixGrabber.javaMatrixStack.javaMatrixTrackingGL.java

我在我的GLU.glUnProject method中使用這些類,它應該做從真實屏幕座標到3D或物體座標的轉換。

段:

MatrixGrabber mg = new MatrixGrabber(); 
    int viewport[] = {0, 0, renderer._width, renderer._height}; 
    mg.getCurrentModelView(renderer.myg); 
    mg.getCurrentProjection(renderer.myg); 
    float nearCoords[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 
    float farCoords[] = { 0.0f, 0.0f, 0.0f, 0.0f }; 
    float x = event.getX(); 
    float y = event.getY(); 
    GLU.gluUnProject(x, y, -1.0f, mg.mModelView, 0, mg.mProjection , 0, viewport, 0, nearCoords, 0) 
    GLU.gluUnProject(x, y, 1.0f, mg.mModelView, 0, mg.mProjection , 0, viewport, 0, farCoords, 0) 

這段代碼執行沒有錯誤,將輸出看起來不正確的。我知道屏幕左下角有原點(0,0)。而3D場景,至少是我的,似乎有一個經典的笛卡爾系統在屏幕中間的起源。因此,在屏幕座標爲(0,718)的位置觸摸左下角,運行我的代碼。我從去年參數gluUnProject輸出:

靠近:{-2.544,2.927,2.839,1.99}

遠:{0.083,0.802,-0.760,0.009}

這些數字不對我有意義。我的觸摸甚至在第三象限,所以我所有的近距離和遠距離的x,y值應該是負的,但它們不是。 gluUnProject文件沒有提到任何需要轉換屏幕座標。然後再一次,相同的文檔會讓你相信Near和Far應該是3號的數組,但它們必須是4號,我沒有CLUE的原因。

所以,我有兩個問題(我敢肯定會有更多的問題出現)。

  1. 我怎樣才能確保我得到適當的近和遠的座標基於屏幕座標 。
  2. 一旦我有近和遠的座標,我如何使用它來查找它們創建的線是否與屏幕上的對象相交。

回答

2

我記得在我大學的時候,在Android上回到glUnProject問題。 (這是在Android的早期階段)我的一位同學發現我們的計算會被glUnProject的結果中的第四維所破壞。如果我記得正確的話,這是某處記錄的東西,但由於某種原因,我還沒有能夠再次挖掘。我從來沒有深入瞭解它的具體細節,但也許幫助我們的東西也可能對你有用。這是有可能與我們應用數學做...

/** 
* Convert the 4D input into 3D space (or something like that, otherwise the gluUnproject values are incorrect) 
* @param v 4D input 
* @return 3D output 
*/ 
private static float[] fixW(float[] v) { 
    float w = v[3]; 
    for(int i = 0; i < 4; i++) 
     v[i] = v[i]/w; 
    return v; 
} 

我們實際使用上面的方法來解決了glUnProject結果,並做拾/觸摸/選擇在3D空間中的球形物體的動作。以下代碼可能會提供如何執行此操作的指南。這不僅僅是投射射線並進行射線球面相交測試。

一些額外的注意事項,可能使下面的代碼更容易理解:

  • Vector3f是基於3個浮點值的三維矢量的自定義實現,並實現了通常的向量運算。
  • shootTarget是3D空間中的球形物體。
  • 諸如getXZBoundsInWorldspace(0)getPosition(0)這樣的調用中的0只是一個索引。我們實現了三維模型動畫,索引決定了要返回的模型的「框架/姿態」。由於我們最終在非動畫對象上執行了此特定命中測試,因此我們始終使用第一幀。
  • Concepts.wConcepts.h只是屏幕的寬度和高度(以像素爲單位) - 或者對於全屏應用程序可能會有所不同:屏幕的分辨率。

_

/** 
* Checks if the ray, casted from the pixel touched on-screen, hits 
* the shoot target (a sphere). 
* @param x 
* @param y 
* @return Whether the target is hit 
*/ 
public static boolean rayHitsTarget(float x, float y) { 
    float[] bounds = Level.shootTarget.getXZBoundsInWorldspace(0); 
    float radius = (bounds[1] - bounds[0])/2f; 
    Ray ray = shootRay(x, y); 
    float a = ray.direction.dot(ray.direction); // = 1 
    float b = ray.direction.mul(2).dot(ray.near.min(Level.shootTarget.getPosition(0))); 
    float c = (ray.near.min(Level.shootTarget.getPosition(0))).dot(ray.near.min(Level.shootTarget.getPosition(0))) - (radius*radius); 

    return (((b * b) - (4 * a * c)) >= 0); 

} 

/** 
* Casts a ray from screen coordinates x and y. 
* @param x 
* @param y 
* @return Ray fired from screen coordinate (x,y) 
*/ 
public static Ray shootRay(float x, float y){ 
    float[] resultNear = {0,0,0,1}; 
    float[] resultFar = {0,0,0,1}; 

    float[] modelViewMatrix = new float[16]; 
    Render.viewStack.getMatrix(modelViewMatrix, 0); 

    float[] projectionMatrix = new float[16]; 
    Render.projectionStack.getMatrix(projectionMatrix, 0); 

    int[] viewport = { 0, 0, Concepts.w, Concepts.h }; 

    float x1 = x; 
    float y1 = viewport[3] - y; 

    GLU.gluUnProject(x1, y1, 0.01f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, resultNear, 0); 
    GLU.gluUnProject(x1, y1, 50f, modelViewMatrix, 0, projectionMatrix, 0, viewport, 0, resultFar, 0); 
    //transform the results from 4d to 3d coordinates. 
    resultNear = fixW(resultNear); 
    resultFar = fixW(resultFar); 
    //create the vector of the ray. 
    Vector3f rayDirection = new Vector3f(resultFar[0]-resultNear[0], resultFar[1]-resultNear[1], resultFar[2]-resultNear[2]); 
    //normalize the ray. 
    rayDirection = rayDirection.normalize(); 
    return new Ray(rayDirection, resultNear, resultFar); 
} 

/** 
* @author MH 
* Provides some accessors for a casted ray. 
*/ 
public static class Ray { 
    Vector3f direction; 
    Vector3f near; 
    Vector3f far; 

    /** 
    * Casts a new ray based on the given direction, near and far params. 
    * @param direction 
    * @param near 
    * @param far 
    */ 
    public Ray(Vector3f direction, float[] near, float[] far){ 
     this.direction = direction; 
     this.near = new Vector3f(near[0], near[1], near[2]); 
     this.far = new Vector3f(far[0], far[1], far[2]); 
    } 
} 
1

我該如何確保根據屏幕座標得到正確的近和遠座標。

如果您還沒有在其他地方閱讀過有關GLU.glProject的信息,請首先閱讀this answerGLU.glUnProject做了那個函數的反函數,我發現它更容易理解,並有助於理解將屏幕座標映射到對象空間的概念。至少爲我工作。

如果你想測試你得到正確值從GLU.glUnProject這是最容易做到,如果你從一些易於理解的投影和模型視圖矩陣開始。這是我今天早些時候使用的代碼片段;

// Orthographic projection for the sake of simplicity. 
    float projM[] = new float[16]; 
    Matrix.orthoM(projM, 0, -ratio, ratio, 1f, -1f, zNear, zFar); 

    // Model-View matrix is simply a 180 degree rotation around z -axis. 
    float mvM[] = new float[16]; 
    Matrix.setLookAtM(mvM, 0, 0f, 0f, eyeZ, 0f, 0f, 0f, 0f, 1f, 0f); 
    Matrix.rotateM(mvM, 0, 180f, 0f, 0f, 1f); 

    // (0, 0) is top left point of screen. 
    float x = (width - (width/ratio))/2; 
    float y = height; 
    float objNear[] = new float[4]; 
    float objFar[] = new float[4]; 
    int view[] = { 0, 0 ,width, height }; 
    // --> objNear = { 1, 1, eyeZ - zNear } 
    GLU.gluUnProject(x, y, 0, mvM, 0, projM, 0, view, 0, objNear, 0); 
    // --> objFar = { 1, 1, eyeZ - zFar } 
    GLU.gluUnProject(x, y, 1, mvM, 0, projM, 0, view, 0, objFar, 0); 

對於更復雜的模型 - 視圖和投影矩陣就變得相當困難,驗證你要在物空間你希望座標。這可能是一個很好的起點,可以先玩一些容易理解的矩陣。一旦你對結果滿意,你根本不用擔心GLU.glUnProject

一旦我有近及遠的座標,我怎麼用它來找到,如果他們所創造的線與屏幕上的對象。

對於3D對象的空間命中測試,它應該是最簡單的預先計算bounding spherebounding box爲對象。一旦你想檢查用戶是否點擊了一個對象,並且在對象空間中有兩個屏幕點unProjected,則對象空間中的一條線上有兩個點。

如果您使用的是邊界球,則可以使用此線計算intersection points,或者也可以只計算球體中心點distance。在前一種情況下,至少應該有一個交點,後一個距離應該小於球半徑。

對於邊框this question是一個很好的閱讀。

+0

謝謝您的回答和編輯。假設我的gluUnProject工作正常,我想了解更多有關如何使用座標來檢測碰撞的信息。我遇到的大多數資源都提示「雷霆萬鈞」,但實際上並沒有提供任何關於如何「走線」的信息,以查看它是否與屏幕上的對象相交。也許這並不像我想要的那樣困難,但是我已經爲此做了一些工作,而且這裏的資源也很差。 – Pete

+0

這個[Wolfram頁面](http://mathworld.wolfram.com/Point-LineDistance3-Dimensional.html)很好地解釋了與邊界球體的相交。意思是,對於命中測試來說,計算球體中心點與該線的距離並將其與半徑進行比較就足夠了。 – harism