Android开发OpenGL ES的流程,从着色器编写到显示在屏幕上

创建顶点着色器和片段着色器

顶点着色器代码示例:

attribute vec4 a_Position;

void main()
{
    gl_Position = a_Position;
}

gl_Position为当前顶点的最终位置

片段着色器代码示例:

precision mediump float;

uniform ver4 u_Color;

void main()
{
    gl_FragColor = u_Color;
}

gl_FragColor为当前片段最终输出的颜色

编译着色器

因为有顶点着色器和片段着色器,所以需要编译两次

  • 创建着色器对象,type值为GL_VERTEX_SHADER和GL_FRAGMENT_SHADER,分别代表顶点着色器和片段着色器类型
final int shaderObjectId = glCreateShader(type);
  • 加载着色器代码,shaderCode是前面写的顶点着色器和片段着色器代码,分别将其转换成String类型即可
glShaderSource(shaderObjectId, shaderCode);
  • 编译着色器
glCompileShader(shaderObjectId);

现在顶点着色器和片段着色器都编译好了,我们之后只需要通过shaderObjectId来调用对应的着色器对象即可使用。

将着色器连接到OpenGL ES的program中

前面创建好了顶点着色器对象和片段着色器对象,使用时两者需要一起工作,OpenGL ES就是通过program来将两者链接到一起

链接program的过程和编译着色器的过程很类似,如下:

  • 创建program对象
final int programObjectId = glCreateProgram();
  • 附上着色器,vertexShaderId和fragmentShaderId就是前面编译生成的着色器对象的ID
glAttachShader(programObjectId, vertexShaderId);
glAttachShader(programObjectId, fragmentShaderId);
  • 链接program
glLinkProgram(programObjectId);
  • 告诉OpenGL ES使用此program程序
glUseProgram(programObjectId);

到这里OpenGL ES绘制任何东西到屏幕上都是使用此program程序,这时你问了那我们是怎么设置顶点坐标和片段颜色的呢?

设置顶点坐标和片段颜色

我们在顶点着色器和片段着色器中分别定义了一下两个变量a_Position和u_Color,顶点着色器设置当前顶点的最终坐标位置gl_Position = a_Position;片段着色器设置当前片段的最终颜色gl_FragColor = u_Color;当OpenGL ES把两个着色器链接成一个程序时,会给所有的变量都分配一个位置,我们需要获取这些位置来设置顶点坐标和片段颜色。

  • 首先获取顶点着色器的a_Position位置

我们在Renderer类中定义两个实例变量,一个是所需获取的着色器中的变量名,一个是代表此变量在program中的位置

private static final String A_POSITION = "a_Position";
private int aPositionLocation;

然后在Renderer的onSurfaceCreated()方法中获取位置

aPositionLocation = glGetAttribLocation(program, A_POSITION);
  • 获取片段着色器的u_Color位置

同样在Renderer类中定义两个实例变量

private static final String U_COLOR = "u_Color";
private int uColorLocation;

然后在Renderer的onSurfaceCreated()方法中获取位置

uColorLocation = glGetUniformLocation(program, U_COLOR);

现在我们获取到了所需属性的位置,通过这个位置我们就可以设置OpenGL ES的顶点坐标和片段颜色了

  • 定义顶点数据缓冲区,我们先画个三角形

这里是每两个数据代表一个顶点的(x,y)坐标,屏幕坐标系如下,屏幕中间为原点,(-1, -1)是屏幕的左下角,(1, -1)是右下角,(0, 1)在中上位置

float[] triangleVertices = {
    -1.0f, -1.0f,
    1.0f, -1.0f,
    0.0f, 1.0f
};

因为OpenGL ES作为本地系统库直接运行中硬件上的,所以我们的顶点数据也希望不能被垃圾回收,因此我们使用jni方式,但是为了方便我们还可以使用另一种方式NIO,

private final FloatBuffer vertexData;
vertexData = ByteBuffer
    .allocateDirect(triangleVertices.length * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer();
vertexData.put(triangleVertices);

现在顶点数组就会从虚拟机拷贝到本地内存中,不会受到垃圾回收,当然当进程结束时这块内存会被释放掉。

关联顶点数据和顶点属性

现在顶点数据和OpenGL ES中的顶点位置都有了,我们还需要将两者关联起来,也就是让OpenGL ES找到顶点坐标的最终位置。

我们在Renderer的onSurfaceCreated方法中加入以下代码:

//让vertexData内部的指针指向数据的开头
vertexData.position(0);处,否则可能有bug
//aPositionLocation代表对应OpenGL ES中的属性位置,2代表这个属性每次读取几个数据,GL_FLOAT代表数据类型,false表示我们暂时忽略此参数,0表示跨度,这里就连着读取数据,vertexData就是告诉OpenGL ES去哪读数据
glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, 0, vertexData);

注意这个2,它是表示每次从vertexData读两个数据表示一个顶点,但是前面a_Position的类型是vec4,也就说每个顶点需要四个分量,而我们只传了2个数据,那么剩下的两个分量会使用默认值(0,1),所以最后顶点的坐标是(-1,-1,0,1),(1,-1,0,1),(0,1,0,1),对于0我觉得比较好理解,就是Z轴为0,但是最后的是1怎么说呢,它是用来做透视除法的,以后再细说,现在知道默认为1就行

现在OpenGL ES的program程序已经和顶点数据关联起来了,现在还需要使能顶点数据,也就是让OpenGL ES开始更新顶点数据

在glVertexAttribPointer后面加入下述代码

glEnableVertexAttribArray(aPositionLocation);

设置片段着色器输出的颜色,也就是三角形的颜色

后面四个参数代表RGBA,此例为红色

在onDrawFrame()中加入下述代码

glUniform4f(uColorLocation, 1.0f, 0.0f, 0.0f, 0.0f);

最后绘制出来三角形

在onDrawFrame()尾部加入下述代码

//GL_TRIANGLES代表绘制三角形,GL_POINTS代表绘制点,GL_LINES代表绘制直线,0表示从顶点数组的起始位置读顶点,3表示读3个顶点,因为一个三角形有三个顶点
glDrawArrays(GL_TRIANGLES, 0, 3);

现在运行起来屏幕上就会显示红色三角形了

猜你喜欢

转载自blog.csdn.net/lb377463323/article/details/63684087