【问题标题】:Coordinate system error, opengl es system to screen android坐标系错误,opengl es系统屏幕android
【发布时间】:2015-04-19 13:37:21
【问题描述】:

我正在尝试采用 opengl es 坐标系并将其转换为屏幕坐标系。这将使我能够在屏幕上找到顶点位置,并使碰撞检测更容易。到目前为止,我已经找到了顶点并将它们乘以模型视图投影矩阵,然后使用 android 屏幕的高度和宽度(以像素为单位),我已经将顶点转换为正确的范围。我必须进行转换,因为opengl es坐标系的原点位于屏幕的中心并且从-1变为1。而屏幕的原点来自屏幕的左下角,并且当设备处于横向模式时我'我们发现尺寸为 800x480 像素。

这就是问题所在,当我查看一个顶点在屏幕上可以具有的值范围时,它的范围约为 147 - 640 宽度和 185 - 480 高度。它的范围应为 0-800 宽度和 0-480 高度。

我的代码有什么问题?是模型视图投影矩阵,还是我使用了错误的屏幕测量值,或者是我从 opengl es 范围转换为屏幕像素范围的方式。

这会找到形状顶点,将其乘以 mvp 矩阵并转换值的范围,以便它们具有像素屏幕尺寸。我只检查一个形状顶点。

        dimension[0] = MyGLSurfaceView.width;
        dimension[1] = MyGLSurfaceView.height;

        float starW;
        float starH;

        for (int i = 0; i < star.vertices.length; i += star.vertices.length) {//only checking one vertex

            Matrix.multiplyMM(starVerts, 0, mMVPMatrix, 0, star.vertices, 0);//vertices multiplied by model view projection matrix

           //starVerts[i] is in the range .433 to -.466 should be 1 to -1
           //starVert[i+1] is in the range .973 to -.246  should be 1 to -1

            starW = (starVerts[i] * (dimension[0] / 2)) + (dimension[0] / 2);//should be range 0-800 // instead 147 - 640
            starH = (starVerts[i + 1] * (dimension[1] / 2)) + (dimension[1] / 2);//should be range 0-480 // instead 185 - 480

这就是我找到 mvp 矩阵的方法

        @Override
public void onSurfaceChanged(GL10 unused, int width, int height) {

    GLES20.glViewport(0, 0, width, height);// Sets the current view port to the new size.

    float RATIO = (float) width / height;
    Matrix.frustumM(mProjectionMatrix, 0, -RATIO, RATIO, -1, 1, 3, 7);// this projection matrix is applied to object coordinates in the onDrawFrame() method
}

@Override
public void onDrawFrame(GL10 unused) {
    Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);// Set the camera position (View matrix)
    Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);// Calculate the projection and view transformation

这就是我找到安卓屏幕尺寸(以像素为单位)的方法

Display display = ((WindowManager)
                context.getSystemService(Context.WINDOW_SERVICE))
                .getDefaultDisplay();
        Point size = new Point();
        display.getSize(size);
        height = size.y;
        width = size.x;

自derhass回答后更新

starVerts[i] = starVerts[i]/starVerts[i+3];    //clip.x divided by clip.w
starVerts[i+1] = starVerts[i+1]/starVerts[i+3];//clip.y divided by clip.w

【问题讨论】:

    标签: android opengl-es coordinates


    【解决方案1】:

    您错过了一个关键步骤。当您将 ModelViewProjection 矩阵乘以某个向量时,您会将这个向量从对象空间转换到剪辑空间。

    GL 的下一步是转换为标准化的设备坐标,其中查看体积是所有 3 个维度的单位立方体 [-1,1]。为了从剪辑空间到标准化设备空间,剪辑空间x,yz值除以剪辑空间w值(这最终创建了透视效果,转换链中的所有其他操作只是线性的)。这是你忘记的步骤。

    但是,仅添加它可能无法完全解决您的问题(取决于场景)。问题是 GL 在从裁剪空间转到 NDC 之前会做一些其他事情:裁剪,即与图元相交(并且此操作显然适用于整个图元,而不是单独的顶点)与观看量(此时只是 [-w,w]^3,只是这个 w 因顶点而异)。

    如果你只是想处理单点,你可以检查关系 -w x,y,z w 是否满足。如果没有,你可以放弃这一点。但是如果你处理线条或三角形,事情就会变得更加复杂。您基本上必须实现完整的裁剪,否则如果您正在绘制的图元具有至少一个位于相机前面的顶点并且至少在 后面 的顶点上,您将获得毫无意义的数据相机(或恰好在相机平面上,这将产生w=0 和除以零)。如果您不处理裁剪,则生成的实际片段可能位于通过投影图元的所有原始顶点所跨越的 2D 边界框之外

    【讨论】:

    • 感谢您的帮助。因此,如果我是对的,我的 starVerts[] 变量是剪辑空间值“x”、“y”、“z”。你知道如何计算剪辑空间'w'值吗?
    • @toom:我不知道你的矩阵和向量函数是如何工作的,但一般来说,GL 使用 4x4 矩阵和 4d 向量,其中最后一个分量是 w(通常是输入上的 1边)。裁剪空间w 自然是一个 4x4 矩阵乘以一个 4d 向量的结果。通常,透视矩阵只具有设置w_clip = -z_eye 的效果(投影矩阵的最后一行是(0,0,-1,0))。
    • 嘿兄弟谢谢你的回答。它帮助我了解发生了什么,我能够找到“clip.w”。你的意思是透视矩阵的最后一行吗?
    • 嗯,对于透视投影,projection 矩阵的最后一行通常是 (0,0,-1,0)(或者实际上,在一般情况下,只是图像平面的法线)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-04
    相关资源
    最近更新 更多