OpenGL的图像管线

A.1 OpenGL管线

A.2 C++/OpenGL应用程序

GLFW 文档 - 掘金 (juejin.cn)

在我们尝试编写着色器之前,我们先用编写一个简单的C++/OpenGL应用程序,创建一个GLFWwindow实例并为其设置背景色。

 - 初始化GLFW库
 - 实例化GLFWwindow
 - 初始化GLEW库
 - 调用一次init()函数
 - 重复调用display()函数
 - 注意:在初始化GLEW前必须先初始化GLFW,也就是说必须在调用glewInit()前先调用glfwInit()。

将每个程序的初始化任务都放在init()函数中,用于绘制GLFWwindow的代码都放在display()函数中。

 
#include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include <iostream>
 ​
 void init(GLFWwindow* window){ }
 ​
 void display(GLFWwindow* window, double currentTime)
 {
     //让我们指定颜色缓冲区清除后填充的值,rgba表示,此处为红色
     glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
     //用于清除窗口的颜色缓冲区
     glClear(GL_COLOR_BUFFER_BIT);
 }
 int main()
 {
     if (!glfwInit())
         exit(EXIT_FAILURE);
     //指定计算机必需与OpenGL版本4.3的兼容,主版本号为4,次版本号为3
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
     //glfwCreateWindow,创建宽,高位600像素,顶部标题为Hello,World,
     // 另外两个参数分别用来表示允许全屏显示和资源共享
     GLFWwindow* window = glfwCreateWindow(600, 600, "Hello, World", nullptr, nullptr);
     //关联当前GLFW窗口和OpenGL上下文(我们创建的GLFW窗口不会自动关联OpenGL的上下文)
     glfwMakeContextCurrent(window);
 ​
     if (glewInit() != GLEW_OK)
         exit(EXIT_FAILURE);
     //开启垂直同步
     glfwSwapInterval(1);
 ​
     init(window);
 ​
     while (!glfwWindowShouldClose(window))
     {
         display(window, glfwGetTime());
         glfwSwapBuffers(window);
         //处理窗口相关的事件(如按键事件)
         glfwPollEvents();
     }
 ​
     glfwDestroyWindow(window);
     glfwTerminate();
     exit(EXIT_SUCCESS);
 }

A.3顶点着色器和片段着色器

OpenGL只能绘制几类非常简单的东西,如点、线、三角形。这些简单的东西叫图元。

图元由顶点组成,如三角形由三个顶点。

顶点来源有很多,如从文件读取并由C++/OpenGL应用载入缓冲区,直接在C++中硬编码,或者直接在GLSL代码中生成。

在加载顶点之前,C++/OpenGL应用程序必须编译并链接合适的GLSL顶点着色器和片段着色器程序,之后将他们载入管线。

我们会使用一个OpenGL的函数来构建图形,所有的顶点都会被传入顶点着色器,被逐次处理。

  • glDrawArrays()

语法:

 void WINAPI glDrawArrays(
    GLenum  mode,//要呈现的基元类型
    GLint   first,//已启用的数组中的起始索引。
    GLsizei count//要呈现的所引数
 );

使用C++硬编码的方式,画一个点

 //在C++代码中写GLSL代码--以字符串的形式
 //需要写一个顶点着色器(指定绘制的形状,一般以数组的形式传递)
 //还需一个片段着色器(主要指颜色、光照、阴影等光影效果)
 //可以将其封装到函数CreateShaderProgram
 unsigned int CreateShaderProgram()
 {
     const char* vShaderSource = 
         "#version 430 \n"
         "void main(void) \n"
         "{gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}";
     //gl_Position内置的坐标变量,前三个参数对应坐标的(x,y,z),第四个参数为矩阵变换时会用到的值,变换矩阵是4x4,所有我们     //还需要添加一位才能进行矩阵的乘法
     const char* fShaderSource =
         "#version 430 \n"
         "out vec4 color; \n"
         "void main(void) \n"
         "{ color = vec4(0.0, 0.0, 1.0, 1.0); }";
     
     //创建顶点着色器和片段着色器
     unsigned int vShader = glCreateShader(GL_VERTEX_SHADER);
     unsigned int fShader = glCreateShader(GL_FRAGMENT_SHADER);
     //替换vShader,fShader中的源代码,使用自定义的编码
     glShaderSource(vShader, 1, &vShaderSource, NULL); 
     glShaderSource(fShader, 1, &fShaderSource, NULL);
     //对着色器对象进行编译
     glCompileShader(vShader);
     glCompileShader(fShader);
     //创建一个Program链接着色器对象,执行的就是链接有着色器对象的程序
     unsigned int vfProgram = glCreateProgram();
     //把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们
     glAttachShader(vfProgram, vShader);
     glAttachShader(vfProgram, fShader);
     glLinkProgram(vfProgram);
     //返回连接好的着色器程序
     return vfProgram;
 }
 //init函数
 #define numVAOs 1
 unsigned int renderingProgram;
 unsigned int vao[numVAOs];//顶点数组,必须要有
 void init(GLFWwindow* window)
 { 
     renderingProgram = CreateShaderProgram();
     glGenVertexArrays(numVAOs, vao);//生成顶点数组对象名称,用numVAOs存储
     glBindVertexArray(vao[0]);//绑定vao[0]
 }
 //display函数
 void display(GLFWwindow* window, double currentTime)
 {
     glUseProgram(renderingProgram);//激活程序对象
     glPointSize(30.0f);//设置点的大小
     glDrawArrays(GL_POINTS, 0, 1);//绘制方式,绘制一个点
 }

完整.cpp代码

 
#include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include <iostream>
 #define numVAOs 1
 unsigned int renderingProgram;
 unsigned int vao[numVAOs];//顶点数组,必须要有
 unsigned int CreateShaderProgram();
 ​
 void init(GLFWwindow* window)
 {
     renderingProgram = CreateShaderProgram();
     glGenVertexArrays(numVAOs, vao);//生成顶点数组对象名称,用numVAOs存储
     glBindVertexArray(vao[0]);//绑定vao[0]
 }
 ​
 void display(GLFWwindow* window, double currentTime)
 {
     glUseProgram(renderingProgram);//激活程序对象
     glPointSize(30.0f);//设置点的大小
     glDrawArrays(GL_POINTS, 0, 1);//绘制方式,绘制一个点
 }
 unsigned int CreateShaderProgram()
 {
     const char* vShaderSource =
         "#version 430 \n"
         "void main(void) \n"
         "{gl_Position = vec4(0.0, 0.0, 0.0, 1.0);}";
     //gl_Position内置的坐标变量,前三个参数对应坐标的(x,y,z),第四个参数为矩阵变换时会用到的值,变换矩阵是4x4,所有我们     //还需要添加一位才能进行矩阵的乘法
     const char* fShaderSource =
         "#version 430 \n"
         "out vec4 color; \n"
         "void main(void) \n"
         "{ color = vec4(0.0, 0.0, 1.0, 1.0); }";
 ​
     //创建顶点着色器和片段着色器
     unsigned int vShader = glCreateShader(GL_VERTEX_SHADER);
     unsigned int fShader = glCreateShader(GL_FRAGMENT_SHADER);
     //替换vShader,fShader中的源代码,使用自定义的编码
     glShaderSource(vShader, 1, &vShaderSource, NULL);
     glShaderSource(fShader, 1, &fShaderSource, NULL);
     //对着色器对象进行编译
     glCompileShader(vShader);
     glCompileShader(fShader);
     //创建一个Program链接着色器对象,执行的就是链接有着色器对象的程序
     unsigned int vfProgram = glCreateProgram();
     //把之前编译的着色器附加到程序对象上,然后用glLinkProgram链接它们
     glAttachShader(vfProgram, vShader);
     glAttachShader(vfProgram, fShader);
     glLinkProgram(vfProgram);
     //返回连接好的着色器程序
     return vfProgram;
 }
 int main()
 {
     if (!glfwInit())
         exit(EXIT_FAILURE);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
     glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
     GLFWwindow* window = glfwCreateWindow(600, 600, "Hello, world", nullptr, nullptr);
     glfwMakeContextCurrent(window);
 ​
     if (glewInit() != GLEW_OK)
         exit(EXIT_FAILURE);
     glfwSwapInterval(1);
 ​
     init(window);
 ​
     while (!glfwWindowShouldClose(window))
     {
         display(window, glfwGetTime());
         glfwSwapBuffers(window);
         glfwPollEvents();
     }
 ​
     glfwDestroyWindow(window);
     glfwTerminate();
     exit(EXIT_SUCCESS);
 }

相关函数

参考:1.GLES2.0中文API-glShaderSource-CSDN博客等相关博客
1.glCreateShader - 创建一个着色器对象
C规范

GLuint glCreateShader(GLenum shaderType);

参数

shaderType

指定要创建的着色器的类型。 只能是GL_VERTEX_SHADERGL_FRAGMENT_SHADER

描述

glCreateShader创建一个空的着色器对象,并返回一个可以引用的非零值(shader ID)。着色器对象用于维护定义着色器的源代码字符串。shaderType指示要创建的着色器的类型。 支持两种类型的着色器。 GL_VERTEX_SHADER类型的着色器是一个用于在可编程顶点处理器上运行的着色器。 GL_FRAGMENT_SHADER类型的着色器是一个着色器,旨在在可编程片段处理器上运行。

创建时,着色器对象的GL_SHADER_TYPE参数设置为GL_VERTEX_SHADERGL_FRAGMENT_SHADER,具体取决于shaderType的值。

注意

与纹理对象一样,着色器对象的名称空间可以在一组上下文中共享,只要上下文的服务器端共享相同的地址空间即可。 如果名称空间跨上下文共享,则也会共享任何附加对象和与这些附加对象关联的数据。

当从不同的执行线程访问对象时,应用程序需要负责跨API调用提供同步。

错误

如果创建着色器对象时发生错误,则此函数返回0。

GL_INVALID_ENUMshaderType不是一个可接受的值。

2.glGetShaderSource - 从着色器对象返回源代码字符串
C规范

void glGetShaderSource( GLuint shader, GLsizei bufSize, GLsizei length*, GLchar source*);

参数

shader

指定要查询的着色器对象。

bufSize

指定用于存储返回的源代码字符串的字符缓冲区的大小。

length

返回source中返回的字符串的长度(不包括null终止符)。

source

指定用于返回源代码字符串的字符数组。

描述

glGetShaderSource返回由着色器指定的着色器对象的源代码字符串的串联。着色器对象的源代码字符串是先前调用glShaderSource的结果。函数返回的字符串将以null结尾。

glGetShaderSource尽可能多地在源代码中返回源代码字符串,最多可返回bufSize字符。实际返回的字符数(不包括空终止字符)由length指定。如果不需要返回字符串的长度,则可以在length参数中传递NULL值。可以通过调用值为GL_SHADER_SOURCE_LENGTH的glGetShaderiv来获取存储返回的源代码字符串所需的缓冲区大小。

错误

GL_INVALID_VALUEshader不是OpenGL生成的值。

GL_INVALID_OPERATIONshader不是着色器对象。

GL_INVALID_VALUEbufSize小于0。

3.glShaderSource-替换着色器对象中的源代码
C规范

void glShaderSource(GLuint shader,GLsizei count,const GLchar * const string*,const GLint length*);

参数

shader

要被替换源代码的着色器对象的句柄(ID)。

count

指定字符串和长度数组中的元素数。

string

指定指向包含要加载到着色器的源代码的字符串的指针数组。

length

指定字符串长度的数组。

描述

对于支持着色器编译器的实现,glShaderSource将着色器中的源代码设置为string指定的字符串数组中的源代码。先前存储在着色器对象中的任何源代码都将被完全替换。数组中的字符串数由count指定。 如果lengthNULL,则认为每个字符串都以null结尾。如果length不是NULL,则它指向包含字符串的每个相应元素的字符串长度的数组。length数组中的每个元素可以包含相应字符串的长度(空字符不计为字符串长度的一部分)或小于0的值以表示该字符串为空终止。此时不扫描或解析源代码字符串; 它们只是复制到指定的着色器对象中。

注意

着色器编译器支持是可选的,因此必须在使用之前通过使用参数GL_SHADER_COMPILER调用glGet来查询。glShaderSource,glCompileShaderglGetShaderPrecisionFormatglReleaseShaderCompiler等在不支持着色器编译器的实现上都将生成GL_INVALID_OPERATION。这样的实现提供了glShaderBinary替代方案,用于提供预编译的着色器二进制文件。

调用glShaderSource时,OpenGL会复制着色器源代码字符串,因此应用程序可以在函数返回后立即释放源代码字符串的副本。

错误

GL_INVALID_OPERATION:不支持着色器编译器

GL_INVALID_VALUEshader不是OpenGL生成的值

GL_INVALID_OPERATIONshader不是着色器对象

GL_INVALID_VALUEcount比0小

4.glCompileShader - 编译一个着色器对象
C规范

void glCompileShader(GLuint shader);

参数

shader

指定要编译的着色器对象。

描述

对于支持着色器编译器的实现,glCompileShader编译已存储在shader指定的着色器对象中的源代码字符串。

编译状态将存储为着色器对象的状态的一部分。 如果着色器编译时没有错误并且可以使用,则此值将设置为GL_TRUE,否则将设置为GL_FALSE。 可以通过使用参数shaderGL_COMPILE_STATUS调用glGetShaderiv来查询状态值。

由于OpenGL ES着色语言规范指定的多种原因,着色器的编译可能会失败。 无论编译是否成功,都可以通过调用glGetShaderInfoLog从着色器对象的信息日志中获取有关编译的信息。

注意

着色器编译器支持是可选的,因此必须在使用之前通过使用参数GL_SHADER_COMPILER调用glGet来查询。glShaderSource,glCompileShaderglGetShaderPrecisionFormatglReleaseShaderCompiler等在不支持着色器编译器的实现上都将生成GL_INVALID_OPERATION。这样的实现提供了glShaderBinary替代方案,用于提供预编译的着色器二进制文件。

错误

GL_INVALID_OPERATION:不支持着色器编译器

GL_INVALID_VALUEshader不是OpenGL生成的值

GL_INVALID_OPERATIONshader不是着色器对象

5.glCreateProgram- 创建一个program(建议不要翻译成“程序”,以免引起与APP的混淆)对象
C规范

GLuint glCreateProgram(void);

描述

glCreateProgram创建一个空program并返回一个可以被引用的非零值(program ID)。 program对象是可以附加着色器对象的对象。 这提供了一种机制来指定将链接已创建program的着色器对象。 它还提供了一种检查将用于创建program的着色器的兼容性的方法(例如,检查顶点着色器和片元着色器之间的兼容性)。 当不再需要作为program对象的一部分时,着色器对象就可以被分离了。

通过调用glCompileShader成功编译着色器对象,并且通过调用glAttachShader成功地将着色器对象附加到program 对象,并且通过调用glLinkProgram成功的链接program 对象之后,可以在program 对象中创建一个或多个可执行文件。

当调用glUseProgram时,这些可执行文件成为当前状态的一部分。 可以通过调用glDeleteProgram删除程序对象。 当program 对象不再是任何上下文的当前呈现状态的一部分时,将删除与program 对象关联的内存。

注意

与纹理对象一样,只要上下文的服务器端共享相同的地址空间,程序对象的名称空间就可以在一组上下文中共享。 如果名称空间跨上下文共享,则也会共享任何附加对象和与这些附加对象关联的数据。

当从不同的执行线程访问对象时,应用程序负责跨API调用提供同步。

错误

如果创建program 对象时发生错误,则此函数返回0。

6.glGenVertexArrays —生成顶点数组对象名称
C规范

void glGenVertexArrays(GLsizei n, GLuint *arrays);

参数

n 指定要生成的顶点数组对象名称的数量。

arrays 指定一个数组,在其中存储生成的顶点数组对象名称。

描述

glGenVertexArrays返回数组中的n个顶点数组对象名称。 不能保证名称形成连续的整数集。 但是,可以保证在调用glGenVertexArrays之前,不会立即使用任何返回的名称。

调用glGenVertexArrays返回的顶点数组对象名称不会被后续调用返回,除非首先使用glDeleteVertexArrays删除它们。

仅出于glGenVertexArrays的目的,将数组中返回的名称标记为已使用,但仅在首次绑定时才获取状态和类型。

错误

如果n为负,则生成GL_INVALID_VALUE。

7.glBindVertexArray — 绑定一个顶点数组对象
C规范

void glBindVertexArray( GLuint array);

参数

array 指定要绑定的顶点数组的名称。

描述

glBindVertexArray将顶点数组对象与名称数组绑定。 array是先前从glGenVertexArrays调用返回的顶点数组对象的名称,或者为0以绑定默认的顶点数组对象绑定。

如果不存在名称为array的顶点数组对象,则在第一次绑定array时创建一个对象。 如果绑定成功,则不会更改顶点数组对象的状态,并且任何先前的顶点数组对象绑定都会中断。

错误

如果array不为零或先前从调用glGenVertexArrays返回的顶点数组对象的名称,则生成GL_INVALID_OPERATION。

运行结果:

image-20231124144640906

在这GLuint是OpenGL中的数据类型,表示的就是无符号整数,所以我用unsigned int。

  1. #define numVAOs 1:这行代码定义了一个宏numVAOs,并将其值设置为1。这是为了在后面的代码中使用这个宏。

  2. unsigned int renderingProgram;:声明一个无符号整数类型的变量renderingProgram,用于存储渲染程序的ID。

  3. unsigned int vao[numVAOs];:声明一个无符号整数类型的数组vao,大小为numVAOs,用于存储顶点着色器程序的ID。

  4. unsigned int createShaderProgram():该函数用于创建一个渲染程序。

  5. const char* vshaderSource:用于存储顶点着色器的源代码。

  6. const char* fshaderSource:用于存储片段着色器的源代码。

  7. unsigned int vShader = glCreateShader(GL_VERTEX_SHADER);:调用OpenGL函数glCreateShader创建一个顶点着色器,并将其赋值给变量vShader

  8. unsigned int fShader = glCreateShader(GL_FRAGMENT_SHADER);:调用OpenGL函数glCreateShader创建一个片段着色器,并将其赋值给变量fShader

  9. glShaderSource(vShader, 1, &vShaderSource, NULL);:调用OpenGL函数glShaderSource将顶点着色器的源代码传递给顶点着色器。

  10. glShaderSource(fShader, 1, &fShaderSource, NULL);:调用OpenGL函数glShaderSource将片段着色器的源代码传递给片段着色器。

  11. glCompileShader(vShader);:调用OpenGL函数glCompileShader编译顶点着色器。

  12. glCompileShader(fShader);:调用OpenGL函数glCompileShader编译片段着色器。

  13. unsigned int vfProgram = glCreateProgram();:调用OpenGL函数glCreateProgram创建一个渲染程序,并将其赋值给变量vfProgram

  14. glAttachShader(vfProgram, vShader);:调用OpenGL函数glAttachShader将顶点着色器添加到渲染程序中。

  15. glAttachShader(vfProgram, fShader);:调用OpenGL函数glAttachShader将片段着色器添加到渲染程序中。

  16. glLinkProgram(vfProgram);:调用OpenGL函数glLinkProgram链接渲染程序。

  17. glGenVertexArrays(numVAOs, vao);:调用OpenGL函数glGenVertexArrays创建一个顶点着色器数组,并将其赋值给变量vao[0]

  18. glBindVertexArray(vao[0]);:调用OpenGL函数glBindVertexArray将顶点着色器数组绑定到当前的渲染程序中。

在片段着色器中同样有一个变量能让我们访问输入片段的坐标,叫作gl_FragCoord。让我们来使用它基于位置来设置每个像素的值。

 const char* fShaderSource =
         "#version 430 \n"
         "out vec4 color; \n"
         "void main(void) \n"
         "{ if(gl_FragCoord.x < 255) color = vec4(1.0, 0.0, 0.0, 1.0); else color = vec4(0.0, 0.0, 1.0, 1.0);}";     

同时我们需要将该点设置得更大一点。

 glPointSize(430.0f);

运行结果:

image-20231124145929558

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值