复制粘贴运行
我只会复制和粘贴,哪里会这个?
可是,这个实验,单纯的CV也不是不行……
release:
debug:
在实验1里,release模式下是报错的。
在实验2里,两个模式 的环境,都按实验1的路径设置的,都正常了?
前面,刚把动态lib,静态lib给“为什么不设置DLL也能运行”勉强糊弄过去;
这里,又冒出来个debug和release都用一个lib还都正常?lib不是分debug和release吗?
实际和预期不符,还是个计算机方面的问题
所以,自己用cmake编译一个试试吧……
看看到底如何,虽然已经能跑了
cmake编译lib
照着指导书搞吧,这次不用预编译好的了,尝试自己编译一下
新版的没有32位的啊……
终于在这里找到一样的版本了
但是不知道得下载多久……
下完了,18年的cmake没有VS2019……还报错
因为这个?
直接换最新的64位cmake吧……
依旧不快
下完了,这是正常的吗?
编译完了,但是,只有X64的?
这里说可以直接新建一个X86
照着搞了,报错,那就64位的吧……
实验指导书,为什么让32位的?不知道。
lib还分位数?越搞越复杂。
用这个64位的,重配一下环境试试。
还是得x64对x64:
然后,实测,debug和release的lib,混用也正常……玄学。。
如何cmake出32位的项目?
设置这个吗?试试吧……
win32了?
试验一下:
重新生成:
改为x64
恢复正常
上次那个debug和release通用,是不是因为没有重新生成项目啊?
报错
cmake的总结
算了吧,反正release也不常用。。
先这样吧,能跑就行。
至少下了一个cmake,然后知道了cmake如何生成32/64位的VS项目
多关注关注代码吧,VAO,VBO什么的。
感觉核心是OpenGL如何画图,这个GLFW充其量是个和窗口有关的外边的壳……
新陈代谢
上面那个阿里云的链接,里面配完环境,还找了个代码跑了一下
我也跑了一下,报错了
为什么呢?
昨天下载的时候,好像确实选了一下core。。
这下是真真实实的感受到了什么叫新陈代谢了。
新版OpenGL就这么取代了旧版OpenGL
引用:
(33条消息) glfw+glad配置后glbegin未定义标识符错误_dook33的博客-CSDN博客_未定义标识符gl
代码的解释
计算机图形学_中国大学MOOC(慕课) (icourse163.org)
直接CV,也不大行,还是尝试看一看具体内容吧
核心模式,直接抛弃了之前旧OpenGL的那个模式?VAO,VBO,着色器……
这个慕课,看样子是跳过固定管线,直接从现代的OpenGL开始了……
太强了。不是我这样的小白搞得来的。 能力有限,跟着混一混吧……看看热闹
这个现代流程和我脑子里那个古早的流程完全不是一回事
OpenGL是个大的状态机,这个状态,又叫作上下文
GLAD是管理函数指针的。图形学+指针=劝退
《现代》
我只会传统的。
核心模式,要求用VAO
VAO,我只知道它一次传一大串数据到GPU
就听明白了这一句……
VAO,VBO,是把数据发送到显卡GPU上的。之后,就是用shader,来处理这些数据
以后用着色器就可以了,所以这些用不到的就删除了
跟着视频又看了一遍代码,加了点注释
/***
* 例程 绘制三角形 (MAKE后运行时可删除ALL_BUILD,也可以将Task-triangle设为默认启动工程)
* 步骤:
* 1-初始化: GLFW窗口,GLAD。
* 2-数据处理: 给定顶点数据,生成并绑定VAO&VBO(准备在GPU中进行处理),设置顶点属性指针(本质上就是告诉OpenGL如何处理数据)。
* 3-着色器: 给出顶点和片段着色器,然后链接为着色器程序,渲染时使用着色器程序。
* 4-渲染: 清空缓冲,绑定纹理,使用着色器程序,绘制三角形,交换缓冲区检查触发事件后释放资源
*/
#include <iostream>
#include "glad/glad.h"
#include "GLFW/glfw3.h"
// 三角形的顶点数据
const float triangle[] = {
// ---- 位置 ----
-0.5f, -0.5f, 0.0f, // 左下
0.5f, -0.5f, 0.0f, // 右下
0.0f, 0.5f, 0.0f // 正上
};
// 屏幕宽,高
int screen_width = 1280;
int screen_height = 720;
int main() {
// 第一部分,初始化GLFW和GLAD,分为4个步骤进行/
// 初始化GLFW
glfwInit(); // 初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL版本为3.3,主次版本号均设为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式(无需向后兼容性)
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 如果使用的是Mac OS X系统,需加上这行
glfwWindowHint(GLFW_RESIZABLE, false); // 不可改变窗口大小
// 创建窗口(宽、高、窗口名称)
auto window = glfwCreateWindow(screen_width, screen_height, "Triangle", nullptr, nullptr);
if (window == nullptr) { // 如果窗口创建失败,输出Failed to Create OpenGL Context
std::cout << "Failed to Create OpenGL Context" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // 将窗口的上下文设置为当前线程的主上下文
// 初始化GLAD,加载OpenGL函数指针地址的函数
// 在调用任何OpenGL函数之前
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 指定当前视口尺寸(前两个参数为左下角位置,后两个参数是渲染窗口宽、高)
glViewport(0, 0, screen_width, screen_height);
// 第二部分,数据处理,通过VAO,VBO,把顶点数据发送到显卡GPU上,并设置属性指针,告诉GPU如何解释这些数据/
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO 核心模式要求必须使用VAO
glGenVertexArrays(1, &vertex_array_object);//先生成
glBindVertexArray(vertex_array_object);//再绑定
GLuint vertex_buffer_object; // == VBO
glGenBuffers(1, &vertex_buffer_object);//先生成
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);//再绑定
// 将顶点数据绑定至当前默认的缓冲中
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW);
// 设置顶点属性指针——告知OpenGL,如何解释这些数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 解绑VAO和VBO 为了代码更规范
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 第三部分,着色器,用来处理GPU里的数据//
// 顶点着色器和片段着色器源码【GLSL】
const char* vertex_shader_source =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" // 位置变量的属性位置值为0
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n" //内建变量
"}\n\0";
const char* fragment_shader_source =
"#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 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
char info_log[512];
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 片段着色器
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 链接顶点和片段着色器至一个着色器程序
int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
// 检查着色器是否成功链接,如果链接失败,打印错误信息
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << info_log << std::endl;
}
// 删除着色器 需要的着色器程序已经拿到了,这个没用了,就删除了
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
// 线框模式
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 第4部分,渲染//
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序 【之前链接好的那个】
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindVertexArray(0); // 解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
// 第五部分,善后工作
// 删除VAO和VBO
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteBuffers(1, &vertex_buffer_object);
// 清理所有的资源并正确退出程序
glfwTerminate();
return 0;
}
五个部分,绘制出了三角形
挺清晰的,现代OpenGL
VAO和VBO的介绍
百度,启动
网页1
可以理解为VBO就是显存中的一个存储区域,可以保持大量的顶点属性信息。并且可以开辟很多个VBO,每个VBO在OpenGL中有它的唯一标识ID,这个ID对应着具体的VBO的显存地址,通过这个ID可以对特定的VBO内的数据进行存取操作。
【ID,显存地址,指针?有计算机内味儿了】
还有个图,这个图画的挺漂亮的,虽然看不懂。
只能看明白这里面有一堆指针。
引用:
网页2
这个里面有代码的解释:
能看明白的一共是这么几句解释VBO的代码:
//声明一个int作为VBO对象的id unsigned int VBO; //生成一个Buffer对象,id为VBO glGenBuffers(1, &VBO); //将缓冲对象绑定到顶点缓冲对象,相当于指定了这个缓冲对象是啥类型的 glBindBuffer(GL_ARRAY_BUFFER, VBO); //传数据给VBO glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); //设置如何解释这些数据 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); //开启顶点属性 glEnableVertexAttribArray(0);
具体解释如下:
VBO也就是一个OpenGL里的结构体,它用来存一些顶点数据方便一次性给GPU内存多发一点。
首先,按照惯例,先生成一个缓冲对象(注意这里还不是顶点缓冲对象哦),得到它的id:
unsigned int VBO;//声明一个int作为VBO对象的id glGenBuffers(1, &VBO);//生成一个Buffer对象,id为VBO
这一步就是将缓冲对象绑定到顶点缓冲对象,相当于就是指定了这个缓冲对象是啥类型的
glBindBuffer(GL_ARRAY_BUFFER, VBO);
id,,和上个链接好像呼应上了
现在,我们任何在GL_ARRAY_BUFFER上的缓冲调用都是对VBO这个对象起作用的。这里有点难理解吧?我再来举例子。
C语言总学过吧
我们声明一个变量是用 int a;
给他初始化后就能用 addOne(a)各种函数来调用了。OpenGL里呢,没有那么方便,因为他的函数很多是直接调用“类型”的,就这样的:
addOne(int),你传一个a进去,不认得啊。
所以呢要首先bind(int,a)把a跟int这个类型绑定,告诉它现在int就是专指a了!然后addOne(int)这个函数呢,就知道是给a加一了。简单理解,这个例子里int就是GL_ARRAY_BUFFER,a就是VBO。下一步,是这句:
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
把我们之前的那个一维数组,传给了GL_ARRAY_BUFFER对象,注意它已经被我们的VBO给绑定了哦,所以就传给了VBO对象。
这个函数的介绍如下:
- 它的第一个参数是目标缓冲的类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上。
- 第二个参数指定传输数据的大小(以字节为单位);用一个简单的sizeof计算出顶点数据大小就行。
- 第三个参数是我们希望发送的实际数据。
- 第四个参数指定了我们希望显卡如何管理给定的数据。它有三种形式:
- GL_STATIC_DRAW :数据不会或几乎不会改变。
- GL_DYNAMIC_DRAW:数据会被改变很多。
- GL_STREAM_DRAW :数据每次绘制时都会改变。
说那么多,其实就一句话,把一个一维数组,给了一个结构体来存储。
然后:一个简简单单的一维数组,咋让顶点着色器知道这代表了三个顶点呢?用这两句:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0);
第一行是告诉OpenGL如何解释数据,第二行是启用顶点属性。
简单来说就是一维数组是如何变成三维点的。注意这里一个点的信息只有位置坐标,之后还会有颜色、贴图等信息,这样就要修改一个单位的大小和单位之间的偏移量了。引用自:
(33条消息) 是人都能懂,通俗解释VBO和VAO在OpenGL中的作用_kiss_the_rain86的博客-CSDN博客_opengl vbo
网页3
这个里面有完整点的代码
VBO的B为Buffer之意,用来存储顶点数据;VAO的A为Array,但我认为理解为 Attribute(属性) 之意更好,意思是 Buffer(VBO)的属性。
即,我们用VBO来存储数据,而用VAO来告诉计算机这些数据分别有什么属性、起什么作用。
《自动》
VAO如何解释数据?这么解释:
当数据来自同一数组时:
float buffer = { //顶点坐标(3个一组) //顶点颜色(3个一组) //纹理坐标(2个一组) 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f }; //把对VBO中数据的描述存到了一个VAO中 //在一些复杂的OpenGL程序中,VBO可能会有多个,但VAO只有一个 //vertex coord glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // color attribute glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); // texture coord attribute glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);
当数据来自不同的数组时:
用一个VAO来解释多个VBO的操作流程就如下
首先,绑定VAO,以告知OpenGL程序该使用这个VAO来对VBO做出解释。 然后,绑定第一个VBO,向这个VBO中写入数据,告知VAO该如何解释这个VBO的信息; 然后,解绑这个VBO。 然后,绑定第二个VBO,向这个VBO中写入数据,并在VAO中保存该如何解释这个VBO的信息; 然后,解绑这个VBO。 ……
unsigned int VBO[2], VAO; glGenVertexArrays(1, &VAO); glGenBuffers(2, VBO); //=========================绑定VAO=============================== glBindVertexArray(VAO); //=============================================================== //=======================绑定第一个VBO============================ glBindBuffer(GL_ARRAY_BUFFER, VBO[0]); //=============================================================== glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), &sphereVertices[0], GL_STATIC_DRAW); //向第一个VBO中写入数据 //================告知VAO该如何解释第一个VBO的信息================= glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); //=============================================================== //=======================解绑第一个VBO=========================== glBindBuffer(GL_ARRAY_BUFFER, 0); //=============================================================== //=======================绑定第二个VBO============================ glBindBuffer(GL_ARRAY_BUFFER, VBO[1]); //=============================================================== glBufferData(GL_ARRAY_BUFFER, sizeof(texVertrices), texVertrices, GL_STATIC_DRAW);//向第二个VBO中写入数据 //================告知VAO该如何解释第二个VBO的信息================= glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glEnableVertexAttribArray(1); //=============================================================
引用:
网页4
这个可能偏应用一点:
一个enable函数的解释
引用:
看来,这个VAO+VBO里的VAO,和之前理解的用来加快传数据速度的VAO,好像不大一样。
一个VAO,两个VBO,两个三角形
实际运用吧这应该算是
初始
一开始,根据猜测,改写成了这样:
代码:
/***
* 例程 绘制三角形 (MAKE后运行时可删除ALL_BUILD,也可以将Task-triangle设为默认启动工程)
* 步骤:
* 1-初始化: GLFW窗口,GLAD。
* 2-数据处理: 给定顶点数据,生成并绑定VAO&VBO(准备在GPU中进行处理),设置顶点属性指针(本质上就是告诉OpenGL如何处理数据)。
* 3-着色器: 给出顶点和片段着色器,然后链接为着色器程序,渲染时使用着色器程序。
* 4-渲染: 清空缓冲,绑定纹理,使用着色器程序,绘制三角形,交换缓冲区检查触发事件后释放资源
*/
#include <iostream>
#include "glad/glad.h"
#include "GLFW/glfw3.h"
// 三角形的顶点数据
const float triangle[] = {
// ---- 位置 ----
-0.5f, -0.5f, 0.0f, // 左下
0.5f, -0.5f, 0.0f, // 右下
0.0f, 0.5f, 0.0f // 正上
};
const float triangle2[] = {
// ---- 位置 ----
0.6f, -0.5f, 0.0f, // 左下
0.8f, -0.5f, 0.0f, // 右下
0.7f, 0.5f, 0.0f // 正上
};
// 屏幕宽,高
int screen_width = 1280;
int screen_height = 720;
int main() {
// 第一部分,初始化GLFW和GLAD,分为4个步骤进行/
// 初始化GLFW
glfwInit(); // 初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL版本为3.3,主次版本号均设为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式(无需向后兼容性)
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 如果使用的是Mac OS X系统,需加上这行
glfwWindowHint(GLFW_RESIZABLE, false); // 不可改变窗口大小
// 创建窗口(宽、高、窗口名称)
auto window = glfwCreateWindow(screen_width, screen_height, "Triangle", nullptr, nullptr);
if (window == nullptr) { // 如果窗口创建失败,输出Failed to Create OpenGL Context
std::cout << "Failed to Create OpenGL Context" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // 将窗口的上下文设置为当前线程的主上下文
// 初始化GLAD,加载OpenGL函数指针地址的函数
// 在调用任何OpenGL函数之前
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 指定当前视口尺寸(前两个参数为左下角位置,后两个参数是渲染窗口宽、高)
glViewport(0, 0, screen_width, screen_height);
// 第二部分,数据处理,通过VAO,VBO,把顶点数据发送到显卡GPU上,并设置属性指针,告诉GPU如何解释这些数据/
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO 核心模式要求必须使用VAO
glGenVertexArrays(1, &vertex_array_object);//先生成
glBindVertexArray(vertex_array_object);//再绑定
GLuint vertex_buffer_object[2]; // == VBO
glGenBuffers(2, &vertex_buffer_object[0]);//先生成
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解绑VAO和VBO 为了代码更规范
glBindVertexArray(0);
// 第三部分,着色器,用来处理GPU里的数据//
// 顶点着色器和片段着色器源码【GLSL】
const char* vertex_shader_source =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" // 位置变量的属性位置值为0
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n" //内建变量
"}\n\0";
const char* fragment_shader_source =
"#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 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
char info_log[512];
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 片段着色器
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 链接顶点和片段着色器至一个着色器程序
int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
// 检查着色器是否成功链接,如果链接失败,打印错误信息
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << info_log << std::endl;
}
// 删除着色器 需要的着色器程序已经拿到了,这个没用了,就删除了
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
// 线框模式
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 第4部分,渲染//
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序 【之前链接好的那个】
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindVertexArray(0); // 解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
// 第五部分,善后工作
// 删除VAO和VBO
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteBuffers(1, &vertex_buffer_object[0]);
// 清理所有的资源并正确退出程序
glfwTerminate();
return 0;
}
运行结果,明显不对……
不对就改嘛,实践——认识——再实践——。。。
小白,所以,百度,启动
修改1
《这个问题和我遇到的差不多》《也使用属性0》
想起了上面的某个图
又想起了代码里的某一行
好像能呼应上似的
引用:
修改2
这个说是,渲染绘制之前,需要设置一下这个指针
引用:
进行修改
然后,就修改了 这两部分,死马当活马医而已,因为我是小白,只会百度
// 第二部分,数据处理,通过VAO,VBO,把顶点数据发送到显卡GPU上,并设置属性指针,告诉GPU如何解释这些数据/
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO 核心模式要求必须使用VAO
glGenVertexArrays(1, &vertex_array_object);//先生成
glBindVertexArray(vertex_array_object);//再绑定
GLuint vertex_buffer_object[2]; // == VBO
glGenBuffers(2, &vertex_buffer_object[0]);//先生成
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解绑VAO和VBO 为了代码更规范
glBindVertexArray(0);
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序 【之前链接好的那个】
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindVertexArray(0); // 解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
修改后
总的代码:
/***
* 例程 绘制三角形 (MAKE后运行时可删除ALL_BUILD,也可以将Task-triangle设为默认启动工程)
* 步骤:
* 1-初始化: GLFW窗口,GLAD。
* 2-数据处理: 给定顶点数据,生成并绑定VAO&VBO(准备在GPU中进行处理),设置顶点属性指针(本质上就是告诉OpenGL如何处理数据)。
* 3-着色器: 给出顶点和片段着色器,然后链接为着色器程序,渲染时使用着色器程序。
* 4-渲染: 清空缓冲,绑定纹理,使用着色器程序,绘制三角形,交换缓冲区检查触发事件后释放资源
*/
#include <iostream>
#include "glad/glad.h"
#include "GLFW/glfw3.h"
// 三角形的顶点数据
const float triangle[] = {
// ---- 位置 ----
-0.5f, -0.5f, 0.0f, // 左下
0.5f, -0.5f, 0.0f, // 右下
0.0f, 0.5f, 0.0f // 正上
};
const float triangle2[] = {
// ---- 位置 ----
0.6f, -0.5f, 0.0f, // 左下
0.8f, -0.5f, 0.0f, // 右下
0.7f, 0.5f, 0.0f // 正上
};
// 屏幕宽,高
int screen_width = 1280;
int screen_height = 720;
int main() {
// 第一部分,初始化GLFW和GLAD,分为4个步骤进行/
// 初始化GLFW
glfwInit(); // 初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL版本为3.3,主次版本号均设为3
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式(无需向后兼容性)
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 如果使用的是Mac OS X系统,需加上这行
glfwWindowHint(GLFW_RESIZABLE, false); // 不可改变窗口大小
// 创建窗口(宽、高、窗口名称)
auto window = glfwCreateWindow(screen_width, screen_height, "Triangle", nullptr, nullptr);
if (window == nullptr) { // 如果窗口创建失败,输出Failed to Create OpenGL Context
std::cout << "Failed to Create OpenGL Context" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window); // 将窗口的上下文设置为当前线程的主上下文
// 初始化GLAD,加载OpenGL函数指针地址的函数
// 在调用任何OpenGL函数之前
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// 指定当前视口尺寸(前两个参数为左下角位置,后两个参数是渲染窗口宽、高)
glViewport(0, 0, screen_width, screen_height);
// 第二部分,数据处理,通过VAO,VBO,把顶点数据发送到显卡GPU上,并设置属性指针,告诉GPU如何解释这些数据/
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO 核心模式要求必须使用VAO
glGenVertexArrays(1, &vertex_array_object);//先生成
glBindVertexArray(vertex_array_object);//再绑定
GLuint vertex_buffer_object[2]; // == VBO
glGenBuffers(2, &vertex_buffer_object[0]);//先生成
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解绑VAO和VBO 为了代码更规范
glBindVertexArray(0);
// 第三部分,着色器,用来处理GPU里的数据//
// 顶点着色器和片段着色器源码【GLSL】
const char* vertex_shader_source =
"#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n" // 位置变量的属性位置值为0
"void main()\n"
"{\n"
" gl_Position = vec4(aPos, 1.0);\n" //内建变量
"}\n\0";
const char* fragment_shader_source =
"#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 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
glCompileShader(vertex_shader);
int success;
char info_log[512];
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(vertex_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 片段着色器
int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
glCompileShader(fragment_shader);
// 检查着色器是否成功编译,如果编译失败,打印错误信息
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(fragment_shader, 512, NULL, info_log);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << info_log << std::endl;
}
// 链接顶点和片段着色器至一个着色器程序
int shader_program = glCreateProgram();
glAttachShader(shader_program, vertex_shader);
glAttachShader(shader_program, fragment_shader);
glLinkProgram(shader_program);
// 检查着色器是否成功链接,如果链接失败,打印错误信息
glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader_program, 512, NULL, info_log);
std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << info_log << std::endl;
}
// 删除着色器 需要的着色器程序已经拿到了,这个没用了,就删除了
glDeleteShader(vertex_shader);
glDeleteShader(fragment_shader);
// 线框模式
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// 第4部分,渲染//
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序 【之前链接好的那个】
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindVertexArray(0); // 解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
// 第五部分,善后工作
// 删除VAO和VBO
glDeleteVertexArrays(1, &vertex_array_object);
glDeleteBuffers(1, &vertex_buffer_object[0]);
// 清理所有的资源并正确退出程序
glfwTerminate();
return 0;
}
运行结果
结果,符合预期
代码,是正经的写法吗?不知道……小白总是一问三不知,我就是小白,所以……先这样吧……
一点函数解释
名称
glBindBuffer- 绑定一个命名(ID)的缓冲区对象
void glBindBuffer(GLenum target,GLuint buffer);
target
指定缓冲区对象绑定的目标。
符号常量必须为GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY_BUFFER。【只能二选一?】
buffer
指定缓冲区对象的名称(ID)。
glBindBuffer允许您创建或使用命名缓冲区对象。 调用glBindBuffer,目标设置为GL_ARRAY_BUFFER或GL_ELEMENT_ARRAY_BUFFER,缓冲区设置为新缓冲区对象的名称,这样就将缓冲区对象名称绑定到目标了。 当缓冲区对象绑定到目标时,该目标的先前绑定将自动中断。
缓冲区对象名称是无符号整数。 0值保留,但每个缓冲区对象目标没有默认缓冲区对象。 相反,缓冲区设置为0可以有效地取消绑定先前绑定的任何缓冲区对象,并恢复该缓冲区对象目标的客户机内存使用情况。 缓冲区对象名称和相应的缓冲区对象内容对于当前GL渲染上下文的共享对象空间是本地的。
引用:
(31条消息) GLES2.0中文API-glBindBuffer_flycatdeng的博客-CSDN博客_glbindbuffer
GLuint vbo; glGenBuffers(1,&vbo); GLuint vbo[3]; glGenBuffers(3,vbo);
glGenBuffers()函数仅仅是生成一个缓冲对象的名称,这个缓冲对象并不具备任何意义,它仅仅是个缓冲对象,还不是一个顶点数组缓冲,它类似于C语言中的一个指针变量【这个类比挺好的】,我们可以分配内存对象并且用它的名称来引用这个内存对象。OpenGL有很多缓冲对象类型,那么这个缓冲对象到底是什么类型,就要用到下面的glBindBuffer()函数了。
glBindBuffer(GL_ARRAY_BUFFER, VBO); //VBO变成了一个顶点缓冲类型 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
第一个就是缓冲对象的类型,第二个参数就是要绑定的缓冲对象的名称,也就是我们在上一个函数里生成的名称,使用该函数将缓冲对象绑定到OpenGL上下文环境中以便使用。如果把target绑定到一个已经创建好的缓冲对象,那么这个缓冲对象将为当前target的激活对象;但是如果绑定的buffer值为0,那么OpenGL将不再对当前target使用任何缓存对象。
在OpenGL红宝书中给出了一个恰当的比喻:绑定对象的过程就像设置铁路的道岔开关,每一个缓冲类型中的各个对象就像不同的轨道一样,我们将开关设置为其中一个状态,那么之后的列车都会驶入这条轨道。
- 我要把数据存入顶点缓冲区,但是顶点缓冲区可以有很多缓冲对象,我需要传入哪个呢,于是我就要提前绑定一个,之后,我只要向顶点缓冲区内传入数据,这个数据就会自动进入被绑定的那个对象里面。
- 之后调用glBufferData()传输所需数据,其中第一个参数就是要制定缓冲类型,根据这个类型锁定当前唯一的目标缓冲。
引用:
(31条消息) glGenBuffers与glBindBuffer理解_莫之的博客-CSDN博客_glgenbuffers
《渲染时》
glVertexAttribPointer_百度百科 (baidu.com)
试了试,这么搞也能画出来图
// 第二部分,数据处理,通过VAO,VBO,把顶点数据发送到显卡GPU上,并设置属性指针,告诉GPU如何解释这些数据/
// 生成并绑定VAO和VBO
GLuint vertex_array_object; // == VAO 核心模式要求必须使用VAO
glGenVertexArrays(1, &vertex_array_object);//先生成
glBindVertexArray(vertex_array_object);//再绑定
GLuint vertex_buffer_object[2]; // == VBO
glGenBuffers(2, &vertex_buffer_object[0]);//先生成,相当于C里声明的指针
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);//再绑定, 相当于声明指针指向的变量类型,同时设置一下状态机的状态(火车轨道变轨,变完了后面的车都得按变了的轨道走)
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle), triangle, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
//glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);//再绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(triangle2), triangle2, GL_STATIC_DRAW); // 将顶点数据绑定至当前默认的缓冲中
//glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置顶点属性指针——告知OpenGL,如何解释这些数据
//glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// 解绑VAO和VBO 为了代码更规范
glBindVertexArray(0);
// 渲染循环
while (!glfwWindowShouldClose(window)) {
// 清空颜色缓冲
glClearColor(0.0f, 0.34f, 0.57f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 使用着色器程序 【之前链接好的那个】
glUseProgram(shader_program);
// 绘制三角形
glBindVertexArray(vertex_array_object); // 绑定VAO
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[0]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 【渲染时】设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); // 设置指针位置,以便后续从此处读取数据进行渲染
glDrawArrays(GL_TRIANGLES, 0, 3); // 绘制三角形
glBindVertexArray(0); // 解除绑定
// 交换缓冲并且检查是否有触发事件(比如键盘输入、鼠标移动等)
glfwSwapBuffers(window);//双缓冲
glfwPollEvents();
}
后记
状态机 ,铁路岔道开关,所以最早的时候,只渲染不设指针的时候,指针还停留在向VBO里传数据时设置的地址,所以只画出了一个。
能自圆其说,对不对?不知道。小白一个,要求不能太高。
红宝书……好像是个字典。