You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:15:31