系列文章目录
通俗易懂的 OpenGL ES 3.0(一)入门必备知识!!
通俗易懂的 OpenGL ES 3.0(三)NDK集成opengl
前言
接着上篇,说说 vbo,vao,以及默认的顶点储存。 所以 通过绘制 简易坐标线 以及 三角来说明为什么要用这几个玩意吧。 =v=!!!
效果图
![](https://i-blog.csdnimg.cn/blog_migrate/b418ceb02b09c9e8166f1c0aa5752e33.png)
绘制坐标线以及三角形
入门必备三角形好吧,好比程序入门届的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=!!!。