几张图看明白VAO、VBO、EBO的关系和代码顺序
0.详细教程可看https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/
1.可以简单地认为VAO的作用是这样的:
由于每渲染一次物体就要用一个VBO,而每次绑定一次VBO就要设置各个的顶点的属性,启动各个属性,代码十分复杂,复用性很差,因为每个物体的属性个数什么的都不一样(也就是说不是同构的),循环根本解决不了。所以就抽象出一层VAO来解决这个问题,相当于复用代码,使之简介快速。只在一开始将所有的VBO绑定对应的VAO就OK了,之后渲染的时候完全就可以绑定VAO,然后你就循环处理同构的VAO就好了。
2.其实EBO的作用也可以简单地理解为复用跟减少冗余,貌似还可以节约内存跟缓存:
否则的话VBO中储存大量的数据
3.VAO中存储着VBO的信息和EBO的信息
所以正确的绑定顺序是VAO、VBO、EBO,将后两者的信息也绑定进VAO中去啊
如果不适用VAO的话,那么最后一部分每次渲染都要设置第四部分的顶点属性,要好多行好多行,而用了VAO就一行绑定VAO就OK了啊,嘻嘻
4.创建相同的两个三角形,但对它们的数据使用不同的VAO和VBO:
#include <glad/glad.h>
#include <GLFW/glfw3.h> #include <iostream> void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window); // settings
const unsigned int SCR_WIDTH = ;
const unsigned int SCR_HEIGHT = ; const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
" FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
"}\n\0"; int main()
{
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, );
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, );
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); #ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
#endif // glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); // glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -;
} // build and compile our shader program
// ------------------------------------
// vertex shader
int vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, , &vertexShaderSource, NULL);
glCompileShader(vertexShader);
// check for shader compile errors
int success;
char infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertexShader, , NULL, infoLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// fragment shader
int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
// check for shader compile errors
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragmentShader, , NULL, infoLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;
}
// link shaders
int shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
// check for linking errors
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, , NULL, infoLog);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader); // set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float firstTriangle[] = {
-0.9f, -0.5f, 0.0f, // left
-0.0f, -0.5f, 0.0f, // right
-0.45f, 0.5f, 0.0f, // top
};
float secondTriangle[] = {
0.0f, -0.5f, 0.0f, // left
0.9f, -0.5f, 0.0f, // right
0.45f, 0.5f, 0.0f // top
};
unsigned int VBOs[], VAOs[];
glGenVertexArrays(, VAOs); // we can also generate multiple VAOs or buffers at the same time
glGenBuffers(, VBOs);
// first triangle setup
// --------------------
glBindVertexArray(VAOs[]);
glBindBuffer(GL_ARRAY_BUFFER, VBOs[]);
glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, * sizeof(float), (void*)); // Vertex attributes stay the same
glEnableVertexAttribArray();
// glBindVertexArray(0); // no need to unbind at all as we directly bind a different VAO the next few lines
// second triangle setup
// ---------------------
glBindVertexArray(VAOs[]); // note that we bind to a different VAO now
glBindBuffer(GL_ARRAY_BUFFER, VBOs[]); // and a different VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , (void*)); // because the vertex data is tightly packed we can also specify 0 as the vertex attribute's stride to let OpenGL figure it out
glEnableVertexAttribArray();
// glBindVertexArray(0); // not really necessary as well, but beware of calls that could affect VAOs while this one is bound (like binding element buffer objects, or enabling/disabling vertex attributes) // uncomment this call to draw in wireframe polygons.
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); // render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window); // render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram);
// draw first triangle using the data from the first VAO
glBindVertexArray(VAOs[]);
glDrawArrays(GL_TRIANGLES, , );
// then we draw the second triangle using the data from the second VAO
glBindVertexArray(VAOs[]);
glDrawArrays(GL_TRIANGLES, , ); // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
} // optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(, VAOs);
glDeleteBuffers(, VBOs); // glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return ;
} // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
} // glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(, , width, height);
}
几张图看明白VAO、VBO、EBO的关系和代码顺序的更多相关文章
- 一张图看懂Function和Object的关系及简述instanceof运算符
我在写一篇图解prototype和__proto__的区别时,搜资料搜到了一个有意思的现象,下面这两个运算返回的结果是一样的: Function instanceof Object;//true Ob ...
- 【转帖】自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势
自助式BI的崛起:三张图看清商业智能和大数据分析市场趋势 大数据时代,商业智能和数据分析软件市场正在经历一场巨变,那些强调易用性的,人人都能使用的分析软件正在取代传统复杂的商业智能和分析软件成为市场的 ...
- OpenGL渲染管道,Shader,VAO&VBO&EBO
OpenGL渲染管线 (也就是)OpenGL渲染一帧图形的流程 以下列举最简单的,渲染一个三角形的流程,你可以将它视为 精简版OpenGL渲染管线 更复杂的流程也仅仅就是:在此基础上的各个流程中 添加 ...
- 一张图看懂ANSYS17.0 流体 新功能与改进
一张图看懂ANSYS17.0 流体 新功能与改进 提交 我的留言 加载中 已留言 一张图看懂ANSYS17.0 流体 新功能与改进 原创2016-02-03ANSYS模拟在线模拟在线 模拟在线 ...
- 一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别
一张图看懂开源许可协议,开源许可证GPL.BSD.MIT.Mozilla.Apache和LGPL的区别 首先借用有心人士的一张相当直观清晰的图来划分各种协议:开源许可证GPL.BSD.MIT.Mozi ...
- FUNMVP:几张图看懂区块链技术到底是什么?(转载)
几张图看懂区块链技术到底是什么? 本文转载自:http://www.cnblogs.com/behindman/p/8873191.html “区块链”的概念可以说是异常火爆,好像互联网金融峰会上没人 ...
- 4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程)
4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程) (2016-02-01 03:21:06) 转载▼ 标签: delphi ios delphi10 教程 编程 分类: 编程 ...
- 一张图看懂css的position里的relative和absolute的区别
position有以下属性:static.inherit.fixed.absolute.relative前三个好理解好区分:static:是默认状态,没有定位,元素出现在正常的流中(忽略 top, b ...
- [转帖]两张图看懂GDT、GDTR、LDT、LDTR的关系
两张图看懂GDT.GDTR.LDT.LDTR的关系 2018-06-09 18:13:53 Six_666A 阅读数 2044更多 分类专栏: 深入理解linux内核 转自:http://ju.o ...
随机推荐
- GUI库之Tkinter组件(二)
一.Lable组件 Lable组件是用于在界面上输出描述的标签: 1.举个例子. # Lable组件 from tkinter import * root = Tk() root.title(&quo ...
- 并发编程---线程---开启方式---进程线程的区别----Thread的其他属性
线程 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合)而线程才是cpu上的执行单位 1.同一个进程内的多个线程共享该进程内的地址资源 2.创建线程的开销远小于创建进程的开销(创建一 ...
- Node.js之npm使用
1.使用国内镜像 本人window8系统安装node.js之后,使用npm的命令安装Express一直失败: npm install express -g (以上命令会将 Express 框架安装在当 ...
- seller vue 编写接口请求【mock数据】
[build]-[webpack.dev.conf.js] 或 [build]-[dev-server.js] 在webpack.dev.conf.js中的写法 var appData = requi ...
- 万恶之源 - Python开发规范
开发规范 什么是开发规范?为什么要有开发规范呢? 你现在包括之前写的一些程序,所谓的'项目',都是在一个py文件下完成的,代码量撑死也就几百行,你认为没问题,挺好.但是真正的后端开发的项目,系统等,少 ...
- Centos7下安装syslog-ng
1. syslog-ng的安装地址 https://github.com/balabit/syslog-ng/releases 下载最新版本. 2. 安装gnomeui开发包,因为syslog-ng依 ...
- sjw-风评评测-定位页面元素
一.手工标准化操作流程: 1.登录系统 2.登录后的页面点击:账户设置 3.点击“重新评测”,进入到风险评测页面 4.答完8道题 5.勾选条件checkbox 6.点击“提交” 提交后的页面 二.自动 ...
- js抽红包分配
将 50000元随机分给10个人,其中3个人必须分到百位数,4个人分到千位数,3个人分到万位数,每个人所得金额 <!DOCTYPE html> <html lang="zh ...
- 【转360】KB4041678 Windows 仅安全更新(2017.10) 补丁更新后执行SQL出错! http://bbs.360.cn/thread-15201531-1-1.html
把EXCEL20003表数据导入到MDB数据库中sql命令语句\"SELECT * INTO 表 FROM [Excel 8.0;DATABASE=C:\\1.xls].[Sheet1$]\ ...
- 超参数调试、Batch正则化和编程框架
1.调试处理 2.为超参数选择合适的范围 3.超参数在实践中调整:熊猫与鱼子酱 4.正则化网络的激活函数 5.将batch norm拟合进神经网络 6. 为什么Batch Norm会起作用? 7.测试 ...