最通俗的安卓OpenGL教学06——使用VBO

本文详细介绍了OpenGL中的顶点缓冲对象(VBO)的概念及其使用方法。通过VBO可以将顶点数据缓存到GPU中,避免频繁的CPU-GPU数据交换,从而显著提高图形绘制效率。

OpenGL VBO 即顶点缓冲对象 ,目的是提高顶点坐标获取的效率。使用 VBO时,能把顶点数据缓存到GPU开辟的一段内存中,然后使用时不必再从本地获取,而是直接从显存中获取,这样就能提升绘制的效率,不使用 VBO,每次绘制( glDrawArrays )图形时都是从本地内存处获取顶点数据然后传输给 OpenGL来绘制,这样就会频繁的操作 CPU->GPU增大开销,从而降低效率。

OpenGL VBO的创建大概有以下步骤:

  1. 创建VBO
  2. 绑定VBO
  3. 分配VBO需要的缓存大小
  4. 为VBO设置顶点数据的值
  5. 解绑VBO

OpenGL VBO的使用大概有以下步骤:

  1. 绑定VBO
  2. 从VBO中获取顶点,设置顶点数据
  3. 解绑VBO

1. VBO创建

在onSurfaceCreated时创建VBO:


        //创建 VBO
        int[] vbo = new int[1];
        GLES20.glGenBuffers(1, vbo, 0);
        vboID = vbo[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID);

        //分配 VBO需要的缓存大小
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20.GL_STATIC_DRAW);
        //设置顶点坐标数据的值到 VBO
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
        //设置纹理坐标数据的值到 VBO
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
        //解绑 VBO,指的是离开对 VBO的配置,进入下一个状态
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

2. VBO使用

在onDrawFrame时使用VBO:

  		//开始使用 VBO
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID);
        //使能顶点属性数组,使之有效
        GLES20.glEnableVertexAttribArray(vPosition);
        //使能之后,为顶点属性赋值,从VBO里获取 绑定顶点坐标; 注意:最后一个参数如果是 vertexBuffer,那么就没有用到 VBO,那就还是从CPU里取顶点
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 0);
        //使能片元属性数组,使之有效
        GLES20.glEnableVertexAttribArray(fPosition);
        //使能之后,为片元属性赋值,从VBO里获取 绑定纹理坐标; 注意:最后一个参数为 VBO里的偏移量
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4);

        //退出 VBO的使用 进入纹理绑定状态
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

3.完整代码

package com.york.media.opengl.demo.vbo;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import com.york.media.opengl.R;
import com.york.media.opengl.egl.YGLSurfaceView;
import com.york.media.opengl.egl.YShaderUtil;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

/**
 * author : York
 * date   : 2020/12/20 18:01
 * desc   : OpenGL VBO 即顶点缓冲对象 ,目的是提高顶点坐标获取的效率
 * <p>
 * 不使用 VBO时,每次绘制( glDrawArrays )图形时都是从本地内存处获取顶点数据然后传输给 OpenGL来绘制,这样就会频繁的操作 CPU->GPU增大开销,从而降低效率。
 * 使用 VBO时,能把顶点数据缓存到GPU开辟的一段内存中,然后使用时不必再从本地获取,而是直接从显存中获取,这样就能提升绘制的效率。
 */
public class YVboRender implements YGLSurfaceView.YGLRender {

    private final Context mContext;
    private final FloatBuffer vertexBuffer;
    private final FloatBuffer fragmentBuffer;
    private int program;
    private int vPosition;
    private int fPosition;

    private int bitmapTexture;
    private int vboID;
    //顶点坐标
    float[] vertexData = {
            -1f, -1f,
            1f, -1f,
            -1f, 1f,
            1f, 1f
    };

    //纹理坐标
    float[] fragmentData = {
            0f, 1f,
            1f, 1f,
            0f, 0f,
            1f, 0f
    };


    public YVboRender(Context context) {
        this.mContext = context;

        //读取顶点坐标
        vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData);
        vertexBuffer.position(0);

        //读取纹理坐标
        fragmentBuffer = ByteBuffer.allocateDirect(fragmentData.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(fragmentData);
        fragmentBuffer.position(0);

    }

    @Override
    public void onSurfaceCreated() {
        //加载顶点着色器 shader
        String vertexSource = YShaderUtil.getRawResource(mContext, R.raw.screen_vert);
        //加载片元着色器 shader
        String fragmentSource = YShaderUtil.getRawResource(mContext, R.raw.screen_frag);
        //获取源程序
        program = YShaderUtil.createProgram(vertexSource, fragmentSource);
        //从渲染程序中得到着顶点色器中的属性
        vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //从渲染程序中得到片元着色器中的属性
        fPosition = GLES20.glGetAttribLocation(program, "fPosition");

        //创建 VBO
        int[] vbo = new int[1];
        GLES20.glGenBuffers(1, vbo, 0);
        vboID = vbo[0];
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID);

        //分配 VBO需要的缓存大小
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4 + fragmentData.length * 4, null, GLES20.GL_STATIC_DRAW);
        //设置顶点坐标数据的值到 VBO
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, vertexData.length * 4, vertexBuffer);
        //设置纹理坐标数据的值到 VBO
        GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, vertexData.length * 4, fragmentData.length * 4, fragmentBuffer);
        //解绑 VBO,指的是离开对 VBO的配置,进入下一个状态
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        //创建 1个纹理,放入到 int [] textureIds
        int[] textureIds = new int[1];
        GLES20.glGenTextures(1, textureIds, 0);//第三个参数是指从哪儿开始取
        bitmapTexture = textureIds[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTexture);//在没设置点的情况下默认是绑定 0号纹理

        //设置纹理的环绕方式
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
        //设置纹理的过滤方式
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);

        //绑定bitmap 到  textureIds[0] 2D纹理
        Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.nobb);
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
        bitmap.recycle();

        //解绑纹理 指的是离开对 纹理的配置,进入下一个状态
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    }

    @Override
    public void onSurfaceChanged(int width, int height) {
        //设置窗口大小
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onDrawFrame() {
        //清除屏幕,此处用的是红色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glClearColor(1f, 0f, 0f, 1f);
        //使用着色器源程序
        GLES20.glUseProgram(program);

        //开始使用 VBO
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID);
        //使能顶点属性数组,使之有效
        GLES20.glEnableVertexAttribArray(vPosition);
        //使能之后,为顶点属性赋值,从VBO里获取 绑定顶点坐标; 注意:最后一个参数如果是 vertexBuffer,那么就没有用到 VBO,那就还是从CPU里取顶点
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 8, 0);
        //使能片元属性数组,使之有效
        GLES20.glEnableVertexAttribArray(fPosition);
        //使能之后,为片元属性赋值,从VBO里获取 绑定纹理坐标; 注意:最后一个参数为 VBO里的偏移量
        GLES20.glVertexAttribPointer(fPosition, 2, GLES20.GL_FLOAT, false, 8, vertexData.length * 4);

        //退出 VBO的使用
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        //要开始绘制纹理了,激活纹理 0号, 之所以激活 0号,是因为在没设置点的情况下默认是 0号
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //绑定 bitmapTexture 到纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, bitmapTexture);
        //绘制
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        //解绑 2D纹理,退出对纹理的使用
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    }

}

效果其实一样,看不太出效果,哈哈哈!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值