刚刚接触OpenGL ES一头雾水,完全不知道从什么地方入手。本文的OpenGL ES 为 OpenGL ES2.0,为什么要从2.0开始学习呢,因为后续的3.0版本兼容2.0语法基本相同,1.0已经成为过去式,2.0及以后版本不兼容1.0了。
为了方便学习与记忆。决定脚踏实地从浅入深的了解OpenGL ES,而不是直接从一个完整的项目一点一点讲解。既然是从浅入深的了解那么就要从最简单的例子开始了。
在开始之前还要简单做一些说明:
一、OpenGL ES的构成
OpenGL ES所有只有 点、线、三角形。所有图形都是由以上三种构成的。如果需要复杂的3D图形可以使用3D图形工具生成后使用。
二、OpenGL ES的坐标系
OpenGL ES坐标系有顶点坐标和纹理坐标看下图
定点坐标
纹理坐标
三、OpenGL ES的着色器
OpenGL ES有两个着色器:顶点着色器、片元着色器。
最简单的着色器代码
//顶点着色器代码 private final String vertex = "" + "attribute vec4 vPosition;" + "void main(){" + "gl_Position=vPosition;"+ "}";
//片元着色器代码 private final String fragment = "" + "precision mediump float;" + "uniform vec4 vColor;" + "void main(){" + "gl_FragColor=vColor;"+ "}";
着色器代码可以放在代码内部也可以定义在外部储存设备上的文件中,读取文件内容设置也可以。它们使用GLSL语言撰写,具体语法后面会详细说明。
下面开始一个简单的例子,使用OpenGL画三角形。
1、创建一个View命名为DGLView继承GLSurfaceView实现Renderer接口;
2、创建一个Renderer命名为DGLRender实现Renderer接口。
3、在activity_main.xml内增加
package pers.wtt.opengles10.view; import android.content.Context; import android.opengl.GLSurfaceView; import android.util.AttributeSet; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import pers.wtt.opengles10.render.DGLRender; /** * Created by WT on 2018/4/8. */ public class DGLView extends GLSurfaceView implements GLSurfaceView.Renderer{ private DGLRender dglRender; public DGLView(Context context) { super(context); init(); } public DGLView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { //初始化Renderer dglRender = new DGLRender(); //设置EGLContext为2.0 setEGLContextClientVersion(2); //设置render,绘制全靠它 setRenderer(dglRender); //设置render模式为只在绘制数据发生改变时才绘制view //此设置会阻止绘制GLSurfaceView的帧,直到你调用了requestRender(),这样会非常高效。 setRenderMode(RENDERMODE_WHEN_DIRTY); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { dglRender.onSurfaceCreated(gl, config); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { dglRender.onSurfaceChanged(gl, width, height); } @Override public void onDrawFrame(GL10 gl) { dglRender.onDrawFrame(gl); } }
package pers.wtt.opengles10.render; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; /** * Created by WT on 2018/4/8. */ public class DGLRender implements GLSurfaceView.Renderer { //顶点着色器代码 private final String vertex = "" + "attribute vec4 vPosition;" + "void main(){" + "gl_Position=vPosition;"+ "}"; //片元着色器代码 private final String fragment = "" + "precision mediump float;" + "uniform vec4 vColor;" + "void main(){" + "gl_FragColor=vColor;"+ "}"; //顶点坐标 private final float[] pos = { -0.5f,-0.5f,0.0f, 0.5f,0.5f,0.0f, 0.5f,-0.5f,0.0f, }; //颜色值 private final float[] colors = { 1.0f,0.0f,0.0f,1.0f }; //GL程序 int program; //定点坐标Buffer FloatBuffer vertexBuffer; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { //将背景设置为灰色,这里只是设置,并没有立即生效 GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f); //创建一个定点坐标Buffer,一个float为4字节所以这里需要 ByteBuffer byteBuffer = ByteBuffer.allocateDirect(pos.length*4); byteBuffer.order(ByteOrder.nativeOrder()); vertexBuffer = byteBuffer.asFloatBuffer(); vertexBuffer .put(pos); vertexBuffer .position(0); //装载顶点着色器和片元着色器,从source int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertex); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragment); //创建Opengl程序,获取程序句柄,为了方便onDrawFrame方法使用所以声明为成员变量 program = GLES20.glCreateProgram(); //激活着色器 GLES20.glAttachShader(program, vertexShader); GLES20.glAttachShader(program, fragmentShader); //链接程序 GLES20.glLinkProgram(program); } /** * 装载着色器从资源代码,需要检测是否生成成功,暂时不检测 * @param type 着色器类型 * @param source 着色器代码源 * @return 返回着色器句柄 */ private int loadShader(int type, String source) { int shader = 0; shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); return shader; } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { //当大小改变时重置视区大小 GLES20.glViewport(0,0, width, height); } @Override public void onDrawFrame(GL10 gl) { //清空缓冲区,与 GLES20.glClearColor(0.5f,0.5f,0.5f,1.0f);对应 GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT); //使用OpenGL程序 GLES20.glUseProgram(program); //获取顶点着色器变量vPosition int vPositionHandler = GLES20.glGetAttribLocation(program, "vPosition"); //允许使用顶点坐标数组 GLES20.glEnableVertexAttribArray(vPositionHandler); //第一个参数顶点属性的索引值 // 第二个参数顶点属性的组件数量。必须为1、2、3或者4,如position是由3个(x,y,z)组成,而颜色是4个(r,g,b,a)) // 第三个参数数组中每个组件的数据类型 // 第四个参数指定当被访问时,固定点数据值是否应该被归一化(GL_TRUE)或者直接转换为固定点值(GL_FALSE) // 第五个参数指定连续顶点属性之间的偏移量,这里由于是三个点 每个点4字节(float) 所以就是 3*4 // 第六个参数前面的顶点坐标数组 GLES20.glVertexAttribPointer(vPositionHandler, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); //获取片元着色器变量vColor int vColor = GLES20.glGetUniformLocation(program, "vColor"); GLES20.glUniform4fv(vColor, 1, colors, 0); //三角形绘制方式 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 3); //禁止使用顶点坐标数组 GLES20.glDisableVertexAttribArray(vPositionHandler); } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/sample_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!"/> <pers.wtt.opengles10.view.DGLView android:id="@+id/dglView" android:layout_width="match_parent" android:layout_height="match_parent" > </pers.wtt.opengles10.view.DGLView> </LinearLayout>
到这里就可以运行一下app看看效果了。运行结果图如下。