看完了Opengl ES 2.0,我们再来看一下3.0的内容,PDF下载地址:OPENGL ES 3.0编程指南 原书第2版(中文版),3.0原书中的作者应该是搞C++开发的,所有的实现都是基于C++,当然也有Java语言的,原书所有代码下载地址:OpenGL ES 3.0中文版所有示例代码,我自己只是将Java语言整理了一下。我们还是对着书,一节一节的分析所有的内容。本节要看的就是该书中第2章的内容,非常简单,就是画了一个纯色的三角形,效果如下:

Opengl ES系列学习--你好,三角形

     相信大家如果对前面所有学习能够掌握的话,要实现这样的一个三角形应该来说已经非常简单了。它对应的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就完成了,这就是为什么绘制出来的三角形是红色的原因了。

     该程序非常简单,相信有了之前的基础,我们完全可以自己从零完成这样的功能,好了,让我们继续!!

相关文章: