OpenGL原理与实践——核心模式(一):VBO、VAO等原理解析及项目初始设置
目录
序言——OpenGL在是什么?为什么?做什么?
OpenGL实现了什么
OpenGL内模型数据的本质——顶点数据
我们需要研究什么——三角形,一个图形基元
MVP变换
OpenGL渲染流程的关键——摄像机变换
OpenGL渲染管线概览
准备——项目配置
项目初始代码框架及注释
初识——三角形绘制
OpenGL中的顶点数据格式——float数组
OpenGL中shader如何从CPU中获取数据——layout(锚点)
Shader
VBO:Vertex Buffer Object
VAO:解决锚点问题,记录了VBO的锚点信息
编译shader
设定VAO并进行渲染
整体源码
序言——OpenGL在是什么?为什么?做什么?
OpenGL实现了什么
将三维物体映射到视线方向上的一个裁剪空间(屏幕)上
OpenGL内模型数据的本质——顶点数据
我们需要研究什么——三角形,一个图形基元
MVP变换
OpenGL渲染流程的关键——摄像机变换
OpenGL渲染管线概览
准备——项目配置
GLFW
Download | GLFW
GLAD
https://glad.dav1d.de
下载后,进行相应配置。
项目初始代码框架及注释
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height);
}void processInput(GLFWwindow* window) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {glfwSetWindowShouldClose(window, true);}
}int main() {//初始化OpenGL上下文环境,OpenGL是一个状态机,会保存当前状态下的渲染状态以及管线的状态glfwInit(); //,3版本以上glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//用OpenGL核心开发模式glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);//创建窗体GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGl Core", nullptr, nullptr);if (window == nullptr) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}//把当前上下文绑定至当前窗口glfwMakeContextCurrent(window);//通过glad绑定各种函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}//视口:需要渲染的东西在哪里glViewport(0, 0, 800, 600);//当Frame大小变动,调用回调函数调整视口大小glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);//防止窗口结束退出while (!glfwWindowShouldClose(window)) {processInput(window);//擦除画布,用定义的颜色填充glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);//双缓冲glfwSwapBuffers(window);glfwPollEvents();}//结束,释放资源glfwTerminate();return 0;}
运行结果如下:
初识——三角形绘制
OpenGL中的顶点数据格式——float数组
看向-Z方向
OpenGL中shader如何从CPU中获取数据——layout(锚点)
- CPU将float顶点数据数组传入GPU
- CPU告诉GPU如何解析这个数组
- 调用渲染指令进行绘制
GPU显存中的布局:layout;可以理解为“锚点”,指明在这一锚点代表的区域,存放了什么样的数据。
Shader
直白来说,Shader就是跑在GPU上的一种语言,用来操作GPU。
我们先写好两个shader的内容,先大致了解一番:
vertexShader:
#version 330 core//在layout=0,这块区域放置了一个vec3
layout (location = 0 ) in vec3 aPos;//操作
void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
- vertexShader中的数据gl_Position,会自动流入下一个阶段中,也就是fragmentShader
- vertexShader会被调用多少次?有多少顶点就会调用多少次
fragmentShader:
#version 330 core
out vec4 FragColor;
void main(){FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}
- fragmentShader的目的是为了输出一个数据,这里是vec4 FragColor,被定义为out类型,会被输出到下一个管线流程中。
- fragmentShader会被调用多少次?简单来说有多少像素就会调用多少次
流程:
- 将顶点数据转入到vertexShader,进行空间变换等操作(注意是并行的)
- 数据从vertexShader传入到fragmentShader,进行像素插值等操作(处理一堆像素点)
VBO:Vertex Buffer Object
在上面那个图中,其中的“GPU shader”就是所谓的VBO,也就是我们开辟的一块区域。
在开辟的这块空间,存储顶点数据。
那么在OpenGL中如何做这件事?
- 获取VBO的index(由OpenGL状态机分配的index)
- 绑定VBO的index
- 给VBO分配显存空间,并传输数据
- 告诉shader数据的解析方式
- 激活锚点,按照解析方式取读取数据
具体代码如下,我们在mian.cpp中添加如下函数:
//构建模型数据:VBO,
void initModel() {float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f};glGenBuffers(1, &VBO);//绑定哪一种buffer, glBindBuffer(GL_ARRAY_BUFFER, VBO);//分配显存:分配哪种buffer,分配显存大小,分配地址,使用数据的方式glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//打开锚点:激活glEnableVertexAttribArray(0);//解绑glBindBuffer(GL_ARRAY_BUFFER, 0);
}
每个函数的作用和参数意义,这里我用注释详细标明。方便后时查阅复习。
VAO:解决锚点问题,记录了VBO的锚点信息
编译shader
VAO是与shader密切相关的一个内容,所以在此之前需要进行shader的一系列操作:
首先声明一个全局变量:
unsigned int shaderProgram = 0;
初始化Shader,并进行编译链接。
void initShader(const char* _vertexPath, const char* _fragPath) {//shader的代码读取std::string _vertexCode("");std::string _fragCode("");std::ifstream _vShaderFile;std::ifstream _fShaderFile;_vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);_fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try {_vShaderFile.open(_vertexPath);_fShaderFile.open(_fragPath);std::stringstream _vShaderStream, _fShaderStream;_vShaderStream << _vShaderFile.rdbuf();_fShaderStream << _fShaderFile.rdbuf();_vertexCode = _vShaderStream.str();_fragCode = _fShaderStream.str();}catch(std::ifstream::failure e) {std::string errStr = "read shader fail";std::cout << errStr << ": " << e.what() << std::endl;}const char* _vShaderStr = _vertexCode.c_str();const char* _fShaderStr = _fragCode.c_str();//shader的编译链接unsigned int _vertexID = 0, _fragID = 0;char _infoLog[512];int _successFlag = 0;//编译_vertexID = glCreateShader(GL_VERTEX_SHADER);glShaderSource(_vertexID, 1, &_vShaderStr, nullptr);glCompileShader(_vertexID);//捕捉编译过程中的状态信息glGetShaderiv(_vertexID, GL_COMPILE_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(_vertexID, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}_fragID = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(_fragID, 1, &_vShaderStr, nullptr);glCompileShader(_fragID);//捕捉编译过程中的状态信息glGetShaderiv(_fragID, GL_COMPILE_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(_fragID, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//链接//创建一个程序shaderProgram = glCreateProgram();glAttachShader(shaderProgram, _vertexID);glAttachShader(shaderProgram, _fragID);glLinkProgram(shaderProgram);glGetProgramiv(shaderProgram, GL_LINK_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(shaderProgram, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//删除中间文件glDeleteShader(_vertexID);glDeleteShader(_fragID);}
设定VAO并进行渲染
//构建模型数据:VBO,VAO
void initModel() {float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f};glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);//之后的VBO便属于了VAO的管理范围glGenBuffers(1, &VBO);//绑定哪一种buffer, glBindBuffer(GL_ARRAY_BUFFER, VBO);//分配显存:分配哪种buffer,分配显存大小,分配地址,使用数据的方式glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//打开锚点:激活glEnableVertexAttribArray(0);//解绑//glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);}
//渲染
void render() {glBindVertexArray(VAO);glUseProgram(shaderProgram);//以三角形模式绘制,从第0个顶点开始,起作用的有3个点glDrawArrays(GL_TRIANGLES, 0, 3);glUseProgram(0);
}
渲染结果:
整体源码
main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
void initModel();
void initShader(const char* _vertexPath, const char* _fragPath);
void render();unsigned int VBO = 0;
unsigned int VAO = 0;
unsigned int shaderProgram = 0;int main() {//初始化OpenGL上下文环境,OpenGL是一个状态机,会保存当前状态下的渲染状态以及管线的状态glfwInit(); //,3版本以上glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//用OpenGL核心开发模式glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);//创建窗体GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGl Core", nullptr, nullptr);if (window == nullptr) {std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}//把当前上下文绑定至当前窗口glfwMakeContextCurrent(window);//通过glad绑定各种函数指针if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {std::cout << "Failed to initialize GLAD" << std::endl;return -1;}//视口:需要渲染的东西在哪里glViewport(0, 0, 800, 600);//当Frame大小变动,调用回调函数调整视口大小glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);initModel();initShader("vertexShader.glsl", "fragmentShader.glsl");//防止窗口结束退出while (!glfwWindowShouldClose(window)) {processInput(window);//擦除画布,用定义的颜色填充glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);render();//双缓冲glfwSwapBuffers(window);glfwPollEvents();}//结束,释放资源glfwTerminate();return 0;}void framebuffer_size_callback(GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height);
}void processInput(GLFWwindow* window) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {glfwSetWindowShouldClose(window, true);}
}//渲染
void render() {glBindVertexArray(VAO);glUseProgram(shaderProgram);//以三角形模式绘制,从第0个顶点开始,起作用的有3个点glDrawArrays(GL_TRIANGLES, 0, 3);glUseProgram(0);
}//构建模型数据:VBO,VAO
void initModel() {float vertices[] = {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f};glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);//之后的VBO便属于了VAO的管理范围glGenBuffers(1, &VBO);//绑定哪一种buffer, glBindBuffer(GL_ARRAY_BUFFER, VBO);//分配显存:分配哪种buffer,分配显存大小,分配地址,使用数据的方式glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//对哪个锚点进行操作:layout=0的锚点,读3个顶点,类型为float,不需要归一化,每次步长为3个float大小,从0处开始读glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);//打开锚点:激活glEnableVertexAttribArray(0);//解绑//glBindBuffer(GL_ARRAY_BUFFER, 0);glBindVertexArray(0);}//
void initShader(const char* _vertexPath, const char* _fragPath) {//shader的代码读取std::string _vertexCode("");std::string _fragCode("");std::ifstream _vShaderFile;std::ifstream _fShaderFile;_vShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);_fShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);try {_vShaderFile.open(_vertexPath);_fShaderFile.open(_fragPath);std::stringstream _vShaderStream, _fShaderStream;_vShaderStream << _vShaderFile.rdbuf();_fShaderStream << _fShaderFile.rdbuf();_vShaderFile.close();_fShaderFile.close();_vertexCode = _vShaderStream.str();_fragCode = _fShaderStream.str();}catch(std::ifstream::failure e) {std::string errStr = "read shader fail";std::cout << errStr << ": " << e.what() << std::endl;}const char* _vShaderStr = _vertexCode.c_str();const char* _fShaderStr = _fragCode.c_str();//shader的编译链接unsigned int _vertexID = 0, _fragID = 0;char _infoLog[512];int _successFlag = 0;//编译_vertexID = glCreateShader(GL_VERTEX_SHADER);glShaderSource(_vertexID, 1, &_vShaderStr, nullptr);glCompileShader(_vertexID);//捕捉编译过程中的状态信息glGetShaderiv(_vertexID, GL_COMPILE_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(_vertexID, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}_fragID = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(_fragID, 1, &_fShaderStr, nullptr);glCompileShader(_fragID);//捕捉编译过程中的状态信息glGetShaderiv(_fragID, GL_COMPILE_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(_fragID, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//链接//创建一个程序shaderProgram = glCreateProgram();glAttachShader(shaderProgram, _vertexID);glAttachShader(shaderProgram, _fragID);glLinkProgram(shaderProgram);glGetProgramiv(shaderProgram, GL_LINK_STATUS, &_successFlag);if (!_successFlag) {glGetShaderInfoLog(shaderProgram, 512, nullptr, _infoLog);std::string errStr(_infoLog);std::cout << errStr << std::endl;}//删除中间文件glDeleteShader(_vertexID);glDeleteShader(_fragID);}
vertexShader.glsl
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
};
fragmentShader.glsl
#version 330 core
out vec4 FragColor;
void main()
{FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
};
OpenGL原理与实践——核心模式(一):VBO、VAO等原理解析及项目初始设置相关推荐
- OpenGL原理与实践——核心模式(六):光照贴图、光源分类以及多光源场景主要源码实现
本章主要以代码为主,理论理解即可.详细分析代码 目录 光照贴图 光源分类 平行光 点光源 shader--点光源 聚光灯 聚光灯边缘优化--光强递减 源码解析 main 全局变量.句柄 main函数主 ...
- OpenGL原理与实践——核心模式(五):颜色、基础光照、Phong模型、材质与光
目录 颜色相关理论 什么是颜色 如何计算颜色? 简单实现 Phong光照模型--局部光照模型 环境光 编辑 漫反射 镜面反射 材质与光 材质与纹理的关系 材质在shader的体现 材质属性与光属性 ...
- c++程序设计原理与实践_课程思政水资源系统优化原理与方法课程思政元素的探索...
案例说明+课程基本信息 案例说明: 水资源系统优化原理与方法的思政教育目标是要让学生建立一种科学思维方式,用辩证和历史唯物主义的观点去观察和分析问题,培养其规则意识和约束观念,以社会主义核心价值观来进 ...
- 计算机安全原理与实践第3版PDF,windows安全原理与技术.pdf
085155 Windows 安全原理与技术 学分 :3 学时 :48 先修课程 :高级语言程序设计.操作系统 课程性质 :专业选修(限选)课程 适用专业 :信息安全专业 内容简介 :<Wind ...
- 计算机图形学原理及实践第三版pdf,计算机图形学原理及实践 英文第3版
[实例简介] 本书是图形学经典巨著Computer Graphics: Principles and Practice的英文第3版 Computer Graphics Third edition Th ...
- OpenGL核心模式详细讲解[结合LearnOpenGL]
OpenGL立即渲染模式&核心模式 OpenGL (for"Open Graphics Library") is an API (Application Programmi ...
- 逆向分析并修改Hello World程序《逆向工程核心原理》《软件逆向工程原理与实践》
文章目录 OllyDbg窗口及快捷键 步骤1:VS生成需逆向的文件 步骤2:OllyDbg中打开该程序的exe文件,找到需修改的位置 步骤3:修改 修改1:修改指令 修改2:修改字符串 修改3:输出任 ...
- Qt OpenGL(二十)——Qt OpenGL 核心模式版本
Qt OpenGL(二十)--Qt OpenGL 核心模式版本 一.写在前面 在之前的OpenGL教程(1~19)中,采用的方式都是固定渲染管线,也就是OpenGL3.2版本之前的写法,但是OpenG ...
- 20145233《网络对抗》 第三周 免杀原理与实践
20145233<网络对抗> 第三周 免杀原理与实践 实验内容 理解免杀技术原理(1分) 正确使用msf编码器,veil-evasion,自己利用shellcode编程等免杀工具或技巧:( ...
- Shadow Mapping 的原理与实践 【转】
Shadow Mapping 的原理与实践 [转] Shadow Mapping 的原理与实践 [转] posted on 2018-08-16 23:37 时空观察者9号 阅读(...) 评论(.. ...
最新文章
- springboot+shiro使用权限注解问题_无法使用注解_使用注解无法跳转无权限页面
- 最新《医学图像深度语义分割》综述论文
- Python:初始函数
- linux使用技巧:自动补全、常用快捷键* ? [] {}
- Linux内核访问外设I/O--动态映射(ioremap)和静态映射(map_desc)
- 大气校正后的ndvi_Sentinel2 L1C下载、大气校正、重采样
- mysql常见问题记录
- 被窃听、被定位:“裸奔时代”还有隐私吗?
- Manual Create Database OMF
- jboss mysql_jboss7.1.1配置mysql数据源
- 区分音视频文件的容器格式和编码格式
- 软件测试(四):软件测试用例设计
- [转]c#对象的浅拷贝和深拷贝
- Javaweb阶段学习
- VBA中调用Excel函数
- php实现智能音箱播放内容,海尔小优智能音箱有这些功能 用好它更方便
- 官方纯净版windows10教育版安装密钥极其下载地址
- 华为交换机导入配置_华为交换机通用配置方式方法
- 基于大数据的线上线下电商用户数据挖掘研究
- 读书笔记---阶级逆袭——三代人的卵巢彩票