看完了Opengl ES 2.0,我们再来看一下3.0的内容,PDF下载地址:OPENGL ES 3.0编程指南 原书第2版(中文版),3.0原书中的作者应该是搞C++开发的,所有的实现都是基于C++,当然也有Java语言的,原书所有代码下载地址:OpenGL ES 3.0中文版所有示例代码,我自己只是将Java语言整理了一下。我们还是对着书,一节一节的分析所有的内容。本节要看的就是该书中第2章的内容,非常简单,就是画了一个纯色的三角形,效果如下:
相信大家如果对前面所有学习能够掌握的话,要实现这样的一个三角形应该来说已经非常简单了。它对应的Activity是Gl3Activity,代码很简单,我们就不分析了,来看一下HelloTriangleRenderer渲染类,源码如上:
public class HelloTriangleRenderer implements GLSurfaceView.Renderer {
private Context mContext;
///
// Constructor
//
public HelloTriangleRenderer(Context context) {
mContext = context;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
}
///
// Create a shader object, load the shader source, and
// compile the shader.
//
private int LoadShader(int type, String shaderSrc) {
int shader;
int[] compiled = new int[1];
// Create the shader object
shader = GLES30.glCreateShader(type);
if (shader == 0) {
return 0;
}
// Load the shader source
GLES30.glShaderSource(shader, shaderSrc);
// Compile the shader
GLES30.glCompileShader(shader);
// Check the compile status
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "compile shader error: " + GLES30.glGetShaderInfoLog(shader));
GLES30.glGetError();
GLES30.glDeleteShader(shader);
return 0;
}
Log.i(TAG, "load " + type + " shader result: " + shader);
return shader;
}
///
// Initialize the shader and program object
//
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
String vShaderStr = ESShader.readShader(mContext, "chapter2/vertexShader.vert");
String fShaderStr = ESShader.readShader(mContext, "chapter2/fragmentShader.frag");
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the vertex/fragment shaders
vertexShader = LoadShader(GLES30.GL_VERTEX_SHADER, vShaderStr);
fragmentShader = LoadShader(GLES30.GL_FRAGMENT_SHADER, fShaderStr);
// Create the program object
programObject = GLES30.glCreateProgram();
if (programObject == 0) {
return;
}
GLES30.glAttachShader(programObject, vertexShader);
GLES30.glAttachShader(programObject, fragmentShader);
// Bind vPosition to attribute 0
GLES30.glBindAttribLocation(programObject, 0, "vPosition");
// Link the program
GLES30.glLinkProgram(programObject);
// Check the link status
GLES30.glGetProgramiv(programObject, GLES30.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e(TAG, "Error linking program:");
Log.e(TAG, GLES30.glGetProgramInfoLog(programObject));
GLES30.glDeleteProgram(programObject);
return;
}
// Store the program object
mProgramObject = programObject;
GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
}
// /
// Draw a triangle using the shader pair created in onSurfaceCreated()
//
@Override
public void onDrawFrame(GL10 glUnused) {
// Set the viewport
GLES30.glViewport(0, 0, mWidth, mHeight);
// Clear the color buffer
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
// Use the program object
GLES30.glUseProgram(mProgramObject);
// Load the vertex data
GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mVertices);
GLES30.glEnableVertexAttribArray(0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
}
// /
// Handle surface changes
//
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
mWidth = width;
mHeight = height;
}
// Member variables
private int mProgramObject;
private int mWidth;
private int mHeight;
private FloatBuffer mVertices;
private static String TAG = "HelloTriangleRenderer";
private final float[] mVerticesData =
{0.0f, 0.5f, 0.0f, -1f, -0.8f, 0.0f, 1f, -0.6f, 0.0f};
}
继承父类GLSurfaceView.Renderer,必须实现onSurfaceCreated、onSurfaceChanged、onDrawFrame三个接口方法,先来看一下成员变量。mContext上下文变量是用来加载着色器程序时使用的,在ES 2.0中,我们是将着色器程序放在res/raw目录下,而这里是放在assets目录下,调用context.getAssets().open(fileName)系统API来加载着色器程序,都是一样的道理;mProgramObject存储编译完成的着色器程序ID;mWidth、mHeight表示当前surface窗口的宽高;mVertices将原始的顶点数组数据转换为Buffer存储;mVerticesData就是最原始的顶点数据了,它当中有九个值,因为我们要画三角形,每个顶点三个坐标,所以一共九个,从坐标的位置,我们也就可以想到三角形的形状了。
构造方法中对mVertices进行初始化,并将mVerticesData顶点原始数据存储进来;LoadShader方法和我们之前分析ES 2.0的逻辑完全相同,我们就不展开分析了;onSurfaceCreated、onSurfaceChanged方法中的逻辑也是Opengl ES的标准流程,和我们之前2.0的相同,直接跳过;我们简单看一下onDrawFrame方法的实现。
首先调用GLES30.glViewport指定视口,然后GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)清屏,GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, mVertices)指定顶点数据的取值方式,因为顶点数组中只存储了位置,所以跨距stride为0,第一个参数是要指定的属性ID,就是我们在着色器程序中定义的vPosition,这里没有主动获取它的ID值,虽然程序可以正常工作,但是这种方式不友好,让人感觉困惑,应该修改为
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
GLES30.glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
vPosition = GLES30.glGetAttribLocation(mProgramObject, "vPosition");
Log.i(TAG, "vPosition: " + vPosition);
}
// /
// Draw a triangle using the shader pair created in onSurfaceCreated()
//
@Override
public void onDrawFrame(GL10 glUnused) {
// Load the vertex data
GLES30.glVertexAttribPointer(vPosition, 3, GLES30.GL_FLOAT, false, 0, mVertices);
}
private FloatBuffer mVertices;
private int vPosition;
我们删除掉了一些不相关的代码,增加了vPosition成员变量,给顶点属性赋值时,应该指定它,这样代码的可读性更好;最后调用GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3)绘制三角形。
顶点着色器的源码如下:
#version 300 es
in vec4 vPosition;
void main()
{
gl_Position = vPosition;
}
非常简单,第一行必须声明Opengl ES的版本,然后接收顶点数据,并赋值给内建变量gl_Position就完成了。
片段着色器源码如下:
#version 300 es
precision mediump float;
out vec4 fragColor;
void main()
{
fragColor = vec4 (1.0, 0.0, 0.0, 0.5);
}
第一行仍然要声明版本,第二行声明精度,这一点和2.0是相同的,main函数中创建红色、透明度为50%的色值赋值给输出变量fragColor就完成了,这就是为什么绘制出来的三角形是红色的原因了。
该程序非常简单,相信有了之前的基础,我们完全可以自己从零完成这样的功能,好了,让我们继续!!