openGL入门之1、基础概念到三角形绘制_opengl 为何 分解为三角形 绘制(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量,它在幕后是vec4类型的。
我们将gl_Position设置的值会成为该顶点着色器的输出。
由于我们的输入是一个3分量的向量,我们必须把它转换为4分量的。
我们可以把vec3的数据作为vec4构造器的参数,同时把w分量设置为1.0f(我们会在后面解释为什么)来完成这一任务。
*/
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}


注意:当前这个顶点着色器可能是我们能想到的最简单的顶点着色器了,因为我们对输入数据什么都没有处理就把它传到着色器的输出了。在真实的程序里输入数据通常都不是标准化设备坐标,所以我们首先必须先把它们转换至OpenGL的可视区域内。  
 **向量**:GLSL有一个向量数据类型,它包含1到4个float分量,包含的数量可以从它的后缀数字看出来。由于每个顶点都有一个3D坐标,我们就创建一个vec3输入变量aPos。  
 在图形编程中我们经常会使用向量这个数学概念,因为它简明地表达了任意空间中的位置和方向,并且它有非常有用的数学属性。在GLSL中一个向量有最多4个分量,每个分量值都代表空间中的一个坐标,它们可以通过vec.x、vec.y、vec.z和vec.w来获取。注意vec.w分量不是用作表达空间中的位置的(我们处理的是3D不是4D),而是用在所谓透视除法(Perspective Division)上.


**编译着色器**:



//暂时将顶点着色器的源代码硬编码在代码文件顶部的C风格字符串中:
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”;
/

创建一个着色器对象,注意还是用ID来引用的。
所以我们储存这个顶点着色器为unsigned int,然后用glCreateShader创建这个着色器:
我们把需要创建的着色器类型以参数形式提供给glCreateShader。由于我们正在创建一个顶点着色器,传递的参数是GL_VERTEX_SHADER。
*/
unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);

/*
这个着色器源码附加到着色器对象上,然后编译它:
glShaderSource函数把要编译的着色器对象作为第一个参数。第二参数指定了传递的源码字符串数量,这里只有一个。第三个参数是顶点着色器真正的源码,第四个参数我们先设置为NULL。
*/
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);


**检查是否错误编译**:



你可能会希望检测在调用glCompileShader后编译是否成功了,
如果没成功的话,你还会希望知道错误是什么,这样你才能修复它们。
检测编译时错误可以通过以下代码来实现:

int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
首先我们定义一个整型变量来表示是否成功编译,还定义了一个储存错误消息(如果有的话)的容器。
然后我们用glGetShaderiv检查是否编译成功。
如果编译失败,我们会用glGetShaderInfoLog获取错误消息,然后打印它。

if(!success)
{
glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
std::cout << “ERROR::SHADER::VERTEX::COMPILATION_FAILED\n” << infoLog << std::endl;
}


**片段着色器**:片段着色器所做的是计算像素最后的颜色输出。  
 **计算机图形中颜色表示**:在计算机图形中颜色被表示为有4个元素的数组:红色、绿色、蓝色和alpha(透明度)分量,通常缩写为RGBA。当在OpenGL或GLSL中定义一个颜色的时候,我们把颜色每个分量的强度设置在0.0到1.0之间。比如说我们设置红为1.0f,绿为1.0f,我们会得到两个颜色的混合色,即黄色。这三种颜色分量的不同调配可以生成超过1600万种不同的颜色!



#version 330 core
/*
片段着色器只需要一个输出变量,这个变量是一个4分量向量,
它表示的是最终的输出颜色,我们应该自己将其计算出来
声明输出变量可以使用out关键字,这里我们命名为FragColor
*/
out vec4 FragColor;

void main()
{
//我们将一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

//编译
//编译片段着色器的过程与顶点着色器类似,只不过我们使用GL_FRAGMENT_SHADER常量作为着色器类型:
unsigned int fragmentShader;
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);


**着色器程序**:多个着色器合并之后并最终链接完成的版本  
 如果要使用刚才编译的着色器我们必须把它们链接(Link)为一个着色器程序对象,然后在渲染对象的时候激活这个着色器程序。**已激活着色器程序的着色器将在我们发送渲染调用的时候被使用。**  
 注意:当链接着色器至一个程序的时候,它会把每个着色器的输出链接到下个着色器的输入。当输出和输入不匹配的时候,你会得到一个连接错误。



//创建一个程序对象很简单:glCreateProgram函数创建一个程序,并返回新创建程序对象的ID引用。
unsigned int shaderProgram;
shaderProgram = glCreateProgram();
//现在我们需要把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们:
glAttachShader(shaderProgram, vertexShader);//把着色器附加到了程序上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//然后用glLinkProgram链接。

//得到的结果就是一个程序对象,我们可以调用glUseProgram函数,
//用刚创建的程序对象作为它的参数,以激活这个程序对象:
glUseProgram(shaderProgram);//调用之后每个着色器调用和渲染调用都会使用这个程序对象

//在把着色器对象链接到程序对象以后,记得删除着色器对象,我们不再需要它们了
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);


我们已经把输入顶点数据发送给了GPU,并指示了GPU如何在顶点和片段着色器中处理它。就快要完成了,但还没结束,OpenGL还不知道它该如何解释内存中的顶点数据,以及它该如何将顶点数据链接到顶点着色器的属性上。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/96ad3f0c887f4db49fc0bc857fa6e8c4.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


## 3、实践


## 3.1、三角形


![在这里插入图片描述](https://img-blog.csdnimg.cn/80b0a6782ae442c299eb357cbe5eefba.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



#include
#include <glad/glad.h>
#include <GLFW/glfw3.h>

using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

/*
顶点着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
使用in关键字,在顶点着色器中声明所有的输入顶点属性
layout (location = 0)设定了输入变量(顶点属性)的位置值;并且后面链接顶点属性设置的时候会通过顶点属性位置值进行绑定
vec3 表示三个分量的值(注意每个值都是浮点数类型)
第二行就是表示在位置0的地方有一个三个分量的输入变量aPos
将输入的三维aPos转换为四维并赋值给全局变量gl_Position
/
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”;
/*
片段着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
out 表示输出变量 四维向量的变量FragColor
暂时自定义为 一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
RGBA的四分量向量
片段着色器所做的是计算像素最后的颜色输出。为了让事情更简单暂时自定义为 我们的片段着色器将会一直输出橘黄色。
/
const char
fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{\n”
" FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f);\n"
“}\n\0”;

int main()
{
/1、 初始化opengl窗口***************/
//glfw 初始化和配置
glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef APPLE//如果时mac os xp等系统则要进行配置一下才能生效
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

//glfw创建窗口
GLFWwindow* 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);//GLFW将我们窗口的上下文设置为当前线程的主上下文
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

//GLAD加载所有的opengl函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

/******************************2、 着色器编译编译链接 *********************************************/
//编写 编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//传入GL_VERTEX_SHADER表示创建顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(vertexShader);//编译
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::Vertex::Compilation Fail\n" << infoLog << endl;
}

//编写 编译片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//传入GL_FRAGMENT_SHADER 表示创建片段着色器
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(fragmentShader);//编译
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glad_glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::fragment::Compilation Fail:" << infoLog << endl;
}

//链接着色器程序
unsigned int shaderProgram = glCreateProgram(); //创建着色器程序
glAttachShader(shaderProgram, vertexShader);//之前编译的着色器附加到程序对象上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//链接它们
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);//检查程序是否链接报错
if (!success)
{
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    cout << "ERROR:Program::shaderProgram::Link Fail:" << infoLog << endl;
}
glDeleteShader(vertexShader);//着色器程序已链接完毕则不再需要可以进行delete了
glDeleteShader(fragmentShader);


/******************************3、 顶点属性缓存这些   *********************************************/
//设置顶点数据 和顶点缓存对象 以及配置顶点属性
//标准化设备坐标  三角形的三个顶点
float vertices[] = {
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f,
    0.0f, 0.5f, 0.0f
};
unsigned int VBO, VAO;
glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
glGenBuffers(1, &VBO);//创建顶点缓冲对象
glBindVertexArray(VAO);//绑定顶点数组

glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存

/*
    0;指定我们要配置的顶点属性 就是顶点着色器里面location那个 
*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void *)0);
glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性

glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

//渲染循环
//程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
{
    //可接收键盘输入esc从而退出
    processInput(window);

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
    glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

    
    glUseProgram(shaderProgram);//使用着色器程序
    glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
    /*
        我们希望绘制的是一个三角形,这里传递GL_TRIANGLES给它。
        第二个参数指定了顶点数组的起始索引,我们这里填0。
        最后一个参数指定我们打算绘制多少个顶点,这里是3
        (我们只从我们的数据中渲染一个三角形,它只有3个顶点长)
    */
    glDrawArrays(GL_TRIANGLES, 0, 3);//它使用当前激活的着色器,之前定义的顶点属性配置,和VBO的顶点数据(通过VAO间接绑定)来绘制图元。
    // glBindVertexArray(0); 按道理我们需要每次都解绑定,但是本程序只有一个则可以不用了

    glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
    glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
}

glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);

//释放/删除之前的分配的所有资源
glfwTerminate();

return 0;

}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
//左上角坐标xy和宽高
glViewport(0, 0, width, height);//OpenGL的显示试图
}


## 3.2、四边形


**索引缓冲对象EBO**:  
 背景:绘制矩形的时候如果不断绘制三角形那么就有顶点重复问题;如果我们可以只储存不同的顶点,并设定绘制这些顶点的顺序。这样子我们只要储存4个顶点就能绘制矩形了,之后只要指定绘制的顺序就行了。  
 工作方式:索引缓冲对象的工作方式正是这样的。和顶点缓冲对象一样,EBO也是一个缓冲,它专门储存索引,OpenGL调用这些顶点的索引来决定该绘制哪个顶点。



//顶点
float vertices[] = {
0.5f, 0.5f, 0.0f, // 右上角
0.5f, -0.5f, 0.0f, // 右下角
-0.5f, -0.5f, 0.0f, // 左下角
-0.5f, 0.5f, 0.0f // 左上角
};
//索引
unsigned int indices[] = { // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3 // 第二个三角形
};

unsigned int EBO;
glGenBuffers(1, &EBO);//定义索引缓冲对象
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//GL_ELEMENT_ARRAY_BUFFER类型绑定到EBO
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//glBufferData把索引复制到缓冲里

//用glDrawElements来替换glDrawArrays函数,来指明我们从索引缓冲渲染。
//使用glDrawElements时,我们会使用当前绑定的索引缓冲对象中的索引进行绘制:
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
/*
第一个参数:我们绘制的模式。
第二个参数:我们打算绘制顶点的个数
第三个参数:索引的类型,这里是GL_UNSIGNED_INT
第四个参数:我们可以指定EBO中的偏移量(或者传递一个索引数组,但是这是当你不在使用索引缓冲对象的时候),但是我们会在这里填写0。
*/


注意:顶点数组对象同样可以保存索引缓冲对象的绑定状态。VAO绑定时正在绑定的索引缓冲对象会被保存为VAO的元素缓冲对象。绑定VAO的同时也会自动绑定EBO。但是当目标是GL\_ELEMENT\_ARRAY\_BUFFER的时候,VAO会储存glBindBuffer的函数调用。这也意味着它也会储存解绑调用,所以确保你没有在解绑VAO之前解绑索引数组缓冲,否则它就没有这个EBO配置了。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/4ba32a30a3334ab4a1274c889ccc3ef2.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/1c99f99540a94b328e0879379e2431ed.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



#include
#include <glad/glad.h>
#include <GLFW/glfw3.h>

using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

/*
顶点着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
使用in关键字,在顶点着色器中声明所有的输入顶点属性
layout (location = 0)设定了输入变量(顶点属性)的位置值;并且后面链接顶点属性设置的时候会通过顶点属性位置值进行绑定
vec3 表示三个分量的值(注意每个值都是浮点数类型)
第二行就是表示在位置0的地方有一个三个分量的输入变量aPos
将输入的三维aPos转换为四维并赋值给全局变量gl_Position
/
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”;
/*
片段着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
out 表示输出变量 四维向量的变量FragColor
暂时自定义为 一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
RGBA的四分量向量
片段着色器所做的是计算像素最后的颜色输出。为了让事情更简单暂时自定义为 我们的片段着色器将会一直输出橘黄色。
/
const char
fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{\n”
" FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f);\n"
“}\n\0”;

int main()
{
/1、 初始化opengl窗口***************/
//glfw 初始化和配置
glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef APPLE//如果时mac os xp等系统则要进行配置一下才能生效
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

//glfw创建窗口
GLFWwindow* 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);//GLFW将我们窗口的上下文设置为当前线程的主上下文
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

//GLAD加载所有的opengl函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

/******************************2、 着色器编译编译链接 *********************************************/
//编写 编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//传入GL_VERTEX_SHADER表示创建顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(vertexShader);//编译
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::Vertex::Compilation Fail\n" << infoLog << endl;
}

//编写 编译片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//传入GL_FRAGMENT_SHADER 表示创建片段着色器
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(fragmentShader);//编译
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glad_glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::fragment::Compilation Fail:" << infoLog << endl;
}

//链接着色器程序
unsigned int shaderProgram = glCreateProgram(); //创建着色器程序
glAttachShader(shaderProgram, vertexShader);//之前编译的着色器附加到程序对象上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//链接它们
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);//检查程序是否链接报错
if (!success)
{
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    cout << "ERROR:Program::shaderProgram::Link Fail:" << infoLog << endl;
}
glDeleteShader(vertexShader);//着色器程序已链接完毕则不再需要可以进行delete了
glDeleteShader(fragmentShader);


/******************************3、 顶点属性缓存这些   *********************************************/
//设置顶点数据 和顶点缓存对象 以及配置顶点属性
//标准化设备坐标  顶点
float vertices[] = {
     0.5f,  0.5f, 0.0f,  // top right
     0.5f, -0.5f, 0.0f,  // bottom right
    -0.5f, -0.5f, 0.0f,  // bottom left
    -0.5f,  0.5f, 0.0f   // top left 
};
//索引数组
unsigned int indices[] = {  // note that we start from 0!
    0, 1, 3,  // first Triangle
    1, 2, 3   // second Triangle
};
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);//创建一个顶点数组 与顶点属性相绑定 
glGenBuffers(1, &VBO);//创建顶点缓冲对象
glGenBuffers(1, &EBO);

glBindVertexArray(VAO);//绑定顶点数组 配置顶点属性

glBindBuffer(GL_ARRAY_BUFFER, VBO);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);//将GL_ELEMENT_ARRAY_BUFFER类型的缓冲与EBO绑定
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);//把索引复制到缓冲里

/*
    0;指定我们要配置的顶点属性 就是顶点着色器里面location那个 
*/
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void *)0);
glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性

glBindBuffer(GL_ARRAY_BUFFER, 0);//VBO 已经与顶点属性数组VAO进行绑定了 那么GL_ARRAY_BUFFER就可以解除绑定
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); //记住:不要在VAO激活时解除绑定EBO,因为绑定的元素缓冲区对象存储在VAO中;保持EBO绑定。

glBindVertexArray(0);//您可以在之后解除绑定VAO,这样其他VAO调用就不会意外地修改这个VAO

//渲染循环
//程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
{
    //可接收键盘输入esc从而退出
    processInput(window);

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
    glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

    
    glUseProgram(shaderProgram);//使用着色器程序
    glBindVertexArray(VAO);//绑定顶点数组  就是使用顶点属性
    //glDrawArrays(GL_TRIANGLES, 0, 3);
    //配置OpenGL如何绘制图元。第一个参数表示我们打算将其应用到所有的三角形的正面和背面,第二个参数告诉我们用线来绘制
    //注意如果不进行设置glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) 那么就会一直是线框模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//使用当前绑定的索引缓冲对象中的索引进行绘制:

    glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
    glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
}

glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteProgram(shaderProgram);

//释放/删除之前的分配的所有资源
glfwTerminate();

return 0;

}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
//左上角坐标xy和宽高
glViewport(0, 0, width, height);//OpenGL的显示试图
}


## 3.3、两种方法绘制相连的两个三角形


使用六个顶点的方法  
 使用五个顶点,索引缓冲的方法  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2f0c7b8c37e543b58f8cedc41b74900c.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 使用两个顶点数组使用不同的VAO和VBO的方法  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/8f6ab172bf0241008edf57f52aa14cc2.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



#include
#include <glad/glad.h>
#include <GLFW/glfw3.h>

using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

/*
顶点着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
使用in关键字,在顶点着色器中声明所有的输入顶点属性
layout (location = 0)设定了输入变量(顶点属性)的位置值;并且后面链接顶点属性设置的时候会通过顶点属性位置值进行绑定
vec3 表示三个分量的值(注意每个值都是浮点数类型)
第二行就是表示在位置0的地方有一个三个分量的输入变量aPos
将输入的三维aPos转换为四维并赋值给全局变量gl_Position
/
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”;
/*
片段着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
out 表示输出变量 四维向量的变量FragColor
暂时自定义为 一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
RGBA的四分量向量
片段着色器所做的是计算像素最后的颜色输出。为了让事情更简单暂时自定义为 我们的片段着色器将会一直输出橘黄色。
/
const char
fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{\n”
" FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f);\n"
“}\n\0”;

int main()
{
/1、 初始化opengl窗口***************/
//glfw 初始化和配置
glfwInit();//初始化GLFW
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);//主版本号
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);//子版本号
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//指定哪个配置文件配置上下文:GLFW我们使用的是核心模式
#ifdef APPLE//如果时mac os xp等系统则要进行配置一下才能生效
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

//glfw创建窗口
GLFWwindow* 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);//GLFW将我们窗口的上下文设置为当前线程的主上下文
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

//GLAD加载所有的opengl函数指针
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
    std::cout << "Failed to initialize GLAD" << std::endl;
    return -1;
}

/******************************2、 着色器编译编译链接 *********************************************/
//编写 编译顶点着色器
unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//传入GL_VERTEX_SHADER表示创建顶点着色器
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(vertexShader);//编译
int success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::Vertex::Compilation Fail\n" << infoLog << endl;
}

//编写 编译片段着色器
unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//传入GL_FRAGMENT_SHADER 表示创建片段着色器
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//绑定源码 1表示只有一个
glCompileShader(fragmentShader);//编译
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);//检查是否编译报错  也就是检查GLSL的语法
if (!success)
{
    glad_glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
    cout << "ERROR:Shader::fragment::Compilation Fail:" << infoLog << endl;
}

//链接着色器程序
unsigned int shaderProgram = glCreateProgram(); //创建着色器程序
glAttachShader(shaderProgram, vertexShader);//之前编译的着色器附加到程序对象上
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);//链接它们
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);//检查程序是否链接报错
if (!success)
{
    glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
    cout << "ERROR:Program::shaderProgram::Link Fail:" << infoLog << endl;
}
glDeleteShader(vertexShader);//着色器程序已链接完毕则不再需要可以进行delete了
glDeleteShader(fragmentShader);


/******************************3、 顶点属性缓存这些   *********************************************/
//设置顶点数据 和顶点缓存对象 以及配置顶点属性
//标准化设备坐标  顶点  绘制矩形
//float vertices[] = {
//     0.5f,  0.5f, 0.0f,  // top right
//     0.5f, -0.5f, 0.0f,  // bottom right
//    -0.5f, -0.5f, 0.0f,  // bottom left
//    -0.5f,  0.5f, 0.0f   // top left 
//};
索引数组
//unsigned int indices[] = {  // note that we start from 0!
//    0, 1, 3,  // first Triangle
//    1, 2, 3   // second Triangle
//};

//绘制两个相连的三角形
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 VBO[2], VAO[2];
glGenVertexArrays(2, VAO);//创建一个顶点数组 与顶点属性相绑定 
glGenBuffers(2, VBO);//创建顶点缓冲对象

glBindVertexArray(VAO[0]);//绑定顶点数组 配置顶点属性
glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(firstTriangle), firstTriangle, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3*sizeof(float), (void *)0);
glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性

glBindVertexArray(VAO[1]);//绑定顶点数组 配置顶点属性
glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);//将GL_ARRAY_BUFFER类型的缓存与VBO绑定
glBufferData(GL_ARRAY_BUFFER, sizeof(secondTriangle), secondTriangle, GL_STATIC_DRAW);//之前定义的顶点数据复制到缓冲的内存
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);//可以传入0 由opengl自动计算偏移量
glEnableVertexAttribArray(0);//以顶点属性位置值0作为参数,启用顶点属性
//渲染循环
//程序在我们主动关闭它之前不断绘制图像并能够接受用户输入 GLFW退出前一直保持运行
while (!glfwWindowShouldClose(window))//检查一次GLFW是否被要求退出
{
    //可接收键盘输入esc从而退出
    processInput(window);

    glClearColor(0.2f, 0.3f, 0.3f, 1.0f); //设置状态函数
    glClear(GL_COLOR_BUFFER_BIT);//使用状态函数

    
    glUseProgram(shaderProgram);//使用着色器程序
    glBindVertexArray(VAO[0]);//绑定顶点数组  就是使用顶点属性
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glBindVertexArray(VAO[1]);//绑定顶点数组  就是使用顶点属性
    glDrawArrays(GL_TRIANGLES, 0, 3);

    glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。
    glfwPollEvents();//检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数
}

glDeleteVertexArrays(2, VAO);
glDeleteBuffers(2, VBO);
glDeleteProgram(shaderProgram);

//释放/删除之前的分配的所有资源
glfwTerminate();

return 0;

}
void processInput(GLFWwindow* window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)//会返回这个按键是否正在被按下
glfwSetWindowShouldClose(window, true);//把WindowShouldClose属性设置为 true的方法关闭GLFW
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
//左上角坐标xy和宽高
glViewport(0, 0, width, height);//OpenGL的显示试图
}


## 3.4、使用两个着色器输出不同颜色的三角形


![在这里插入图片描述](https://img-blog.csdnimg.cn/981b39ad4caf45b09ee69276803c6dbc.jpg?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



#include
#include <glad/glad.h>
#include <GLFW/glfw3.h>

using namespace std;
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

/*
顶点着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
使用in关键字,在顶点着色器中声明所有的输入顶点属性
layout (location = 0)设定了输入变量(顶点属性)的位置值;并且后面链接顶点属性设置的时候会通过顶点属性位置值进行绑定
vec3 表示三个分量的值(注意每个值都是浮点数类型)
第二行就是表示在位置0的地方有一个三个分量的输入变量aPos
将输入的三维aPos转换为四维并赋值给全局变量gl_Position
/
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”;
/*
片段着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
out 表示输出变量 四维向量的变量FragColor
暂时自定义为 一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
RGBA的四分量向量
片段着色器所做的是计算像素最后的颜色输出。为了让事情更简单暂时自定义为 我们的片段着色器将会一直输出橘黄色。
/
const char
fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{\n”
" FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f);\n"
“}\n\0”;

const char* fragmentShaderSourceTwo = “#version 330 core\n”
“out vec4 FragColor;\n”

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

aPos.x, aPos.y, aPos.z, 1.0);\n"
“}\0”;
/*
片段着色器GLSL源码
GLSL版本号和OpenGL的版本是匹配的,使用openGL3.3版本则使用GLSL的330
out 表示输出变量 四维向量的变量FragColor
暂时自定义为 一个Alpha值为1.0(1.0代表完全不透明)的橘黄色的vec4赋值给颜色输出。
RGBA的四分量向量
片段着色器所做的是计算像素最后的颜色输出。为了让事情更简单暂时自定义为 我们的片段着色器将会一直输出橘黄色。
/
const char
fragmentShaderSource = “#version 330 core\n”
“out vec4 FragColor;\n”
“void main()\n”
“{\n”
" FragColor = vec4(1.0f, 0.2f, 0.2f, 1.0f);\n"
“}\n\0”;

const char* fragmentShaderSourceTwo = “#version 330 core\n”
“out vec4 FragColor;\n”

[外链图片转存中…(img-4VBvgvJ0-1715839352795)]
[外链图片转存中…(img-TPTK3Zvo-1715839352795)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值