Android OpenGL顶点着色器

来源:互联网 发布:淘宝网购电影票 编辑:程序博客网 时间:2024/04/27 14:10

Android OpenGL顶点着色器

首先申明下,本文为笔者学习《OpenGL ES应用开发实践指南》的笔记,并加入笔者自己的理解和归纳总结。

1、OpenGL坐标

OpenGL会把屏幕映射到[-1, 1]的范围内。

在OpenGL里,只能绘制点、直线以及三角形。三角形一般以逆时针顺序排列顶点。定义一个长方形,可以用两个三角形拼接而成。

2、数据存储

由于OpenGL运行在本地环境,而android运行在Davik虚拟机上,需要把android中的数据复制到本地内存块。
FloatBuffer类可以用来保存顶点数据。

private static final int BYTES_PER_FLOAT = 4;float[] tableVerticesWithTriangles;// ByteBuffer.allocateDirect分配一块本地内存,不被垃圾回收// 每个float类型有4个字节,本地内存的大小是tableVerticesWithTriangles.length * BYTES_PER_FLOAT// order方法是确保按本地字节序排列保存// put方法把数据从Davik的内存复制到本地内存FloatBuffer vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexData.put(tableVerticesWithTriangles);

3、着色器文件

(1) 顶点着色器,生成每个顶点的最终位置,针对每个顶点,它都会执行一次。
simple_vertex_shader.glsl文件

attribute vec4 a_Position;void main(){    gl_Position = a_Position;    gl_PointSize = 10.0;}
vec4是包含4个分量的向量,可以认为是x,y,z,w,默认情况下,前三个坐标设为0,并把最后一个坐标设为1。
着色器会有几个属性,关键字attribute是把这些属性放进着色器的手段。
main()是着色器的主要入口点,把前面定义过的位置复制到指定的输出变量gl_Position。

(2) 片段着色器,为组成点、线或者三角形的每个片段生成最终的颜色。

OpenGL把每个点、直线及三角形分解成大量的小片段,这些片段类似于屏幕上的像素,每一个都包含单一的纯色。对于每个片段,片段着色器都会被调用一次。
simple_fragment_shader.glsl文件

precision mediump float;uniform vec4 u_Color;void main(){                        gl_FragColor = u_Color;}
precision指定浮点数据类型精度,lowp、mediump和highp分别对应低精度、中等精度和高精度。
uniform会让每个顶点都使用一个值,u_Color是4个分量的向量,对应红、绿、蓝和透明度。
main()是片段着色器的主要入口点,把uniform定义的颜色复制到指定的输出变量gl_FragColor,作为当前片段的最终颜色。

4、绘制着色器

(1) 加载着色器代码
protected String readShaderFromRaw(int resId) {BufferedReader br = null;StringBuffer stringBuffer = new StringBuffer();try {br = new BufferedReader(new InputStreamReader(getResources().openRawResource(resId)));String line = null;while ((line = br.readLine()) != null) {stringBuffer.append(line + "\n");}} catch (IOException e) {} finally {if (br != null) {try {br.close();} catch (IOException e) {}}}return stringBuffer.toString();}
(2) 编译着色器代码
protected int compileShader(int type, String shaderCode) {// 创建一个新的着色器对象int shaderObjectId = GLES20.glCreateShader(type);if (shaderObjectId == 0) {// 创建失败return 0;}// 上传和编译着色器代码GLES20.glShaderSource(shaderObjectId, shaderCode);GLES20.glCompileShader(shaderObjectId);// 获取编译状态int[] compileStatus = new int[1];GLES20.glGetShaderiv(shaderObjectId, GLES20.GL_COMPILE_STATUS, compileStatus, 0);// 获取着色器信息日志LogUtil.log("OpenGL", GLES20.glGetShaderInfoLog(shaderObjectId));if (compileStatus[0] == 0) {// 如果失败,删除着色器对象GLES20.glDeleteShader(shaderObjectId);return 0;}return shaderObjectId;}
(3) 链接程序对象
protected int linkProgram(int vertexShaderId, int fragmentShaderId) {// 创建一个新的程序对象int programId = GLES20.glCreateProgram();if (programId == 0) {return 0;}// 新建程序对象附上着色器,并链接程序GLES20.glAttachShader(programId, vertexShaderId);GLES20.glAttachShader(programId, fragmentShaderId);GLES20.glLinkProgram(programId);// 获取链接状态int[] linkStatus = new int[1];GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0);// 获取着程序链接信息日志LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));if (linkStatus[0] == 0) {// 如果链接失败,删除程序对象GLES20.glDeleteProgram(programId);return 0;}return programId;}
(4) 验证程序对象
protected boolean validateProgram(int programId) {// 验证程序,只在开发阶段需要GLES20.glValidateProgram(programId);LogUtil.log("OpenGL", GLES20.glGetProgramInfoLog(programId));int[] validateStatus = new int[1];GLES20.glGetProgramiv(programId, GLES20.GL_VALIDATE_STATUS, validateStatus, 0);return validateStatus[0] != 0;}
(5) 使用程序对象
protected int useProgram(int vertexShaderResId, int fragmentShaderResId) {String vertexShaderCode = readShaderFromRaw(vertexShaderResId);String fragmentShaderCode = readShaderFromRaw(fragmentShaderResId);int vertexShaderId = compileShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);int fragmentShaderId = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);int programId = linkProgram(vertexShaderId, fragmentShaderId);validateProgram(programId);GLES20.glUseProgram(programId);return programId;} 
(6) 获取Uniform位置
private static final String U_COLOR = "u_Color";uColorLocation = GLES20.glGetUniformLocation(mProgramId, U_COLOR);
(7) 获取Attribute位置
private static final String A_POSITION = "a_Position";aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);
(8) 关联顶点数据
vertexData.position(0);GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,GLES20.GL_FLOAT, false, 0, vertexData);
glVertexAttribPointer(int indx, int size, int type, boolean normalized,
                int stride, java.nio.Buffer ptr) 
  • index是属性位置
  • size是对每个属性的计数
  • type是数据的类型
  • normalized只对整型数据有意义
  • stride只有当一个数组存储多于一个属性时,指定位移
  • ptr是缓存数据
(9) Renderer对象
private class OpenGLVertexShaderRender implements GLSurfaceView.Renderer {private static final String U_COLOR = "u_Color";private static final String A_POSITION = "a_Position";private static final int POSITION_COMPONENT_COUNT = 2;private static final int BYTES_PER_FLOAT = 4;private FloatBuffer vertexData;private int mProgramId;private int uColorLocation;private int aPositionLocation;OpenGLVertexShaderRender() {float[] tableVerticesWithTriangles = {// 左上角-0.5f, -0.5f,0.5f,  0.5f,-0.5f,  0.5f,// 右下角-0.5f, -0.5f,0.5f, -0.5f,0.5f,  0.5f,// 直线-0.5f, 0f,0.5f, 0f,// 点0f, -0.25f,0f,  0.25f};// ByteBuffer.allocateDirect分配一块本地内存,不被垃圾回收,保存到FloatBuffer中vertexData = ByteBuffer.allocateDirect(tableVerticesWithTriangles.length * BYTES_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexData.put(tableVerticesWithTriangles);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);mProgramId = useProgram(R.raw.simple_vertex_shader, R.raw.simple_fragment_shader);// 获取Uniform的位置uColorLocation = GLES20.glGetUniformLocation(mProgramId, U_COLOR);// 获取Attribute位置aPositionLocation = GLES20.glGetAttribLocation(mProgramId, A_POSITION);vertexData.position(0);// 关联顶点数据GLES20.glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT,GLES20.GL_FLOAT, false, 0, vertexData);GLES20.glEnableVertexAttribArray(aPositionLocation);}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {GLES20.glViewport(0, 0, width, height);}@Overridepublic void onDrawFrame(GL10 gl) {GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);// 绘制三角形,白色背景GLES20.glUniform4f(uColorLocation, 1, 1, 1, 1);GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);// 绘制直线,红色背景GLES20.glUniform4f(uColorLocation, 1, 0, 0, 1);GLES20.glDrawArrays(GLES20.GL_LINES, 6, 2);// 绘制点,蓝色背景GLES20.glUniform4f(uColorLocation, 0, 0, 1, 1);GLES20.glDrawArrays(GLES20.GL_POINTS, 8, 1);// 绘制点,红色背景GLES20.glUniform4f(uColorLocation, 1, 0, 0, 1);GLES20.glDrawArrays(GLES20.GL_POINTS, 9, 1);}}
显示如下


原创粉丝点击