坐标系错误,opengl es系统转屏android

Coordinate system error, opengl es system to screen android

我正在尝试将 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

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

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

你错过了关键的一步。当您将 ModelViewProjection 矩阵乘以某个向量时,您将此向量从对象 space 转换为剪辑 space.

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

但是,仅添加它可能无法完全解决您的问题(取决于场景)。问题是 GL 在从剪辑 space 到 NDC 之前会做一些其他事情:clipping,这与图元相交(并且此操作适用于整个图元,而不是显然)将顶点与查看体积分开(此时只是 [-w,w]^3,只是这个 w 每个顶点都不同)。

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