在 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
根据我尝试创建示例的文档:
在 pastebin 示例中,我认为 fSampleVertices 会小得多并且位于屏幕的中央,但实际上它仍然几乎是整个屏幕,如果我尝试将它放入 glDrawArray 中,fBoardOuter 只会显示黑屏.
您可能需要找一本书或一些好的教程来深入掌握其中的一些概念。但是由于您的问题中有一些特定的项目,我将尝试以这种格式尽可能地解释它们。
你发现的坐标系,x、y坐标方向取值范围为[-1.0, 1.0],正式名称为Normalized Device Coordinates,常简称为NDC。这与您想出的名称非常相似,因此一些 OpenGL 术语实际上非常合乎逻辑。 :)
至少只要您处理的是二维坐标,这就是您的顶点着色器需要生成的坐标范围。 IE。您分配给内置 gl_Position
变量的坐标需要在此范围内才能在输出中可见。如果您处理 3D 坐标并应用透视投影,事情会稍微复杂一些,但我们现在将跳过该部分。
现在,正如您已经猜到的那样,如果您想在不同的坐标系中指定坐标,您有两个主要选择:
- 在将它们传递给 OpenGL 之前,您可以在代码中将它们转换为 NDC。
- 您已让 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()
.
之后
我在 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
根据我尝试创建示例的文档:
在 pastebin 示例中,我认为 fSampleVertices 会小得多并且位于屏幕的中央,但实际上它仍然几乎是整个屏幕,如果我尝试将它放入 glDrawArray 中,fBoardOuter 只会显示黑屏.
您可能需要找一本书或一些好的教程来深入掌握其中的一些概念。但是由于您的问题中有一些特定的项目,我将尝试以这种格式尽可能地解释它们。
你发现的坐标系,x、y坐标方向取值范围为[-1.0, 1.0],正式名称为Normalized Device Coordinates,常简称为NDC。这与您想出的名称非常相似,因此一些 OpenGL 术语实际上非常合乎逻辑。 :)
至少只要您处理的是二维坐标,这就是您的顶点着色器需要生成的坐标范围。 IE。您分配给内置 gl_Position
变量的坐标需要在此范围内才能在输出中可见。如果您处理 3D 坐标并应用透视投影,事情会稍微复杂一些,但我们现在将跳过该部分。
现在,正如您已经猜到的那样,如果您想在不同的坐标系中指定坐标,您有两个主要选择:
- 在将它们传递给 OpenGL 之前,您可以在代码中将它们转换为 NDC。
- 您已让 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()
.