将屏幕坐标转换为0y

时间:2019-05-08 19:48:08

标签: android opengl-es perspectivecamera

将android opengles屏幕坐标转换为世界坐标。

我正在尝试将屏幕点击转换为我的opengl世界坐标。我的世界只是一个以-y向上且-z较远的矩形矩形平面,其中心为0,0,0。在我的摄影机视图中,飞机旋转了,所以摄影机的位置是0,-10,7,看着0,0,0。因此,在screenwidth / 2,screenheight / 2处轻按一下,应返回0、0、0的opengl坐标。谢谢,我只是无法解决这个问题。这是我的尝试...

//setup world coordinate matrices
/*//set camera position and angles
    Matrix.setLookAtM(cameraViewMatrix, 0, GetCameraPosition().x, -GetCameraPosition().y, GetCameraPosition().z,
        GetCameraTarget().x, GetCameraTarget().y, GetCameraTarget().z,
        0f, 1f, 0.0f);

// Setup perspective projection matrix
    Matrix.perspectiveM(perspectiveMatrix, 0, 45, aspectRatio, 0.1f, -100f);
    tempMatrix = new float[16];
    Matrix.multiplyMM(viewProjMatrix, 0, perspectiveMatrix, 0, cameraViewMatrix, 0);//add camera matrix to perspective
}*/
private PointF ScreenToGL(int screenX, int screenY){

    //get percent of screen
    float percentWidth = (float)screenX / (float)screenWidth;
    float percentHeight = (float)screenY / (float)screenHeight;

    //convert to percent of gl
    float glX = glScreenWidth * percentWidth;
    float glY = glScreenHeight * percentHeight;

    //center coordinates
    glX -= glScreenWidth/2;
    glY -= glScreenHeight/2;

    float[] startMatrix = new float[]{glX, 0, glY, 1};//opengl coordinates that are not yet multiplied by a view or proj matrix. center = 0,0,0 top left -2.5f,0,-5f, bottom right 2.5f,0,5f

    float[] inverseMatrix = new float[16];
    Matrix.invertM(inverseMatrix, 0, viewProjMatrix, 0);//inverted 

    float[] result = new float[16];
    Matrix.multiplyMV(result, 0, inverseMatrix, 0, startMatrix, 0);

    float newX = (result[0]/result[3]);
    float newY = -(result[1]/result[3]);
    float newZ = (result[2]/result[3]);

    return new PointF(newX, newZ);
}

1 个答案:

答案 0 :(得分:0)

首先,您必须将窗口坐标转换为规范化的设备坐标。规范化的设备坐标在[-1,1]范围内:

float ndcX = 2.0f * (float)screenX / (float)screenWidth - 1.0f;
float ndcY = 1.0f - 2.0f * (float)screenY / (float)percentHeight;

投影矩阵从视图空间转换为剪辑空间。 Perspective divide将表单剪辑空间转换为规范化的设备空间。
要将标准化的设备空间转换为视图空间,您必须做相反的事情。通过逆投影矩阵进行转换,然后进行透视除法。
另请参见How to recover view space position given view space depth value and ndc xy

投影矩阵将场景中视图的3D点转换为视口上的2D点。您无法将2D点转换为3D点,因为缺少第3维。但是您可以变换2个点,即光线穿过视点的起点,终点和终点。 (ndcX, ndcY, -1)是近平面上光线的起点,(ndcX, ndcY, 1)是远平面上光线的终点。
请注意,在规范化的设备空间中,所有具有相同x和y坐标的点都在同一条射线上,并从摄影机位置开始。

要在世界空间中获得一个点,必须通过逆视图矩阵对其进行转换。注意,视图矩阵从世界空间转换为视图空间。

通过反视图和投影矩阵进行的变换可以像在问题代码中一样一步完成。

例如

float[] ndcNear = new float[]{ndcX, ndcY, -1, 1};
float[] ndcFar  = new float[]{ndcX, ndcY,  1, 1};

float[] inverseMatrix = new float[16];
Matrix.invertM(inverseMatrix, 0, viewProjMatrix, 0);//inverted 

float[] wNearH = new float[4];
float[] wFarH  = new float[4];
Matrix.multiplyMV(wNearH, 0, inverseMatrix, 0, ndcNear, 0);
Matrix.multiplyMV(wFarH,  0, inverseMatrix, 0, ndcFar,  0);

float nearX = wNearH[0] / wNearH[3];
float nearY = wNearH[1] / wNearH[3];
float nearZ = wNearH[2] / wNearH[3];

float farX = wFarH[0] / wFarH[3];
float farY = wFarH[1] / wFarH[3];
float farZ = wFarH[2] / wFarH[3];

在世界空间中,鼠标光标位于从(nearX, nearY, nearZ)(farX, farY, farZ)的光线中。

如果要找到射线与XZ平面的交点(这意味着y = 0,则可以通过简单的插值来完成:

float x0 = nearX + (farX-nearX) * -nearY / (farY-nearY));
float z0 = nearZ + (farZ-nearZ) * -nearY / (farY-nearY));