通俗易懂的 OpenGL ES 3.0(四)NDK VAO VBO

系列文章目录

通俗易懂的 OpenGL ES 3.0(一)入门必备知识!!

通俗易懂的 OpenGL ES 3.0(二)渲染三角形

通俗易懂的 OpenGL ES 3.0(三)NDK集成opengl


前言

接着上篇,说说 vbo,vao,以及默认的顶点储存。 所以 通过绘制 简易坐标线 以及 三角来说明为什么要用这几个玩意吧。 =v=!!!

效果图

演示demo

绘制坐标线以及三角形

入门必备三角形好吧,好比程序入门届的hello world。所以就画多两条坐标线。

跟上篇那个背景绘制的流程差不多,总的来说就是借助GLSurfaceView提供的环境,在渲染的回调里面做合适的事情就好了,就不写那么详细了,详细的具体可以看看上篇。

头文件就省略了直接写核心的实现 TriangleDemo.h(略)

TriangleDemo.cpp

//
// Created by Lai on 2020/11/7.
//

#include "TriangleDemo.h"
#include "../util/logUtil.h"
#include "../util/GLUtil.h"
#include <GLES3/gl3.h>


void TriangleDemo::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");

    GLfloat triangleVertices[] = {
            0.0f,  0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };
    GLfloat lineVertices[] = {
            -1.0f,  0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f,  1.0f, 0.0f,
            0.0f,  -1.0f, 0.0f,
    };

    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    // Load the vertex data

    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );
    glDrawArrays (GL_LINES  , 0, 4);


    glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, triangleVertices );
    glEnableVertexAttribArray (0);

    glDrawArrays (GL_TRIANGLES  , 0, 3);


    glUseProgram (GL_NONE);
}

void TriangleDemo::Init() {

    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemo");

    m_ProgramObj =  GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
}


void TriangleDemo::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

编译shader,通过glVertexAttribPointer填充顶点坐标数据然后glDrawArrays 绘制

 void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);

就拿线条的顶点坐标例子

   GLfloat lineVertices[] = {
            -1.0f,  0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f,  1.0f, 0.0f,
            0.0f,  -1.0f, 0.0f,
    };
   //   绘制线条/从0开始/一共4顶点
   glDrawArrays (GL_LINES  , 0, 4);

GLenum mode:绘制模型。有以下形状

在这里插入图片描述

GL_APIENTRY glEnableVertexAttribArray (GLuint index);

开启顶点位置,允许着色器读取GPU数据。

参数就是 对应着的是顶点着色器的location,所以他就是glEnableVertexAttribArray(0);

char vShaderStr[] =
        "#version 300 es                          \n"
        "layout(location = 0) in vec4 vPosition;  \n"  
        "void main()                              \n"
        "{                                        \n"
        "   gl_Position = vPosition;              \n"
        "gl_PointSize = 50.0; \n"
        "}                         

调用顺序只要在绘图调用前调用即可。

那就完成了效果图

通常的顶点赋值

glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, 0, lineVertices );

第一个参数 0: 对应顶点着色器的位置,比如: 顶点着色器中使用layout(location = 0) Attribute的索引

第二个参数 3 : size, Attribute 变量数据是由几个元素组成的, x,y,z,w ; 最多四个,例子这边用到的顶点数据就是3个 。

第三个参数 GL_FLOAT : 顶点数据的类型,例子这边用到的是 浮点数值组成的

第四个参数 normalized, 是否归一化, 通常设置为GL_FALSE

第五个参数 0 : 步长,写0让opengl决定

最后一个参数 :没有用vbo,直接填顶点数据

更详细的解释可以看看

learnOpenGl里的你好,三角形

api大概就这样,说说顶点的储存,现在这种方式,是从内存拿数据,也就是现在顶点较少,所以一般只是画4个顶点,做做滤镜啥的,也无所谓了,但如果顶点数据一多就要考虑优化了,所以为了缩短cpu到gpu的距离,就是提高效率,所以就出现了这个VBO。

顶点缓冲对象(Vertex Buffer Objects, VBO)

总的来说就是在 GPU内存(通常被称为显存)中储存大量顶点,不用从cpu->gpu跑来跑去。

一般的步骤就是,生成->绑定->填数据

拿三角形的顶点来说明

生成VBO缓存区吧

//1 :需要创建VBO创建的数量,
//&triangleVBO: 存储缓冲对象
glGenBuffers(1, &triangleVBO);

绑定数据

//GL_ARRAY_BUFFER: 缓冲对象的类型, 这里是一个顶点缓存区类型
//triangleVBO: 需要绑定的缓冲区对象,也就是上面所生成的东西
glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);

填数据

//GL_ARRAY_BUFFER: 为指定类型的 当前VBO塞点数据
//sizeof(lineVertices): 数据大小。sizeof计算即可
//lineVertices:顶点数据
//GL_STATIC_DRAW:GL_STATIC_DRAW 表示数据不会或几乎不会改变。
glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);

给顶点属性赋值

//0:对应顶点着色器的layout(location = 0)
//3: 顶点属性多少为一组吧,比如我的是 x,y,z 所以就3 。 颜色的可能是4 因为有 r,g,b,a
//GL_FLOAT: 数据类型
//0: 步长
//0: 这里直接0了,不需要填充顶点数据,自己回去当前绑定的缓冲区找。 缓冲区起始位置的偏移量
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

然后就绘制

glDrawArrays(GL_TRIANGLES, 0, 3);

具体的代码,为了方便看懂,就不弄什么多态封装啥的。直接从上一个拷贝一份改改,直接上代码

头文件省略

TriangleDemoVBO.cpp

//
// Created by Lai on 2020/11/15.
//

#include "TriangleDemoVBO.h"

#include "../util/logUtil.h"
#include "../util/GLUtil.h"
#include <GLES3/gl3.h>


void TriangleDemoVBO::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");

    GLfloat triangleVertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };;
    GLfloat lineVertices[]= {
            -1.0f, 0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f, 1.0f, 0.0f,
            0.0f, -1.0f, 0.0f,
    };

    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    //绑定对象 GL_ARRAY_BUFFER:可以用来保存glVertexAttribPointer()设置的顶点数组数据
    glBindBuffer(GL_ARRAY_BUFFER, lineVBO);
    //为当前VBO塞点数据 GL_STATIC_DRAW 表示数据不会或几乎不会改变。
    glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_LINES, 0, 4);

    //绑定对象 GL_ARRAY_BUFFER:可以用来保存glVertexAttribPointer()设置的顶点数组数据
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    //为当前VBO塞点数据 GL_STATIC_DRAW 表示数据不会或几乎不会改变。
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glEnableVertexAttribArray(0);

    glUseProgram (GL_NONE);
}

void TriangleDemoVBO::Init() {
    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 0.0, 0.0, 0.0, 1);  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemoVBO");

    m_ProgramObj = GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);
    //创建对象
    glGenBuffers(1, &triangleVBO);
    glGenBuffers(1, &lineVBO);

}


void TriangleDemoVBO::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

不知有没有发现,每次draw绘制都需要glBindBuffer->glBufferData->draw. 都是要绑定,填充。我这边切换两个模型还好,要是顶点数据一多,那这个可不好管理,为了绑定到对应的VBO就会很麻烦,所以就出了一个叫VAO的东西,来代表这是谁的VBO。 就像一个班需要班主任,到时好找。

顶点数组对象(Vertex Array Object, VAO)

VAO的创建和 VBO差不多

//创建VAO
glGenVertexArrays(1, &triangleVAO);

绑定

glBindVertexArray(triangleVAO);

然后和平时写VBO一样,走流程就是

//绑定对象 GL_ARRAY_BUFFER:可以用来保存glVertexAttribPointer()设置的顶点数组数据
glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
//为当前VBO塞点数据 GL_STATIC_DRAW 表示数据不会或几乎不会改变。
glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);

这样这个 triangleVBO 就属于这个triangleVAO啦。

所以绘制的时候

glBindVertexArray(triangleVAO);
glDrawArrays(GL_TRIANGLES, 0, 3);

就那么简单。

具体代码,头文件省略

//
// Created by Lai on 2020/11/20.
//

#include <logUtil.h>
#include <GLUtil.h>
#include "TriangleDemoVAO.h"


void TriangleDemoVAO::draw(int screenW, int screenH) {
    //LOGCATE("TriangleSample::Draw");
    if(m_ProgramObj == 0)
        return;

    glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glClearColor(1.0, 1.0, 1.0, 1.0);

    // Use the program object
    glUseProgram (m_ProgramObj);

    glBindVertexArray(triangleVAO);
    glDrawArrays(GL_TRIANGLES, 0, 3);


    glBindVertexArray(lineVAO);
    glDrawArrays(GL_LINES, 0, 4);

    glUseProgram (GL_NONE);
}

void TriangleDemoVAO::Init() {

    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);

    if (m_ProgramObj != 0) return;

    if(m_ProgramObj != 0)
        return;
    char vShaderStr[] =
            "#version 300 es                          \n"
            "layout(location = 0) in vec4 vPosition;  \n"
            "void main()                              \n"
            "{                                        \n"
            "   gl_Position = vPosition;              \n"
            "gl_PointSize = 50.0; \n"
            "}                                        \n";

    char fShaderStr[] =
            "#version 300 es                              \n"
            "precision mediump float;                     \n"
            "out vec4 fragColor;                          \n"
            "void main()                                  \n"
            "{                                            \n"
            "   fragColor = vec4 ( 1.0, 0.5, 0.0, 1);  \n"
            "}                                            \n";

    LOGCATE("init TriangleDemoVAO");

    m_ProgramObj = GLUtil::CreateProgram(vShaderStr, fShaderStr, m_VertexShader, m_FragmentShader);

    GLfloat triangleVertices[] = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f,
    };
    glGenVertexArrays(1, &triangleVAO);
    //创建对象
    glGenBuffers(1, &triangleVBO);
    glBindVertexArray(triangleVAO);
    //绑定对象 GL_ARRAY_BUFFER:可以用来保存glVertexAttribPointer()设置的顶点数组数据
    glBindBuffer(GL_ARRAY_BUFFER, triangleVBO);
    //为当前VBO塞点数据 GL_STATIC_DRAW 表示数据不会或几乎不会改变。
    glBufferData(GL_ARRAY_BUFFER, sizeof(triangleVertices), triangleVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);


    GLfloat lineVertices[]= {
            -1.0f, 0.0f, 0.0f,
            1.0, -0, 0.0f,
            0.0f, 1.0f, 0.0f,
            0.0f, -1.0f, 0.0f,
    };

    glGenVertexArrays(1, &lineVAO);
    glGenBuffers(1, &lineVBO);
    glBindVertexArray(lineVAO);
    //绑定对象 GL_ARRAY_BUFFER:可以用来保存glVertexAttribPointer()设置的顶点数组数据
    glBindBuffer(GL_ARRAY_BUFFER, lineVBO);
    //为当前VBO塞点数据 GL_STATIC_DRAW 表示数据不会或几乎不会改变。
    glBufferData(GL_ARRAY_BUFFER, sizeof(lineVertices), lineVertices, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);
}

void TriangleDemoVAO::OnSurfaceChanged(int width, int height) {
    LOGCATE("MyGLRenderContext::OnSurfaceChanged [w, h] = [%d, %d]", width, height);
    glViewport(0, 0, width, height);
}

生成VAO和VBO都差不多,以后可以封装成工具类啥的。

总结

至于用不用VAO,VBO. 看自己的情况吧。顶点数据多可以考虑好吧。对你有帮助的话给个赞好吧=v=!!!。

演示demo

参考

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值