opengl-第二章:无shader的2D绘制
绘制教程
1.opengl的绘制策略
推荐阅读:https://learnopengl.com/Getting-started/Hello-Triangle
也称:graphics pipeline
在opengl里面,将整个绘制流程当做是一个状态机,你可以理解为显卡本身是一个黑板,当我们向里面填顶点数据时,可以通过着色器控制顶点数据去显示。
而当我们填写顶点数据的方法则通常是
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
这个时候,数据其实已经写入到显卡里面。而渲染流水线,不过就是对缓存里面的数据进行操作,然后通过显卡提供的指令集最终让显示屏里面的液晶亮起来。
2.本教程基本思路
- 本教程不涉及顶点着色器和片元着色器,仅涉及绘制命令
- 本教程的基本思路
1. 在CPU里面创建数据(VBO和VAO),并拷贝到GPU
2. 绘制窗口
3. 调用绘制命令绘制三角面
3.顶点数据VBO和VAO
1. 顶点数据包括位置,点的绘制索引
VertexBufferObject(VBO)
1.顶点最重要的属性就是位置属性(x,y,z).所以,这也是VBO最重要的属性。
我们可以定义一个最基本的VBO
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f ,// top
0.5f, 0.5f, 0.0f
};
注意,现在数据还是在cpu里面,接下来我们要拷贝到gpu的缓存里面
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
那么,仅通过VBO的数据,我们是否能够绘制三角面呢?答案是不能,这是因为opengl
的内核需要Vertex Array Object对象去进行绘制。
或者你可以理解为,Opengl的绘制指令是通过控制VAO来进行的。
那么,这个时候,你大致可以理解为VAO本身是对VBO的引用。
Vertex Array Object(VAO)
生成VAO可以直接通过以下代码
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
这个时候是否可以直接使用triangle进行绘制了呢?答案是不行的,事实上,对于顶点
数据本身来说,它既可以包含位置信息,也可以包含颜色信息,甚至还能包含其他信
息,比如以下两个VBO
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
};
float vertices[] = {
//pos //color //texccord
-0.5f, -0.5f, 0.0f, 0.5,0.5,0.5,0.0,0.5,
0.5f, -0.5f, 0.0f, 0.3,0.2,0.6,0.1,0.3
};
第一个VBO仅仅包括位置,但是第二个VBO包括了位置,颜色,和纹理分布,显然的我
们需要通过命令将其进行分开,这个过程叫做“修饰顶点属性”
修饰(设定)顶点属性(状态)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
1. 这串代码其实很容易理解,就是对VBO的数据进行修饰。我们看到的任意一个VBO都是
一维的数组,那么问题来了,我们从哪个地方进行绘制呢?那么第一个参数就是绘制的
起始位置,定义为0.
2. 新的问题,我们的顶点属性数据量不一样,我们人是知道的,但是机器却不知道,所
以,我们需要让机器知道,所以我们定义了3用于表示顶点属性的数据量
3. 另外一个问题,我们希望一个顶点的数据的大小,因为除了浮点数,我们还可以选择
整形数据,短整型数据,所以我们可以定义GL_Float
4. 第四个参数略微有些复杂,现在只需要理解对浮点数类型,将它指派为FL_FALSE就可以了
5. 另外一个重要的问题,我们输入的数据终究是一个一维的,那么怎么切成一个一个的
vertex则需要在这里定义一个gap了,如果我们定义一个vertex数据包括位置,颜色,纹
理一共七个浮点数,那么这个参数就会变成7 * sizeof(float)
6. 这个参数略微复杂,在这个版本的教程里面我们不会涉及,默认为void* 0
使用顶点属性
在前面的介绍里面,我们定义了一个顶点的数据模型,那么新的问题来了,如果我希望某
些属性可以使用而某些属性不可以使用要怎么做呢?显然gl肯定不会忘记提供一个这样的
API
glEnableVertexAttribArray(0);//让第0个属性可以使用
glDisableVertexAttribArray(0);//让第0个属性不可以使用
其实在原文里面,我们可以发现,在更加复杂的情况下的一个使用
清除缓存
防止内存泄露,需要将buffer给清理掉
glDeleteBuffers(1, &VBO);
4. 绘制窗口
定义窗口
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <time.h>
#include <iostream>
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
clock_t lastTimes;
GLFWwindow* window ;
int init() {
lastTimes = clock();
window = nullptr;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
}
void draw() {
}
void run() {
while (!glfwWindowShouldClose(window))
{
clock_t now = clock();
draw();
cout << "loop gap = " << now - lastTimes << endl;
lastTimes = now;
glfwSwapBuffers(window);
glfwPollEvents();
}
}
void shutDown() {
glfwTerminate();
}
int main()
{
init();
run();
shutDown();
return 0;
}
注意,我们的绘制函数将会在draw里面进行调用
5.调用绘制命令绘制三角
给一个背景色
没有着色器的三角默认是黑色的,所以你总得给一个背景色
设定状态
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
使用状态
glClear(GL_COLOR_BUFFER_BIT);
值得注意的是,当使用GL_COLOR_BUFFER_BIT时,会直接将当前的帧缓存刷成glClearColor指定的缓存,另外还可以有以下的几种类型,当然,在本教程中并不考虑
#define GL_DEPTH_BUFFER_BIT 0x00000100
#define GL_STENCIL_BUFFER_BIT 0x00000400
#define GL_COLOR_BUFFER_BIT 0x00004000
这个时候我们可以直接draw出一个带墨绿色的窗口
void draw() {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
剩下的代码
void draw() {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//添加数据
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f ,// top
0.5f,0.5f,0.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//调用几何着色器命令
glDrawArrays(GL_TRIANGLES, 0, 3);//绘制0,1,2连城的三角
//glDrawArrays(GL_TRIANGLES, 1, 4);//绘制1,2,3连城的三角
}
测试结果
6.完整的代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <time.h>
#include <iostream>
using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
clock_t lastTimes;
GLFWwindow* window ;
int init() {
lastTimes = clock();
window = nullptr;
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
}
void draw() {
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//添加数据
float vertices[] = {
-0.5f, -0.5f, 0.0f, // left
0.5f, -0.5f, 0.0f, // right
0.0f, 0.5f, 0.0f ,// top
0.5f,0.5f,0.0f
};
unsigned int VBO;
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
unsigned int VAO;
glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//调用几何着色器命令
glDrawArrays(GL_TRIANGLES, 0, 3);//绘制0,1,2连城的三角
//glDrawArrays(GL_TRIANGLES, 1, 4);//绘制1,2,3连城的三角
}
void run() {
while (!glfwWindowShouldClose(window))
{
clock_t now = clock();
draw();
cout << "loop gap = " << now - lastTimes << endl;
lastTimes = now;
glfwSwapBuffers(window);
glfwPollEvents();
}
}
void shutDown() {
glfwTerminate();
}
int main()
{
init();
run();
shutDown();
return 0;
}