在 OpenGL 3.0 中绘制时将像素坐标转换为归一化坐标

Converting pixel co-ordinates to normalized co-ordinates at draw time in OpenGL 3.0

我在 OpenGL 中绘制一个三角形,如下所示:

            MyGLRenderer( )
            {
                fSampleVertices = ByteBuffer.allocateDirect( fSampleVerticesData.length * 4 )
                        .order ( ByteOrder.nativeOrder( ) ).asFloatBuffer( );

                fSampleVertices.put( fSampleVerticesData ).position ( 0 );

                Log.d( TAG, "MyGLRender( )" );
            }

            private FloatBuffer fSampleVertices;

            private final float[] fSampleVerticesData =
                    { .8f, .8f, 0.0f, -.8f, .8f, 0.0f, -.8f, -.8f, 0.0f };

            public void onDrawFrame( GL10 unused )
            {
                GLES30.glViewport ( 0, 0, mWidth, mHeight );

                GLES30.glClear ( GLES30.GL_COLOR_BUFFER_BIT );

                GLES30.glUseProgram ( dProgramObject1 );

                GLES30.glVertexAttribPointer ( 0, 3, GLES30.GL_FLOAT, false, 0, fSampleVertices );

                GLES30.glEnableVertexAttribArray ( 0 );

                GLES30.glDrawArrays( GLES30.GL_TRIANGLES, 0, 3 );

                //Log.d( TAG, "onDrawFrame( )" );
            }

因此,由于我已经对坐标进行了试验,所以很快就可以弄清楚屏幕的可见区域 在-1,1之间。所以三角形占据了屏幕的 80%。我也确定了我的像素尺寸 GLSurfaceView 宽度为 2560,高度为 1600。

然后给出一个具有这些基于像素的坐标 (fBoardOuter) 的三角形:

    1112.0f
    800.0f
    0.0f
    -1280.0f
    800.0f
    0.0f
    -1280.0f
    -800.0f
    0.0f

我必须要么将这些像素坐标转换为 -1,1 之间的值,要么找到一种方法让 gl 转换这些坐标 在他们被画的时候?由于我是 OpenGL 的新手,我正在寻找一些指导来做到这一点?

我的顶点着色器是这样的:

    String sVertexShader1 =
              "#version 300 es              \n"
            + "in vec4 vPosition;           \n"
            + "void main()                  \n"
            + "{                            \n"
            + "   gl_Position = vPosition;  \n"
            + "}                            \n";

那么我说基于像素的系统将被称为世界坐标是否正确?我现在想做的只是一些棋盘游戏的二维绘图。


我发现Android有这个功能:

    orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)

然而,到目前为止,我读过的文档中没有任何内容解释矩阵的用法,即如何在 GLES30 中将具有像素坐标的 float[] 转换为具有该矩阵的归一化坐标。

我还在此处找到了文档:

http://developer.android.com/guide/topics/graphics/opengl.html

根据我尝试创建示例的文档:

http://pastebin.com/5PTsfSdz

在 pastebin 示例中,我认为 fSampleVertices 会小得多并且位于屏幕的中央,但实际上它仍然几乎是整个屏幕,如果我尝试将它放入 glDrawArray 中,fBoardOuter 只会显示黑屏.

您可能需要找一本书或一些好的教程来深入掌握其中的一些概念。但是由于您的问题中有一些特定的项目,我将尝试以这种格式尽可能地解释它们。

你发现的坐标系,x、y坐标方向取值范围为[-1.0, 1.0],正式名称为Normalized Device Coordinates,常简称为NDC。这与您想出的名称非常相似,因此一些 OpenGL 术语实际上非常合乎逻辑。 :)

至少只要您处理的是二维坐标,这就是您的顶点着色器需要生成的坐标范围。 IE。您分配给内置 gl_Position 变量的坐标需要在此范围内才能在输出中可见。如果您处理 3D 坐标并应用透视投影,事情会稍微复杂一些,但我们现在将跳过该部分。

现在,正如您已经猜到的那样,如果您想在不同的坐标系中指定坐标,您有两个主要选择:

  1. 在将它们传递给 OpenGL 之前,您可以在代码中将它们转换为 NDC。
  2. 您已让 OpenGL 将变换应用于您的输入坐标。

选项 2 显然更好,因为 GPU 在执行这项工作时非常高效。

在非常简单的层面上,这意味着您修改顶点着色器中的坐标。如果您查看非常简单的第一个顶点着色器:

in vec4 vPosition;
void main()
{
    gl_Position = vPosition;
}

您在 vPosition 输入变量中获得应用程序代码提供的坐标,并将完全相同的坐标分配给顶点着色器输出 gl_Position.

如果您想使用不同的坐标系,您可以在顶点着色器代码中处理输入坐标,并将这些处理后的坐标分配给输出。

现代版本的 OpenGL 不再真正为这些坐标系命名。曾经有 "model coordinates" 和 "world coordinates" 时,其中一些东西仍然硬连接到固定管道中。现在这是通过可编程着色器代码完成的,从 OpenGL 的角度来看,这些概念不再相关。它只关心顶点着色器的坐标。在那之前发生的一切都是你自己的事。

应用线性变换(包括预期用途所需的平移和缩放)的规范方法是将坐标与变换矩阵相乘。如果您不想自己编写(简单的)代码,您已经发现了 android.opengl.Matrix 包,其中包含一些用于构建转换矩阵的实用函数。

一旦有了转换矩阵,就可以将其作为 uniform 变量传递给顶点着色器,并在着色器代码中应用该矩阵。这在着色器代码中的样子是例如:

in vec4 vPosition;
uniform mat4 TransformMat;
void main()
{
    gl_Position = TransformMat * vPosition;
}

要设置此矩阵的值,您需要在链接着色器后获取统一变量的位置一次,prog您的着色器程序:

GLint transformLoc = GLES20.glGetUniformLocation(prog, "TransformMat");

然后,至少一次,每次你想改变矩阵,你调用:

GLES20.glUniformMatrix4fv(transformLoc, 1, GL_FALSE, mat, 0);

其中 mat 是您自己构建的矩阵,或者是从 android.opengl.Matrix 中的实用函数之一获得的矩阵。请注意,此调用需要 使程序成为当前 glUseProgram().

之后