Preface

这一节所有的主要内容都在一个OpenGL库文件中<LoadShaders.h> ,只需要用LoadShader()函数进行加载即可。但是由于老是出错,所以自己实现了一下,也用以提供给有兴趣了解着色器的编译、连接等原理的人。

因为程序基本都是自己实现的,所以,只需要包含最基本的OpenGL头文件即可运行。

效果如下:

Background

由于固定管线编程过程中涉及的大量计算(当然,上个例子并没有多少计算量)都是再CPU上进行的,而这些可能影响CPU处理其他事情的效率,所以不妨运用一种编程技法,将这些繁重的运算交予GPU去处理, 进而腾出CPU的时间。于是就引进了我们今天的前提——可编程管线,其对应的语言为GLSL(OpenGL Shading Language)。

先说一下博文撰写原则,博文撰写涉及到的背景知识只阐述与本次主题相关的,至于整合的那种完整的理论原理综述,如果需要的话会在后面做统一的阐述,而单篇有主题的文章中不做总结,不然可能会喧宾夺主。

看完这一篇可能会有几个疑问:着色器到底干了啥,它是怎么导致图形效果的,以及它里面每一个关键字都是干什么的,等等

上述问题,可能考虑下一篇讲,有人会问为什么不先弄清楚上面的再写下面的,因为我就是那样过来的,只有空洞的理论和语法,这种还是先看到效果,然后追究其原理更合适一点,这是我的一个安排和考虑,理解万岁。

Getting ready

觉得还是需要简单说明一下可编程渲染管线的流程(超级精简版):

对于顶点属性数据,如:位置、颜色、纹理等等,他们都需要进行某些操作,比如,位置可能需要平移,颜色可能需要渐变等,而这些一定都是矩阵运算,而不同的处理就对应着不同的着色器,嗯,就是酱紫。

而我们今天只需要用到顶点着色器和片元着色器。

以顶点着色器为例来介绍我们这次需要掌握的GLSL语法

vertex Shader (顶点着色器)

将下面的内容写到一个名为basic.vert的文件中 (emmm,最后一行可能要有一个空行,这个不是语法要求,这个是该程序所需,后面会讲到)

  1. #version
  2.  
  3. layout (location=) in vec3 VertexPosition;
  4. layout (location=) in vec3 VertexColor;
  5.  
  6. out vec3 Color;
  7.  
  8. void main()
  9. {
  10. Color = VertexColor;
  11.  
  12. gl_Position = vec4(VertexPosition,1.0);
  13. }

顶点着色器:对于绘制命令传输的每一个顶点,OpenGL都会调用一个顶点着色器来处理顶点相关的属性数据。

顶点着色器的复杂与否与其他着色器的活跃与否有关,所以,它可能会非常简单,如:值将数据复制并传递到下一个阶段,这类似于pass-through shader,就是,走一下形式,数据简单流过,无任何操作。它也可能非常复杂,如:执行大量的计算来得到顶点在屏幕上的位置,或者通过光照的计算来判断顶点的颜色。

注:一个复杂应用程序可能有多个顶点着色器,但统一时刻只能有一个顶点着色器起作用

in 从应用程序(我们习惯将主程序称为应用程序)中传递进来的数据

out 从该着色器传递出去的新数据(相当于C++程序中的return 变量)

第一句#version 430指的是opengl或GLSL的版本为4.3(后面用程序获取本机的版本号)

因为可能会有很多个in 对象,而我们知道应用程序中需要获取它的位置,所以就要有Location,即下标、索引 号,而layout就是一个GLSL语言的关键字用于人工确定索引的排布方式。上一节中,提到过Location:(https://www.cnblogs.com/lv-anchoret/p/9221504.html)

vec3就是一个一维的有三个元素的数值类型(三元组)

gl_Position就是OpenGL系统内部的量,需要将处理好的量传递给它,它和out量都会传递到下面继续处理.

vertex shader takes the input attributes VertexPosition and VertexColor and passes them along to the fragment shader via the output variabes gl_Position and Color.

而关于主程序代码中,对着色器的处理与之前固定管线的结合,我们也并不需要担心太多,主程序中,单纯从代码上说它们可能交融的不是很紧密。

我们需要用一些window工具包来构建脚本,跨平台的有GLFW、GLUT、FLTK、QT或者wxWidgets。我们这里继续选用GLUT / freeglut

Compiling a shader

the diagram of this recipe

第一步 #  创建一个着色器对象   Create the shader object

  1. vertShader = glCreateShader(GL_VERTEX_SHADER);

函数的参数为着色器类型,该函数返回一个着色器对象的映射量(GLuint型),有时称之为对象句柄(object“handle”)。

如果创建失败,将返回一个0,所以我们可以根据返回值来检验是否创建成,如果没有创建成功,则返回一个正确的信息,以用于检验程序错误。

第二步 #  拷贝脚本源程序  Copy the source code

  1. const GLchar* shaderCode = loadShaderAsString("basic.vert");
  2. const GLchar* codeArray[] = {shaderCode};
  3. glShaderSource(vertShader, , codeArray, NULL);

loadShaderAsString(); 似乎是在flew头文件中才有的吧,我也不是很清楚,原则上应该是这么整的,但是出于,我也不知道它的头文件,所以就这个也是自己写的。

它的目的就是将以参数为名的文件信息内容读取出来。(因为我也不知道这个东西,之前一直以为是类型转换之类的,然后就出现了很多错误)

glShaderSource();      // load the source code into the shader object

因为这个函数支持一次性加载多个源代码,所以我们把读取的字符串指针放入一个数组中,即 codeArray

parameter 1:创建的着色器对象的映射值(即:shader_Obj  handle)

para  2:  Array 数组里面元素的个数

para  3:  Array数组

para  4:  一个数组,记录Array数组中每个元素的长度。如果你读取的每个源代码字符串是以null结尾的,那么此处可以填写NULL

第三步 #  编译着色器   Compile the shader

  1. glCompileShader(vertShader);

第四步 #  验证编译状态    Vertify the compilation status

  1. GLint result;
  2. glGetShaderiv(vertShader, GL_COMPILE_STATUS, &result);
  3. if (GL_FALSE == result)
  4. {
  5. fprintf(stderr, "Vertex shader compilation failed!\n");
  6. GLint logLen;
  7. glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &logLen);
  8. if (logLen > )
  9. {
  10. char* log = new char[logLen];
  11. GLsizei written;
  12. glGetShaderInfoLog(vertShader, logLen, &written, log);
  13. fprintf(stderr, "Shader log:\n%s", log);
  14. delete[] log;
  15. }
  16. }

glGetShaderiv(shader_Obj handle,要查询的状态的枚举量,用于记录结果的参数);

之后就是逐步验证着色器编译时候的每一个状态,直到找到错误点为止,然后提示相应的错误信息

Linking a shader program

the diagram of this recipe:

我们编译好我们的着色器之后,在将它们初始化到管线之前,我们还需要将它们链接到同一个着色器程序中。

我们知道上面提到的管线过程中,着色器之间是有数据交流的,所以为了让所有在同一个着色器程序中的各个着色器能够互相通信,我们需要把它们链接到同一个着色器程序中。

其步骤分为一下几步:

第一步 #  创建一个着色器程序     Create the program object

  1. GLuint programHandle = glCreateProgram();

第二步 #  关联着色器与着色器程序   attach the shaders to the program object

  1. glAttachShader(programHandle, vertShader);
  2. glAttachShader(programHandle, fragShader);

就是将着色器对象句柄与着色器程序句柄关联在一起

第三步 #  链接着色器程序   Link the progra

  1. glLinkProgram(programHandle);

检验,如果没问题,则进行下一步

第四步 #  Install the program into the OpenGL  pipline

  1. glUseProgram(programHandle);

到此,我们拥有了一个完整的OpenGL管线,然后就可以渲染了。

如果要删除句柄可以如下这么做

glDeleteProgram(programHandle); 如果正在使用该着色器程序,那么,将会当它 不再使用的时候删除

glDeleteShader   同上

代码:做了点小小的整合

顶点着色器:basic.vert

  1. #version
  2.  
  3. layout (location=) in vec3 VertexPosition;
  4. layout (location=) in vec3 VertexColor;
  5.  
  6. out vec3 Color;
  7.  
  8. void main()
  9. {
  10. Color = VertexColor;
  11.  
  12. gl_Position = vec4(VertexPosition,1.0);
  13. }

片元着色器:basic.frag

  1. #version
  2.  
  3. in vec3 Color;
  4.  
  5. out vec4 FragColor;
  6.  
  7. void main()
  8. {
  9. FragColor = vec4(Color, 1.0);
  10. }

注意两者后面要空一行空行,原因在前面说过了

主程序:

与本节无关的代码,如果有什么问题,可以参见上一节 笔记<1>:https://www.cnblogs.com/lv-anchoret/p/9221504.html

  1. //配置代码
  2. #if _MSC_VER>=1900
  3. #include "stdio.h"
  4. _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
  5. #ifdef __cplusplus
  6. extern "C"
  7. #endif
  8. FILE* __cdecl __iob_func(unsigned i) {
  9. return __acrt_iob_func(i);
  10. }
  11. #endif /* _MSC_VER>=1900 */
  12.  
  13. //code-list
  14. //using namespace std;
  15. #include <iostream>
  16. #include <fstream>
  17. using namespace std;
  18. #include <vgl.h>
  19.  
  20. GLint vertShader, fragShader;
  21. GLuint vaoHandle;
  22.  
  23. float positionDate[] =
  24. {
  25. -0.8f,-0.8f,0.0f,
  26. 0.8f,-0.8f,0.0f,
  27. 0.0f,0.8f,0.0f,
  28. };
  29. float colorDate[] =
  30. {
  31. 1.0f,0.0f,0.0f,
  32. 0.0f,1.0f,0.0f,
  33. 0.0f,0.0f,1.0f,
  34. };
  35.  
  36. void init();
  37. void Display();
  38. void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName); //编译着色器
  39. void _Link_Shader_(); //链接着色器
  40. bool readFile(const char*, string&); //读取文件内容的函数
  41.  
  42. int main(int argc, char** argv)
  43. {
  44. glutInit(&argc, argv);
  45. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
  46. glutInitWindowSize(, );
  47. glutInitWindowPosition(, );
  48. glutCreateWindow("test");
  49.  
  50. if (glewInit())
  51. {
  52. cout << "Error!" << glGetString(glewInit()) << endl;
  53. exit(EXIT_FAILURE);
  54. }
  55.  
  56. cout << "GL version:" << glGetString(GL_VERSION) << endl; //查询本机OpenGL版本
  57.  
  58. init();
  59.  
  60. _Compiling_Shader_(vertShader, GL_VERTEX_SHADER,"basic.vert");
  61. _Compiling_Shader_(fragShader, GL_FRAGMENT_SHADER, "basic.frag");
  62.  
  63. _Link_Shader_();
  64.  
  65. glutDisplayFunc(Display);
  66.  
  67. glutMainLoop();
  68. }
  69.  
  70. void init()
  71. {
  72. GLuint vboHandles[];
  73.  
  74. glGenBuffers(, vboHandles);
  75.  
  76. GLuint postionBufferHandle = vboHandles[];
  77. GLuint colorBufferHanle = vboHandles[];
  78.  
  79. glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
  80. glBufferData(GL_ARRAY_BUFFER, * sizeof(float), positionDate, GL_STATIC_DRAW);
  81.  
  82. glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
  83. glBufferData(GL_ARRAY_BUFFER, * sizeof(float), colorDate, GL_STATIC_DRAW);
  84.  
  85. glGenVertexArrays(, &vaoHandle);
  86. glBindVertexArray(vaoHandle);
  87.  
  88. glEnableVertexAttribArray();
  89. glEnableVertexAttribArray();
  90.  
  91. glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
  92. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr);
  93.  
  94. glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
  95. glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr);
  96. }
  97.  
  98. void Display()
  99. {
  100. glClear(GL_COLOR_BUFFER_BIT);
  101.  
  102. glBindVertexArray(vaoHandle);
  103. glDrawArrays(GL_TRIANGLES, , );
  104. glutSwapBuffers();
  105. }
  106.  
  107. bool readFile(const char* filename, string& content)
  108. {
  109. ifstream infile;
  110. infile.open(filename);
  111. if (!infile.is_open())return false;
  112.  
  113. char ch;
  114. infile >> noskipws;
  115. while (!infile.eof())
  116. {
  117. infile >> ch;
  118. content += ch;
  119. }
  120. infile.close();
  121. content += '\0';
  122. return true;
  123. }
  124.  
  125. void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName)
  126. {
  127. shaderHandle = glCreateShader(GL_Shader_type);
  128. //检查编译情况
  129. if ( == shaderHandle)
  130. {
  131. fprintf(stderr, "Error creating shader.\n");
  132. exit(EXIT_FAILURE);
  133. }
  134. string ShaderCode;
  135. if (!readFile(shaderName, ShaderCode))
  136. {
  137. cout << "readFile Error!" << endl;
  138. exit(EXIT_FAILURE);
  139. }
  140. const GLchar* shaderSource = ShaderCode.c_str();
  141. const GLchar* codeArray[] = { shaderSource };
  142. glShaderSource(shaderHandle, , codeArray, NULL);
  143.  
  144. glCompileShader(shaderHandle);
  145.  
  146. GLint result;
  147. glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result);
  148. if (GL_FALSE == result)
  149. {
  150. fprintf(stderr, "shader compilation failed!\n");
  151.  
  152. GLint logLen;
  153. glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen);
  154.  
  155. if (logLen > )
  156. {
  157. char* log = new char[logLen];
  158.  
  159. GLsizei written;
  160. glGetShaderInfoLog(shaderHandle, logLen, &written, log);
  161.  
  162. fprintf(stderr, "Shader log:\n%s", log);
  163. delete[] log;
  164. }
  165. }
  166. }
  167. void _Link_Shader_()
  168. {
  169. GLuint programHandle = glCreateProgram();
  170. if ( == programHandle)
  171. {
  172. fprintf(stderr, "Error creating program object.\n");
  173. exit();
  174. }
  175.  
  176. glAttachShader(programHandle, vertShader);
  177. glAttachShader(programHandle, fragShader);
  178.  
  179. glLinkProgram(programHandle);
  180.  
  181. GLint status;
  182. glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
  183. if (GL_FALSE == status)
  184. {
  185. fprintf(stderr, "Failed to link shader program!\n");
  186.  
  187. GLint logLen;
  188. glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
  189. if (logLen > )
  190. {
  191. char* log = new char[logLen];
  192.  
  193. GLsizei written;
  194. glGetShaderInfoLog(programHandle, logLen, &written, log);
  195.  
  196. fprintf(stderr, "Program log:\n%s", log);
  197. delete[] log;
  198. }
  199. }
  200. else
  201. glUseProgram(programHandle);
  202. }

如果出现_fprintf、_sscanf或者__iob_func问题,请参见:https://www.cnblogs.com/lv-anchoret/p/8729384.html

如果没有vgl.h的,见如下

  1. #ifndef __VGL_H__
  2. #define __VGL_H__
  3.  
  4. // #define USE_GL3W
  5.  
  6. #ifdef USE_GL3W
  7.  
  8. #include <GL3/gl3.h>
  9. #include <GL3/gl3w.h>
  10.  
  11. #else
  12.  
  13. #define GLEW_STATIC
  14.  
  15. #include <GL/glew.h>
  16.  
  17. #ifdef _MSC_VER
  18. # ifdef _DEBUG
  19. # if (_MSC_VER >= )
  20. # pragma comment (lib, "glew_static_vs2010_d.lib")
  21. # else
  22. # pragma comment (lib, "glew_static_d.lib")
  23. # endif
  24. # else
  25. # if (_MSC_VER >= )
  26. # pragma comment (lib, "glew_static_vs2010.lib")
  27. # else
  28. # pragma comment (lib, "glew_static.lib")
  29. # endif
  30. # endif
  31. #endif
  32.  
  33. #endif
  34.  
  35. #define FREEGLUT_STATIC
  36.  
  37. #include <GL/freeglut.h>
  38.  
  39. #ifdef _MSC_VER
  40. # ifdef _DEBUG
  41. # if (_MSC_VER >= )
  42. # pragma comment (lib, "freeglut_static_vs2010_d.lib")
  43. # else
  44. # pragma comment (lib, "freeglut_static_d.lib")
  45. # endif
  46. # else
  47. # if (_MSC_VER >= )
  48. # pragma comment (lib, "freeglut_static_vs2010.lib")
  49. # else
  50. # pragma comment (lib, "freeglut_static.lib")
  51. # endif
  52. # endif
  53. #endif
  54.  
  55. #define BUFFER_OFFSET(x) ((const void*) (x))
  56.  
  57. #endif /* __VGL_H__ */

如果下载的是9th版本的OpenGL库文件,那么vgl是不完整的

这个程序的运行效果在一开始有,就不放了,放一个错误提示,当初因为一个函数解读错误导致的

可以看出来错误定位还是非常精准的

今天就到这里

感谢您的阅读,生活愉快~

OpenGL 笔记 <2> Compiling and Linking a shader program的更多相关文章

  1. OpenGL笔记<5> shader 调试信息获取 Debug

    我们今天来讲调试信息,这个东西讲起来会比较无聊,因为都是一些函数调用,没啥可讲的,函数就是那样用的,不过其效果挺好玩的,同时在程序设计中也是很必要的,所以还是来写一下,不过,就是因为知识比较固定且简单 ...

  2. OpenGL笔记<第一章> 构建 GLSL class

    恭喜,我们终于很扎实地完成了第一章——glsl 入门 不幸的是,it's not the basic of GLSL shader ,我们下一节开篇,basic of GLSL shader 在下一章 ...

  3. opengl笔记——旋转,一段代码的理解

    重看:opengl笔记——OpenGL好资料备忘 在找到这段代码,对理解opengl旋转很有帮助 ... glPushMatrix(); // initialze ModelView matrix g ...

  4. OpenGL笔记(五) 着色器渲染(以Android为例)

    一.Android平台上下文环境的创建及初始化 1. 首先实例化Android上下文环境,即EGL的初始化. bool EGLCore::init(EGLContext sharedContext) ...

  5. OpenGL笔记<4> 数据传递二

    Sending data to a shader using uniform Preface 上一节我们介绍了通过顶点属性量进行数据传递,今天我们介绍一下通过uniform变量来进行数据传递的方法. ...

  6. OpenGL 笔记<3> 数据传递 一

    Sending data to a shader using vertex attributes and vertex buffer object 上次我们说到着色器的编译和连接,后面的事情没有做过多 ...

  7. 在为shader program pass uniform之前一定要先use program。

    在为shader program pass uniform之前一定要先use program.

  8. 学习笔记:GLSL Core Tutorial – Vertex Shader(内置变量说明)

    1.每个Vertex Shader都有用户定义的输入属性,例如:位置,法线向量和纹理坐标等.Vertex Shaders也接收一致变量(uniform variables). uniform vari ...

  9. OpenGL笔记(四) API参考

    常见API glActiveTexture 选择活动纹理单元 glAttachShader 将一个着色器对象绑定到一个程序对象 void glAttachShader(GLuint program, ...

随机推荐

  1. Android external扩展工程

    Android的扩展工程包含在external文件夹中,这是一些经过修改后适应Android系统的开源工程,这些工程有些在主机上运行,有些在目标机上运行: 工程名称  工程描述  aes  高级加密标 ...

  2. 1.2环境的准备(二)之Pycharm的安装和使用

    目录: 1.Pycharm的安装 2.Pycharm的使用 (一)pycharm的安装: (1)官网下载:(分为两个版本,专业版和社区版,社区版就足够我们学习用的)https://www.jetbra ...

  3. MySQL优化—工欲善其事,必先利其器(2)

    上一篇文章简单介绍了下EXPLAIN的用法,今天主要介绍以下几点内容: 慢查询日志 打开慢查询日志 保存慢查询日志到表中 慢查询日志分析 Percona Toolkit介绍 安装 pt-query-d ...

  4. Java的数组堆溢出问题

    在写测试方法的时候,生成了一个数组,之后报了堆溢出错误,这样的报错一般来说只要有一些JVM的基础都知道要用-Xmx.-Xms来开更大的堆,接下来看看我碰到的一个堆溢出的问题 在测试代码中开了一个500 ...

  5. 【python27】猜随机数的小游戏

    游戏规则: 猜一个随机数,如果猜对了就给出相应的猜成功提示语(自定义文字),如果猜大或者是猜小了,给出对应的提示,但总的猜次数为三次,每猜错一次重新猜时,给用户提示所剩余的猜次数 实现如下: # -* ...

  6. 7、Django的模型层(1)

    第1节:ORM简介 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极 ...

  7. swift的类型约束

    关键词: 类型与功能绑定.类型指定.访问控制. 类型约束的本质: 1.是否强制指定具有某些特征的类型:看类型构造器的定义本身是否对类型有约束: 2.访问控制:类型构造器的功能分为通用功能和约束功能: ...

  8. android studio 错误: InnerClass annotations are missing corresponding EnclosingMember annotations. Such InnerClass annotations are ignored

    android studio 错误: InnerClass annotations are missing corresponding EnclosingMember annotations. Suc ...

  9. Spark项目之电商用户行为分析大数据平台之(二)CentOS7集群搭建

    一.CentOS7集群搭建 1.1 准备3台centos7的虚拟机 IP及主机名规划如下: 192.168.123.110 spark1192.168.123.111 spark2192.168.12 ...

  10. Nginx HTTPS功能部署实践

    本文出处:http://oldboy.blog.51cto.com/2561410/1889346 30.1 文档目的 本文目的提高自己文档的写作能力及排版能力,加强上课所讲的内容得以锻炼也方便自己以 ...