运行环境:Linux 、C++

  • 本教程仅个人学习总结
    • 第一个hello world,渲染一个窗体
    • 渲染一个矩形

本教程仅个人学习总结

一切参考资源:都在官网。

1、安装glfw

首先下载glfw : 点击这里

1、下载
2、解压
3、mkdir build && cd build
4、cmake … && make -j4
5、cd …
6、将此目录下的include目录里面的东西全拷到自己工程的include中
7、cd build/src 这个目录下面的静态库(默认编译是静态库) libglfw3.a ,为我们需要的库

1、 GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。它允许用户创建OpenGL上下文、定义窗口参数以及处理用户输入,对我们来说这就够了。

因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用

2、安装GLAD
GLAD是一个开源的库,它能解决我们上面提到的那个繁琐的问题。GLAD的配置与大多数的开源库有些许的不同,GLAD使用了一个在线服务。在这里我们能够告诉GLAD需要定义的OpenGL版本,并且根据这个版本加载所有相关的OpenGL函数。
打开GLAD的在线服务,将语言(Language)设置为C/C++,在API选项中,选择3.3以上的OpenGL(gl)版本(我们的教程中将使用3.3版本,但更新的版本也能用)。之后将模式(Profile)设置为Core,并且保证选中了生成加载器(Generate a loader)选项。现在可以先(暂时)忽略扩展(Extensions)中的内容。都选择完之后,点击生成(Generate)按钮来生成库文件。

GLAD现在应该提供给你了一个zip压缩文件,包含两个头文件目录,和一个glad.c文件。将两个头文件目录(glad和KHR)复制到你的Include文件夹中(或者增加一个额外的项目指向这些目录),并添加glad.c文件到你的工程中。

3、OpenGL安装。本站一大堆教程这里就不多说了。

第一个hello world,渲染一个窗体

上文中说到GLFW提供了一系列操作OPENGL的接口,首先第一步即为初始化GLFW环境

#include "glfw3.h"

初始化前我们需要先知道GLFW的版本。

    int major = 0, minor = 0, rev = 0;glfwGetVersion(&major, &minor, &rev); //查看GLFW版本号std::cout << "GLFW version: " << major << "." << minor << "." << rev << std::endl;

然后我们在初始化GLFW环境

    if (glfwInit() == GLFW_FALSE){std::cout << "Failed to initialize GLFW" << std::endl;}std::cout << "Success to initialize GLFW" << std::endl;

glfwInit这个函数有如下需要注意的点:

PS1:如果此函数失败,它会在返回前调用glfwTerminate。如果成功,您应该在应用程序退出之前调用glfwTerminate。
PS2:此函数只能从主线程调用。

我们还需要告诉GLFW我们使用的openGL版本和模式

    //教程都是基于OpenGL 3.3版本展开讨论的,所以我们需要告诉GLFW我们要使用的OpenGL版本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);                 //主板本号glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);                 //次glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //核心模式

具体设置GLFW的约束条件,我也没整明白~,目前就这么用。1、版本号,2、使用的openGL模式。

接下来就是创建窗口

    GLFWwindow *window = glfwCreateWindow(800, 600, "HelloWorld", NULL, NULL);//倒数第二个参数是选择的监视器模式//最后一个参数,基本用不到。if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}

注意创建失败的话,需要清除所有窗体。glfwTerminate此函数会销毁所有剩余的窗口和光标

接下来我们要设置窗体的归属,简单来说就是设置窗体为那个线程锁持有。

glfwMakeContextCurrent(window);

一个窗体,大小肯定是可变的。我们可以通过鼠标去拉伸和缩放,这样窗体的大小就是可变的。因此我们要设置一个回调函数
实时设置我们手动改变窗体的大小。

void set_render_size(GLFWwindow *window, int width, int height);glfwSetFramebufferSizeCallback(window, set_render_size); //注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数void set_render_size(GLFWwindow *window, int width, int height)
{std::cout<< width<<" "<< height<<std::endl;glViewport(0, 0, width, height);
}

下面就是要我们使用openGL来渲染这个窗体,这就要用到GLAD

再次说明一下GLAD的作用便于理解。

到这里还没有结束,我们仍然还有一件事要做。因为OpenGL只是一个标准/规范,具体的实现是由驱动开发商针对特定显卡实现的。由于OpenGL驱动版本众多,它大多数函数的位置都无法在编译时确定下来,需要在运行时查询。所以任务就落在了开发者身上,开发者需要在运行时获取函数地址并将其保存在一个函数指针中供以后使用。

初始化GLAD库:常用接口如下:
int gladLoadGLLoader(GLADloadproc load):任何的OpenGL接口调用都必须在初始化GLAD库后才可以正常访问。如果成功的话,该接口将返回GL_TRUE,否则就会返回GL_FALSE。
其中GLADloadproc函数声明如下:
void* (*GLADloadproc)(const char* name)GLFWglproc glfwGetProcAddress    (   const char *    procname    )
如果当前上下文支持,此函数返回指定 OpenGL 或 OpenGL ES核心或扩展函数的地址。
上下文必须在调用线程上是当前的。在没有当前上下文的情况下调用此函数将导致GLFW_NO_CURRENT_CONTEXT错误。
    //初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}const GLubyte *OpenGLVersion = glGetString(GL_VERSION); //返回当前OpenGL实现的版本号printf("OpenGL实现的版本号:%s\n", OpenGLVersion);
我们不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。
我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。
因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),
它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环。
    while (!glfwWindowShouldClose(window)) // glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出,{                                      //如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glfwSwapBuffers(window); //函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上glfwPollEvents();        //函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。}

最后销毁所有窗体

    //销毁所有窗口glfwTerminate();

这样我们渲染一个窗体的大体流程就是这些

完整源码如下

#include <iostream>
#include <cstdio>
#include <sys/unistd.h>#include "glad.h"
#include "glfw3.h"void set_render_size(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);
int main()
{int major = 0, minor = 0, rev = 0;glfwGetVersion(&major, &minor, &rev); //查看GLFW版本号std::cout << "GLFW version: " << major << "." << minor << "." << rev << std::endl;if (glfwInit() == GLFW_FALSE){std::cout << "Failed to initialize GLFW" << std::endl;}std::cout << "Success to initialize GLFW" << std::endl;//教程都是基于OpenGL 3.3版本展开讨论的,所以我们需要告诉GLFW我们要使用的OpenGL版本是3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);                 //主板本号glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);                 //次glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); //核心模式GLFWwindow *window = glfwCreateWindow(800, 600, "HelloWorld", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);                          //使调用线程的指定窗口的上下文成为当前的glfwSetFramebufferSizeCallback(window, set_render_size); //注册这个函数,告诉GLFW我们希望每当窗口调整大小的时候调用这个函数://初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}const GLubyte *OpenGLVersion = glGetString(GL_VERSION); //返回当前OpenGL实现的版本号printf("OpenGL实现的版本号:%s\n", OpenGLVersion);/*不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:*/while (!glfwWindowShouldClose(window)) // glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出,{                                      //如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glfwSwapBuffers(window); //函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上glfwPollEvents();        //函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。}//销毁所有窗口glfwTerminate();return 0;
}void set_render_size(GLFWwindow *window, int width, int height)
{std::cout<< width<<" "<< height<<std::endl;glViewport(0, 0, width, height);
}void processInput(GLFWwindow *window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}

运行结果如下

渲染一个矩形

接下来我们尝试在渲染窗体中加上图形的渲染,理解起来可能有点抽象。首先假设渲染图形肯定不可能只是平面图像吧,只是在屏幕上显示的是2D的图像。比如说一个苹果,在现实世界中肯定是3D的存在,但是我们屏幕
只是一个2D的平面,我们如何将一个现实世界3D的物体,转换成屏幕上2D显示呢(2D显示3D的效果)?这就涉及到坐标的转换了。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管
道,期间经过各种变化处理最终出现在屏幕的过程)管理的。图形渲染管线可以被划分为两个主要部分:
1、第一部分把你的3D坐标转换为2D坐标
2、2D坐标转变为实际的有颜色的像素。

图形渲染管线接受一组3D坐标,然后把它们转变为你屏幕上的有色2D像素输出。图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做着色器(Shader)。
.
有些着色器可以由开发者配置,因为允许用自己写的着色器来代替默认的,所以能够更细致地控制图形渲染管线中的特定部分了。因为它们运行在GPU上,所以节省了宝贵的CPU时间。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading > Language, GLSL)写成的,在下一节中我们再花更多时间研究它。
.
下面,你会看到一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。

要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。

如上图即是绘制渲染一个图片的流程。按照官网的教程说的话,,,,,,,有点过于抽象繁琐,简单来说就是如下几个过程。

1、你想渲染一个苹果,首先你得知道这个苹果在坐标系中的位置坐标吧。

那么顶点着色器它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。

再粗暴点理解就一句话顶点着色器 : 坐标转换用的

需要注意

2、图元装配阶段将就是将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状

简单点来说将顶点着色器输出的点,绘制出对应的形状

3、几何着色器这玩意是干啥的捏???上个图

几何着色器其实就是决定线怎么连,最终绘制成什么样子的图形!这样说还无法理解,那对着上图悟吧!!!!!!1

4、光栅化阶段,这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。

简单点理解就是,将几何着色器生成的形状,这块形状包含的像素块圈起来。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。

5、片段着色器的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。

这部分作用一句话概括就是:确认像素的颜色。

6、Alpha测试和混合(Blending)阶段 这最后一步是干啥的呢?????就是。。。。。。上色!

接下来我们在窗体中渲染一个矩形
首先GLFW、GLAD的初始化是不可少的,除去循环渲染的部分,其他部分和上一小节完全是一样的,我们直接copy即可。

#include <glad.h>
#include <glfw3.h>#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;int main()
{// glfw: initialize and configure// ------------------------------glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#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 -1;}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 -1;}...//todo...glfwTerminate();return 0;
}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)
{glViewport(0, 0, width, height);
}

接下来我们要开始渲染一个矩形了

首先我们现创建顶点着色器。

    // 创建顶点着色器 //glCreateShader 创建着色器 参数是着色器类型:GL_VERTEX_SHADER是顶点着色器unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);

注意一下上文有这样一句话

有些着色器可以由开发者配置,因为允许用自己写的着色器来代替默认的,所以能够更细致地控制图形渲染管线中的特定部分了。因为它们运行在GPU上,所以节省了宝贵的CPU时间。OpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的

我们创建了顶点着色器,接下来我们就要编写着色器的程序。

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";// 创建顶点着色器unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//替换着色器中的代码:顶点glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//编译已存储在由 指定的着色器对象中的源代码字符串glCompileShader(vertexShader);

接着我们在创建一个片段着色器


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";// 创建片段着色器unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//替换着色器中的代码 : 片段glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//编译已存储在由 指定的着色器对象中的源代码字符串glCompileShader(fragmentShader);// check for shader compile errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){//返回着色器对象的信息日志glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::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);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 vertices[] = {0.5f,  0.5f, 0.0f,  // top right0.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 Triangle1, 2, 3   // second Triangle};unsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbindglBindBuffer(GL_ARRAY_BUFFER, 0); // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.glBindVertexArray(0);

全部完整源码

#include <glad.h>
#include <glfw3.h>#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;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, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#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 -1;}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 -1;}// ------------------------------------// 创建顶点着色器unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);//替换着色器中的代码:顶点glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);//编译已存储在由 指定的着色器对象中的源代码字符串glCompileShader(vertexShader);// check for shader compile errorsint success;char infoLog[512];//获取从着色器对象返回参数glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){//返回着色器对象的信息日志glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}// 创建片段着色器unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);//替换着色器中的代码 : 片段glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);//编译已存储在由 指定的着色器对象中的源代码字符串glCompileShader(fragmentShader);// check for shader compile errorsglGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){//返回着色器对象的信息日志glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::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);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 vertices[] = {0.5f,  0.5f, 0.0f,  // top right0.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 Triangle1, 2, 3   // second Triangle};unsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbindglBindBuffer(GL_ARRAY_BUFFER, 0); // remember: do NOT unbind the EBO while a VAO is active as the bound element buffer object IS stored in the VAO; keep the EBO bound.//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);// You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other// VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.glBindVertexArray(0); // 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);// draw our first triangleglUseProgram(shaderProgram);glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized//glDrawArrays(GL_TRIANGLES, 0, 6);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);// glBindVertexArray(0); // no need to unbind it every time // 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(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glDeleteProgram(shaderProgram);// glfw: terminate, clearing all previously allocated GLFW resources.// ------------------------------------------------------------------glfwTerminate();return 0;
}// 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(0, 0, width, height);
}

运行截图

感谢阅读。
企鹅:918619587

OpenGL教程一,窗体和图形的渲染(包含OpenGL、GLFW、GLAD)相关推荐

  1. OpenGL 开发环境配置(Windows) - Visual Studio 2017 + GLFW + GLAD 详细图文教程

    OpenGL 开发环境配置(Windows) - Visual Studio 2017 + GLFW + GLAD 详细图文教程 大部分 OpenGL 是直接面向设备商的,如果开发者需要使用 Open ...

  2. WPF教程一:基础(转)

    WPF教程一:基础 一.WPF简介 WPF:WPF即Windows Presentation Foundation,翻译为中文"Windows呈现基础",是微软推出的基于Windo ...

  3. 小强学渲染之OpenGL渲染管线详析

    什么是OpenGL? OpenGL是一套图形硬件的软件API接口库,它直接和GPU交互,将3D场景渲染绘制到2D屏幕上.总结说,OpenGL的功能是将程序中定义的各种2D或3D模型绘制到帧缓存中,或者 ...

  4. 借助 OpenGL* ES 2.0 实现动态分辨率渲染

    作者:omar-a-rodrigue 下载 借助 OpenGL* ES 2.0 实现动态分辨率渲染[PDF 677KB] 代码样本: dynamic-resolution.zip[ZIP 4MB] 像 ...

  5. OpenGL 实验一 绘制简单图形

    OpenGL实验一 OpenGL简介: (英语:Open Graphics Library,译名:开放图形库或者"开放式图形库")是用于渲染2D.3D矢量图形的跨语言.跨平台的应用 ...

  6. GPU图形图像渲染原理

    目录 引言 GPU 历史 GPU 图形渲染流水线 GPU 存储系统 GPU 流处理器 CPU-GPU 异构系统 GPU 资源管理模型 CPU-GPU 工作流 屏幕图像显示原理 引言 原文链接 http ...

  7. 计算机那些事(8)——图形图像渲染原理

    最近在 iOS 开发中做了较多动画相关的编程工作.因此想借此机会深入了解了一下 iOS 动画及渲染相关原理.随着对相关方面的深入了解,发现这里面涉及到从硬件底层到软件框架等一系列相关知识. 本文将从相 ...

  8. 计算机那些事——图形图像渲染原理

    最近在 iOS 开发中做了较多动画相关的编程工作.因此想借此机会深入了解了一下 iOS 动画及渲染相关原理.随着对相关方面的深入了解,发现这里面涉及到从硬件底层到软件框架等一系列相关知识. 本文将从相 ...

  9. matlab图片渲染,MATLAB图渲染:OpenGL与Painters?

    当涉及哪个渲染器用于MATLAB数字或什么时候重要时,我无能为力,但我遇到了一些重要的例子: plot(0,0,'ko','markersize',50,'linewidth',8); set(gcf ...

最新文章

  1. 高等数学同济第七版课后答案下册
  2. 计算机组成原理课后习题答案一
  3. jsp简单练习-简单的下拉表单
  4. WCF服务开发与调用的完整示例
  5. 不同层次程序员的比较:三流比设计,一流比方法,顶级比什么?
  6. jquery easyui validatebox remote使用
  7. WebGIS中利用AGS JS+eCharts实现一些数据展示的探索
  8. 推荐一款好用的java反编译软件——JavaDecompiler
  9. 狼人杀休闲游戏微信小程序模板源码/微信小游戏源码
  10. 未雨绸缪,数据保护之NBU介质备份
  11. Android Killer反编译apk报错
  12. u 只读 盘 突然_u盘变成只读方式了,怎么办
  13. 免费的API-手机号码归属地接口
  14. 知己知彼,案例对比 Requests、Selenium、Scrapy 爬虫库!
  15. CS5216 DP to hdmi 1080p转换器或者转接线设计原理
  16. STM32 Cube ADC 测量土壤湿度
  17. 香蕉派,修改 uboot 和 kernel 串口波特率
  18. 关于js延迟加载的几种方法
  19. SQL Server 数据库备份还原和数据恢复
  20. 哪些些实验适合用计算机实测,计算机实测物理实验2014年讨论内容

热门文章

  1. Java 基础(继承)
  2. POI HSSFWorkbook导出Excel到本地电脑
  3. 来自2020的二十一次总结
  4. 2017版MySQL DBA核心课程-第1-16部完整-老男孩-专题视频课程
  5. php中文意思,php中::是什么意思?
  6. cad2016中选择全图字体怎么操作_大线索报道:抖音上的书单都是用啥做的,抖音书单图片怎么做...
  7. ab压测安装,使用方法
  8. 未来已来:足以改变世界的20大发明
  9. el-select下拉加载(实现懒加载)自定义loadmore事件
  10. 使用KNN算法对水果进行分类