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, ...
随机推荐
- 高通LCD的pwm背光驱动
发生异常的现象: msm8953 lcd在快速亮灭的情况下背光概率性休眠不灭:测量高通pwm,发现正常的时候pwm的管脚LCM_BL_PWM为低电平,失败的时候为高电平: 根据原理图: mpp是什么? ...
- Python通过Zabbix API获得数据
Zabbix API查询:https://www.zabbix.com/documentation/2.0/manual/appendix/api/api import json,urllib2 fr ...
- Python抓取zabbix性能监控图
一.通过查询zabbix db的方式通过主机IP获取到所需要的graphid(比如CPU监控图.内存监控图等,每个图对应一个graphid),最后将图片保存到本地 注:该graph必须要在 scree ...
- 运行结果出现Process finished with exit code 0
表示程序正常执行完毕并退出. 可以科普一下exit code,在大部分编程语言中都适用 exit code 0表示程序执行成功,正常退出 exit code 1表示执行过程中遇到了某些问题或者错误,非 ...
- Beyond Compare使用
1.通过过滤功能设置要比较的文件: 2.all,diff功能适用于文件本身的差异以及目录的差异 其它问题: 1.Beyond Compare比较文件时,相同的文件也显示为红色(有差异) http:// ...
- CGJ02、BD09、西安80、北京54、CGCS2000常用坐标系详解
一.万能地图下载器中的常用坐标系 水经注万能地图下载器中的常用的坐标系主要包括WGS84经纬度投影.WGS84 Web 墨卡托投影.WGS84 UTM 投影.北京54高斯投影.西安80高斯投影.CGC ...
- iosclient发现_世界杯送流量活动项目总结
世界杯如火如荼的进行.视频站点相似于门户站点.须要高速依据外部环境更新内容. 产品经理须要策划活动,并安排实施.这个活动就是在这样背景下产生的,爱奇艺与运营商合作,实现双赢.爱奇艺能够通过运营商 ...
- scrapy的request的meta参数是什么意思?
作者:乌尔班链接:https://www.zhihu.com/question/54773510/answer/146971644来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- 使用级联分类器实现人脸检测(OpenCV自带的数据)
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace st ...
- CentOS7下双网卡iptables端口转发规则
1. 拓扑图 10.1.1.173(内网目标) <-------- 10.1.1.207(内网网关)+172.16.5.100(外网入口) <----------- 172.16.6. ...