OpenGL 笔记 <2> Compiling and Linking a shader program
Preface
这一节所有的主要内容都在一个OpenGL库文件中<LoadShaders.h> ,只需要用LoadShader()函数进行加载即可。但是由于老是出错,所以自己实现了一下,也用以提供给有兴趣了解着色器的编译、连接等原理的人。
因为程序基本都是自己实现的,所以,只需要包含最基本的OpenGL头文件即可运行。
效果如下:
Background
由于固定管线编程过程中涉及的大量计算(当然,上个例子并没有多少计算量)都是再CPU上进行的,而这些可能影响CPU处理其他事情的效率,所以不妨运用一种编程技法,将这些繁重的运算交予GPU去处理, 进而腾出CPU的时间。于是就引进了我们今天的前提——可编程管线,其对应的语言为GLSL(OpenGL Shading Language)。
先说一下博文撰写原则,博文撰写涉及到的背景知识只阐述与本次主题相关的,至于整合的那种完整的理论原理综述,如果需要的话会在后面做统一的阐述,而单篇有主题的文章中不做总结,不然可能会喧宾夺主。
看完这一篇可能会有几个疑问:着色器到底干了啥,它是怎么导致图形效果的,以及它里面每一个关键字都是干什么的,等等
上述问题,可能考虑下一篇讲,有人会问为什么不先弄清楚上面的再写下面的,因为我就是那样过来的,只有空洞的理论和语法,这种还是先看到效果,然后追究其原理更合适一点,这是我的一个安排和考虑,理解万岁。
Getting ready
觉得还是需要简单说明一下可编程渲染管线的流程(超级精简版):
对于顶点属性数据,如:位置、颜色、纹理等等,他们都需要进行某些操作,比如,位置可能需要平移,颜色可能需要渐变等,而这些一定都是矩阵运算,而不同的处理就对应着不同的着色器,嗯,就是酱紫。
而我们今天只需要用到顶点着色器和片元着色器。
以顶点着色器为例来介绍我们这次需要掌握的GLSL语法
vertex Shader (顶点着色器)
将下面的内容写到一个名为basic.vert的文件中 (emmm,最后一行可能要有一个空行,这个不是语法要求,这个是该程序所需,后面会讲到)
- #version
- layout (location=) in vec3 VertexPosition;
- layout (location=) in vec3 VertexColor;
- out vec3 Color;
- void main()
- {
- Color = VertexColor;
- gl_Position = vec4(VertexPosition,1.0);
- }
顶点着色器:对于绘制命令传输的每一个顶点,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
- vertShader = glCreateShader(GL_VERTEX_SHADER);
函数的参数为着色器类型,该函数返回一个着色器对象的映射量(GLuint型),有时称之为对象句柄(object“handle”)。
如果创建失败,将返回一个0,所以我们可以根据返回值来检验是否创建成,如果没有创建成功,则返回一个正确的信息,以用于检验程序错误。
第二步 # 拷贝脚本源程序 Copy the source code
- const GLchar* shaderCode = loadShaderAsString("basic.vert");
- const GLchar* codeArray[] = {shaderCode};
- 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
- glCompileShader(vertShader);
第四步 # 验证编译状态 Vertify the compilation status
- GLint result;
- glGetShaderiv(vertShader, GL_COMPILE_STATUS, &result);
- if (GL_FALSE == result)
- {
- fprintf(stderr, "Vertex shader compilation failed!\n");
- GLint logLen;
- glGetShaderiv(vertShader, GL_INFO_LOG_LENGTH, &logLen);
- if (logLen > )
- {
- char* log = new char[logLen];
- GLsizei written;
- glGetShaderInfoLog(vertShader, logLen, &written, log);
- fprintf(stderr, "Shader log:\n%s", log);
- delete[] log;
- }
- }
glGetShaderiv(shader_Obj handle,要查询的状态的枚举量,用于记录结果的参数);
之后就是逐步验证着色器编译时候的每一个状态,直到找到错误点为止,然后提示相应的错误信息
Linking a shader program
the diagram of this recipe:
我们编译好我们的着色器之后,在将它们初始化到管线之前,我们还需要将它们链接到同一个着色器程序中。
我们知道上面提到的管线过程中,着色器之间是有数据交流的,所以为了让所有在同一个着色器程序中的各个着色器能够互相通信,我们需要把它们链接到同一个着色器程序中。
其步骤分为一下几步:
第一步 # 创建一个着色器程序 Create the program object
- GLuint programHandle = glCreateProgram();
第二步 # 关联着色器与着色器程序 attach the shaders to the program object
- glAttachShader(programHandle, vertShader);
- glAttachShader(programHandle, fragShader);
就是将着色器对象句柄与着色器程序句柄关联在一起
第三步 # 链接着色器程序 Link the progra
- glLinkProgram(programHandle);
检验,如果没问题,则进行下一步
第四步 # Install the program into the OpenGL pipline
- glUseProgram(programHandle);
到此,我们拥有了一个完整的OpenGL管线,然后就可以渲染了。
如果要删除句柄可以如下这么做
glDeleteProgram(programHandle); 如果正在使用该着色器程序,那么,将会当它 不再使用的时候删除
glDeleteShader 同上
代码:做了点小小的整合
顶点着色器:basic.vert
- #version
- layout (location=) in vec3 VertexPosition;
- layout (location=) in vec3 VertexColor;
- out vec3 Color;
- void main()
- {
- Color = VertexColor;
- gl_Position = vec4(VertexPosition,1.0);
- }
片元着色器:basic.frag
- #version
- in vec3 Color;
- out vec4 FragColor;
- void main()
- {
- FragColor = vec4(Color, 1.0);
- }
注意两者后面要空一行空行,原因在前面说过了
主程序:
与本节无关的代码,如果有什么问题,可以参见上一节 笔记<1>:https://www.cnblogs.com/lv-anchoret/p/9221504.html
- //配置代码
- #if _MSC_VER>=1900
- #include "stdio.h"
- _ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
- #ifdef __cplusplus
- extern "C"
- #endif
- FILE* __cdecl __iob_func(unsigned i) {
- return __acrt_iob_func(i);
- }
- #endif /* _MSC_VER>=1900 */
- //code-list
- //using namespace std;
- #include <iostream>
- #include <fstream>
- using namespace std;
- #include <vgl.h>
- GLint vertShader, fragShader;
- GLuint vaoHandle;
- float positionDate[] =
- {
- -0.8f,-0.8f,0.0f,
- 0.8f,-0.8f,0.0f,
- 0.0f,0.8f,0.0f,
- };
- float colorDate[] =
- {
- 1.0f,0.0f,0.0f,
- 0.0f,1.0f,0.0f,
- 0.0f,0.0f,1.0f,
- };
- void init();
- void Display();
- void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName); //编译着色器
- void _Link_Shader_(); //链接着色器
- bool readFile(const char*, string&); //读取文件内容的函数
- int main(int argc, char** argv)
- {
- glutInit(&argc, argv);
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
- glutInitWindowSize(, );
- glutInitWindowPosition(, );
- glutCreateWindow("test");
- if (glewInit())
- {
- cout << "Error!" << glGetString(glewInit()) << endl;
- exit(EXIT_FAILURE);
- }
- cout << "GL version:" << glGetString(GL_VERSION) << endl; //查询本机OpenGL版本
- init();
- _Compiling_Shader_(vertShader, GL_VERTEX_SHADER,"basic.vert");
- _Compiling_Shader_(fragShader, GL_FRAGMENT_SHADER, "basic.frag");
- _Link_Shader_();
- glutDisplayFunc(Display);
- glutMainLoop();
- }
- void init()
- {
- GLuint vboHandles[];
- glGenBuffers(, vboHandles);
- GLuint postionBufferHandle = vboHandles[];
- GLuint colorBufferHanle = vboHandles[];
- glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
- glBufferData(GL_ARRAY_BUFFER, * sizeof(float), positionDate, GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
- glBufferData(GL_ARRAY_BUFFER, * sizeof(float), colorDate, GL_STATIC_DRAW);
- glGenVertexArrays(, &vaoHandle);
- glBindVertexArray(vaoHandle);
- glEnableVertexAttribArray();
- glEnableVertexAttribArray();
- glBindBuffer(GL_ARRAY_BUFFER, postionBufferHandle);
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr);
- glBindBuffer(GL_ARRAY_BUFFER, colorBufferHanle);
- glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, , nullptr);
- }
- void Display()
- {
- glClear(GL_COLOR_BUFFER_BIT);
- glBindVertexArray(vaoHandle);
- glDrawArrays(GL_TRIANGLES, , );
- glutSwapBuffers();
- }
- bool readFile(const char* filename, string& content)
- {
- ifstream infile;
- infile.open(filename);
- if (!infile.is_open())return false;
- char ch;
- infile >> noskipws;
- while (!infile.eof())
- {
- infile >> ch;
- content += ch;
- }
- infile.close();
- content += '\0';
- return true;
- }
- void _Compiling_Shader_(GLint& shaderHandle, GLint GL_Shader_type, GLchar* shaderName)
- {
- shaderHandle = glCreateShader(GL_Shader_type);
- //检查编译情况
- if ( == shaderHandle)
- {
- fprintf(stderr, "Error creating shader.\n");
- exit(EXIT_FAILURE);
- }
- string ShaderCode;
- if (!readFile(shaderName, ShaderCode))
- {
- cout << "readFile Error!" << endl;
- exit(EXIT_FAILURE);
- }
- const GLchar* shaderSource = ShaderCode.c_str();
- const GLchar* codeArray[] = { shaderSource };
- glShaderSource(shaderHandle, , codeArray, NULL);
- glCompileShader(shaderHandle);
- GLint result;
- glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &result);
- if (GL_FALSE == result)
- {
- fprintf(stderr, "shader compilation failed!\n");
- GLint logLen;
- glGetShaderiv(shaderHandle, GL_INFO_LOG_LENGTH, &logLen);
- if (logLen > )
- {
- char* log = new char[logLen];
- GLsizei written;
- glGetShaderInfoLog(shaderHandle, logLen, &written, log);
- fprintf(stderr, "Shader log:\n%s", log);
- delete[] log;
- }
- }
- }
- void _Link_Shader_()
- {
- GLuint programHandle = glCreateProgram();
- if ( == programHandle)
- {
- fprintf(stderr, "Error creating program object.\n");
- exit();
- }
- glAttachShader(programHandle, vertShader);
- glAttachShader(programHandle, fragShader);
- glLinkProgram(programHandle);
- GLint status;
- glGetProgramiv(programHandle, GL_LINK_STATUS, &status);
- if (GL_FALSE == status)
- {
- fprintf(stderr, "Failed to link shader program!\n");
- GLint logLen;
- glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &logLen);
- if (logLen > )
- {
- char* log = new char[logLen];
- GLsizei written;
- glGetShaderInfoLog(programHandle, logLen, &written, log);
- fprintf(stderr, "Program log:\n%s", log);
- delete[] log;
- }
- }
- else
- glUseProgram(programHandle);
- }
如果出现_fprintf、_sscanf或者__iob_func问题,请参见:https://www.cnblogs.com/lv-anchoret/p/8729384.html
如果没有vgl.h的,见如下
- #ifndef __VGL_H__
- #define __VGL_H__
- // #define USE_GL3W
- #ifdef USE_GL3W
- #include <GL3/gl3.h>
- #include <GL3/gl3w.h>
- #else
- #define GLEW_STATIC
- #include <GL/glew.h>
- #ifdef _MSC_VER
- # ifdef _DEBUG
- # if (_MSC_VER >= )
- # pragma comment (lib, "glew_static_vs2010_d.lib")
- # else
- # pragma comment (lib, "glew_static_d.lib")
- # endif
- # else
- # if (_MSC_VER >= )
- # pragma comment (lib, "glew_static_vs2010.lib")
- # else
- # pragma comment (lib, "glew_static.lib")
- # endif
- # endif
- #endif
- #endif
- #define FREEGLUT_STATIC
- #include <GL/freeglut.h>
- #ifdef _MSC_VER
- # ifdef _DEBUG
- # if (_MSC_VER >= )
- # pragma comment (lib, "freeglut_static_vs2010_d.lib")
- # else
- # pragma comment (lib, "freeglut_static_d.lib")
- # endif
- # else
- # if (_MSC_VER >= )
- # pragma comment (lib, "freeglut_static_vs2010.lib")
- # else
- # pragma comment (lib, "freeglut_static.lib")
- # endif
- # endif
- #endif
- #define BUFFER_OFFSET(x) ((const void*) (x))
- #endif /* __VGL_H__ */
如果下载的是9th版本的OpenGL库文件,那么vgl是不完整的
这个程序的运行效果在一开始有,就不放了,放一个错误提示,当初因为一个函数解读错误导致的
可以看出来错误定位还是非常精准的
今天就到这里
感谢您的阅读,生活愉快~
OpenGL 笔记 <2> Compiling and Linking a shader program的更多相关文章
- OpenGL笔记<5> shader 调试信息获取 Debug
我们今天来讲调试信息,这个东西讲起来会比较无聊,因为都是一些函数调用,没啥可讲的,函数就是那样用的,不过其效果挺好玩的,同时在程序设计中也是很必要的,所以还是来写一下,不过,就是因为知识比较固定且简单 ...
- OpenGL笔记<第一章> 构建 GLSL class
恭喜,我们终于很扎实地完成了第一章——glsl 入门 不幸的是,it's not the basic of GLSL shader ,我们下一节开篇,basic of GLSL shader 在下一章 ...
- opengl笔记——旋转,一段代码的理解
重看:opengl笔记——OpenGL好资料备忘 在找到这段代码,对理解opengl旋转很有帮助 ... glPushMatrix(); // initialze ModelView matrix g ...
- OpenGL笔记(五) 着色器渲染(以Android为例)
一.Android平台上下文环境的创建及初始化 1. 首先实例化Android上下文环境,即EGL的初始化. bool EGLCore::init(EGLContext sharedContext) ...
- OpenGL笔记<4> 数据传递二
Sending data to a shader using uniform Preface 上一节我们介绍了通过顶点属性量进行数据传递,今天我们介绍一下通过uniform变量来进行数据传递的方法. ...
- OpenGL 笔记<3> 数据传递 一
Sending data to a shader using vertex attributes and vertex buffer object 上次我们说到着色器的编译和连接,后面的事情没有做过多 ...
- 在为shader program pass uniform之前一定要先use program。
在为shader program pass uniform之前一定要先use program.
- 学习笔记:GLSL Core Tutorial – Vertex Shader(内置变量说明)
1.每个Vertex Shader都有用户定义的输入属性,例如:位置,法线向量和纹理坐标等.Vertex Shaders也接收一致变量(uniform variables). uniform vari ...
- OpenGL笔记(四) API参考
常见API glActiveTexture 选择活动纹理单元 glAttachShader 将一个着色器对象绑定到一个程序对象 void glAttachShader(GLuint program, ...
随机推荐
- Android external扩展工程
Android的扩展工程包含在external文件夹中,这是一些经过修改后适应Android系统的开源工程,这些工程有些在主机上运行,有些在目标机上运行: 工程名称 工程描述 aes 高级加密标 ...
- 1.2环境的准备(二)之Pycharm的安装和使用
目录: 1.Pycharm的安装 2.Pycharm的使用 (一)pycharm的安装: (1)官网下载:(分为两个版本,专业版和社区版,社区版就足够我们学习用的)https://www.jetbra ...
- MySQL优化—工欲善其事,必先利其器(2)
上一篇文章简单介绍了下EXPLAIN的用法,今天主要介绍以下几点内容: 慢查询日志 打开慢查询日志 保存慢查询日志到表中 慢查询日志分析 Percona Toolkit介绍 安装 pt-query-d ...
- Java的数组堆溢出问题
在写测试方法的时候,生成了一个数组,之后报了堆溢出错误,这样的报错一般来说只要有一些JVM的基础都知道要用-Xmx.-Xms来开更大的堆,接下来看看我碰到的一个堆溢出的问题 在测试代码中开了一个500 ...
- 【python27】猜随机数的小游戏
游戏规则: 猜一个随机数,如果猜对了就给出相应的猜成功提示语(自定义文字),如果猜大或者是猜小了,给出对应的提示,但总的猜次数为三次,每猜错一次重新猜时,给用户提示所剩余的猜次数 实现如下: # -* ...
- 7、Django的模型层(1)
第1节:ORM简介 ORM简介 MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极 ...
- swift的类型约束
关键词: 类型与功能绑定.类型指定.访问控制. 类型约束的本质: 1.是否强制指定具有某些特征的类型:看类型构造器的定义本身是否对类型有约束: 2.访问控制:类型构造器的功能分为通用功能和约束功能: ...
- android studio 错误: InnerClass annotations are missing corresponding EnclosingMember annotations. Such InnerClass annotations are ignored
android studio 错误: InnerClass annotations are missing corresponding EnclosingMember annotations. Suc ...
- Spark项目之电商用户行为分析大数据平台之(二)CentOS7集群搭建
一.CentOS7集群搭建 1.1 准备3台centos7的虚拟机 IP及主机名规划如下: 192.168.123.110 spark1192.168.123.111 spark2192.168.12 ...
- Nginx HTTPS功能部署实践
本文出处:http://oldboy.blog.51cto.com/2561410/1889346 30.1 文档目的 本文目的提高自己文档的写作能力及排版能力,加强上课所讲的内容得以锻炼也方便自己以 ...