如何将OpenGL ES代码从顶点数组重构为顶点数组对象(VAO)实现
VAO工作原理讲解与代码重构指导
VAO核心工作原理
VAO(顶点数组对象)就是个顶点属性配置的状态容器。你之前在onDrawFrame里每次绘制都要重复调用glVertexAttribPointer、glEnableVertexAttribArray来设置顶点位置、法线的属性,这些配置属于OpenGL的全局状态。用VAO的话,只需要在初始化阶段把这些配置一次性存到VAO里,之后每次绘制只要绑定这个VAO,就能自动恢复所有顶点属性的配置,不用再重复写冗余代码。
代码重构步骤
1. 添加VAO成员变量
在类的私有成员变量中新增:
private int mVAO;
2. 在onSurfaceCreated中初始化VAO
把原来onDrawFrame里的顶点属性配置逻辑移到这里,让VAO记录这些状态:
// 初始化VAO int[] vaoIds = new int[1]; GLES30.glGenVertexArrays(1, vaoIds, 0); mVAO = vaoIds[0]; // 绑定VAO,此时后续的顶点属性配置都会被记录到这个VAO里 GLES30.glBindVertexArray(mVAO); // 设置顶点位置属性(原onDrawFrame中的代码移到这里) GLES30.glVertexAttribPointer(0, 4, GLES30.GL_FLOAT, false, 0, mVertices); GLES30.glEnableVertexAttribArray(0); // 设置顶点法线属性(原onDrawFrame中的代码移到这里) GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, mNormals); GLES30.glEnableVertexAttribArray(1); // 解绑VAO,结束配置记录 GLES30.glBindVertexArray(0);
把这段代码加到onSurfaceCreated方法的末尾(在glEnable(GLES30.GL_DEPTH_TEST);之后即可)。
3. 简化onDrawFrame方法
删除原来的顶点属性配置代码,替换为绑定VAO的逻辑:
// 绑定VAO,自动恢复之前记录的顶点属性配置 GLES30.glBindVertexArray(mVAO); // 执行绘制 GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, nbrOfVertices); // 解绑VAO(可选,推荐养成习惯) GLES30.glBindVertexArray(0);
同时删除原来的glDisableVertexAttribArray调用,VAO会管理属性数组的启用状态,下次绑定VAO时会自动启用对应的属性。
重构后的完整代码
import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.opengl.GLES30; import android.opengl.GLSurfaceView; import android.opengl.Matrix; import android.util.Log; import se.hig.dvg306.modul3app.R; import se.hig.dvg306.modul3app.tools.ResourceHandler; public class Modul3Renderer implements GLSurfaceView.Renderer { public Modul3Renderer (Context context) { appContext = context; Log.e(TAG, "--->>> Creating ModelLoader..."); ModelLoader modelLoader = new ModelLoaderImpl (); Log.e(TAG, "--->>> ...finished."); Log.e(TAG, "--->>> Loading model..."); Log.e(TAG, "--->>> Starting with vertices..."); float[] mVerticesData; try { mVerticesData = modelLoader.loadModel (context, R.raw.torus2, 0, 4, 6); } catch (IOException e) { throw new RuntimeException (e); } Log.e(TAG, "--->>> ...finished."); nbrOfVertices = mVerticesData.length / 4; mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mVertices.put(mVerticesData).position(0); Log.e(TAG, "--->>> Starting with normals..."); float[] mNormalData; try { mNormalData = modelLoader.loadModel (context, R.raw.torus2, 4, 4, 6); } catch (IOException e) { throw new RuntimeException (e); } Log.e(TAG, "--->>> ...finished."); nbrOfNormals = mNormalData.length / 4; mNormals = ByteBuffer.allocateDirect(mNormalData.length * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mNormals.put(mNormalData).position(0); } private int createShader(int type, String shaderSrc ) { int shader; int[] compiled = new int[1]; shader = GLES30.glCreateShader ( type ); if ( shader == 0 ) { return 0; } GLES30.glShaderSource ( shader, shaderSrc ); GLES30.glCompileShader ( shader ); GLES30.glGetShaderiv ( shader, GLES30.GL_COMPILE_STATUS, compiled, 0 ); if ( compiled[0] == 0 ) { Log.e ( TAG, GLES30.glGetShaderInfoLog ( shader ) ); GLES30.glDeleteShader ( shader ); return 0; } return shader; } public void onSurfaceCreated ( GL10 glUnused, EGLConfig config ) { int vertexShader; int fragmentShader; int programObject; int[] linked = new int[1]; try { vShaderStr = ResourceHandler.readTextData(appContext, R.raw.vertex_shader); } catch (IOException e) { Log.e ( TAG, "--->>> Could not load source code for vertex shader."); throw new RuntimeException (e); } Log.e ( TAG, "--->>> Loaded vertex shader: " + vShaderStr); try { fShaderStr = ResourceHandler.readTextData(appContext, R.raw.fragment_shader); } catch (IOException e) { Log.e ( TAG, "--->>> Could not load source code for fragment shader."); throw new RuntimeException (e); } Log.e ( TAG, "--->>> Loaded fragment shader: " + fShaderStr); vertexShader = createShader( GLES30.GL_VERTEX_SHADER, vShaderStr ); fragmentShader = createShader( GLES30.GL_FRAGMENT_SHADER, fShaderStr ); programObject = GLES30.glCreateProgram(); if ( programObject == 0 ) { return; } GLES30.glAttachShader ( programObject, vertexShader ); GLES30.glAttachShader ( programObject, fragmentShader ); GLES30.glBindAttribLocation ( programObject, 0, "vPosition" ); GLES30.glBindAttribLocation ( programObject, 1, "vNormal" ); GLES30.glLinkProgram ( programObject ); 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; } mProgramObject = programObject; GLES30.glClearColor ( 0.15f, 0.15f, 0.15f, 1.0f ); GLES30.glEnable(GLES30.GL_DEPTH_TEST); // 初始化VAO int[] vaoIds = new int[1]; GLES30.glGenVertexArrays(1, vaoIds, 0); mVAO = vaoIds[0]; GLES30.glBindVertexArray(mVAO); // 配置顶点位置属性 GLES30.glVertexAttribPointer(0, 4, GLES30.GL_FLOAT, false, 0, mVertices); GLES30.glEnableVertexAttribArray(0); // 配置顶点法线属性 GLES30.glVertexAttribPointer(1, 4, GLES30.GL_FLOAT, false, 0, mNormals); GLES30.glEnableVertexAttribArray(1); GLES30.glBindVertexArray(0); } public void onDrawFrame ( GL10 glUnused ) { Matrix.setIdentityM(mViewMatrix, 0); Matrix.translateM(mViewMatrix, 0, 0.0f, 0.0f, -60.0f); Matrix.rotateM(mViewMatrix, 0, 90.0f, 1.0f, 0.0f, 0.0f); Matrix.multiplyMM (mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); GLES30.glClear ( GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT ); GLES30.glUseProgram ( mProgramObject ); mMVPMatrixHandle = GLES30.glGetUniformLocation(mProgramObject, "uMVPMatrix"); GLES30.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); vLightPositionHandle = GLES30.glGetUniformLocation(mProgramObject, "vLightPosition"); GLES30.glUniform4fv(vLightPositionHandle, 1, lightPosition, 0); vLightColorDfHandle = GLES30.glGetUniformLocation(mProgramObject, "vLightColorDf"); GLES30.glUniform4fv(vLightColorDfHandle, 1, lightColorDf, 0); vMaterialColorDfHandle = GLES30.glGetUniformLocation(mProgramObject, "vMaterialColorDf"); GLES30.glUniform4fv(vMaterialColorDfHandle, 1, materialColorDf, 0); // 绑定VAO,自动恢复顶点属性配置 GLES30.glBindVertexArray(mVAO); // 执行绘制 GLES30.glDrawArrays (GLES30.GL_TRIANGLES, 0, nbrOfVertices); // 解绑VAO GLES30.glBindVertexArray(0); } public void onSurfaceChanged ( GL10 glUnused, int width, int height ) { mWidth = width; mHeight = height; GLES30.glViewport(0, 0, width, height); float ratio = (float) width / height; Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1.0f, 1.0f, 0.5f, 1000.0f); } private Context appContext; private int mWidth; private int mHeight; private int nbrOfVertices; private FloatBuffer mVertices; private int nbrOfNormals; private FloatBuffer mNormals; private int mProgramObject; private int mMVPMatrixHandle; private int mVAO; // VAO成员变量 private final float[] mMVPMatrix = new float[16]; private final float[] mProjectionMatrix = new float[16]; private final float[] mViewMatrix = new float[16]; private int vLightPositionHandle; private final float lightPosition [] = {175.0f, 75.0f, 125.0f, 0.0f}; private int vLightColorDfHandle; private final float lightColorDf [] = {0.98f, 0.98f, 0.98f, 1.0f}; private int vMaterialColorDfHandle; private final float materialColorDf [] = {0.62f, 0.773f, 0.843f, 1.0f}; private String vShaderStr; private String fShaderStr; private static String TAG = "Modul3Renderer"; }
内容的提问来源于stack exchange,提问作者apo
相关产品推荐
相关产品推荐

