【OpenGL】Shader概述
这篇文章讲述了Shader是如何编译和链接,最终在OpenGL程序中使用的。当然,不了解这些我们仍然可以正常工作,但是作为初学者,了解这些会让我更能明白自己在干嘛。。。
综述
哈,大名鼎鼎的Shader终于让我给见到了……之前在学习Unity3D的时候就被群里的大牛耳濡目染说Shader如何如何重要,现在终于轮到自己领教了。吐槽完毕,进入正题。
Shader的编译器被内嵌到OpenGL库的内部,而且必须在运行OpenGL程序时才能编译。目前还没有可以提前编译Shader的工具。在最新的OpenGL4.1中好像正在改善。
目前的学习中,我使用的是这个教程提 供的一个载入shader的代码。代码不长,功能不全,只能同时载入vertex shader和fragment shader(这里是保存在两个单独的文件里,后缀分别的vertexshader和fragmentshader,后缀不重要,即便是txt也可以,重 要的是内容使用的是GLSL语法),但是对于初学者够用了。(实际上,我们通常需要至少两个shader。)代码如下:
- GLuint LoadShaders(const char * vertex_file_path,const char * fragment_file_path){
- // Create the shaders
- GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
- GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
- // Read the Vertex Shader code from the file
- std::string VertexShaderCode;
- std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
- if(VertexShaderStream.is_open())
- {
- std::string Line = "";
- while(getline(VertexShaderStream, Line))
- VertexShaderCode += "n" + Line;
- VertexShaderStream.close();
- }
- // Read the Fragment Shader code from the file
- std::string FragmentShaderCode;
- std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in);
- if(FragmentShaderStream.is_open()){
- std::string Line = "";
- while(getline(FragmentShaderStream, Line))
- FragmentShaderCode += "n" + Line;
- FragmentShaderStream.close();
- }
- GLint Result = GL_FALSE;
- int InfoLogLength;
- // Compile Vertex Shader
- printf("Compiling shader : %sn", vertex_file_path);
- char const * VertexSourcePointer = VertexShaderCode.c_str();
- glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
- glCompileShader(VertexShaderID);
- // Check Vertex Shader
- glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- std::vector VertexShaderErrorMessage(InfoLogLength);
- glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
- fprintf(stdout, "%sn", &VertexShaderErrorMessage[0]);
- // Compile Fragment Shader
- printf("Compiling shader : %sn", fragment_file_path);
- char const * FragmentSourcePointer = FragmentShaderCode.c_str();
- glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer , NULL);
- glCompileShader(FragmentShaderID);
- // Check Fragment Shader
- glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- std::vector FragmentShaderErrorMessage(InfoLogLength);
- glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]);
- fprintf(stdout, "%sn", &FragmentShaderErrorMessage[0]);
- // Link the program
- fprintf(stdout, "Linking programn");
- GLuint ProgramID = glCreateProgram();
- glAttachShader(ProgramID, VertexShaderID);
- glAttachShader(ProgramID, FragmentShaderID);
- glLinkProgram(ProgramID);
- // Check the program
- glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
- glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- std::vector ProgramErrorMessage( max(InfoLogLength, int(1)) );
- glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
- fprintf(stdout, "%sn", &ProgramErrorMessage[0]);
- glDeleteShader(VertexShaderID);
- glDeleteShader(FragmentShaderID);
- return ProgramID;
- }
为了举例,我们以下都使用下面两个shader。
第一个shader是一个顶点着色器,vertex shader,文件名为ExampleShader.vertexshader。
- #version 400
- in vec3 VertexPosition;
- in vec3 VertexColor;
- out vec3 Color;
- void main()
- Color = VertexColor;
- gl_Position = vec4( VertexPosition, 1.0 );
- }
这里简单解释一下。它接受两个输入和一个输出,并使用输入VertexPosition给gl_position赋值,使用VertexColor给输出Color赋值,而Color将会传递给下面的片段着色器。
第二个是片段着色器,fragment shader,文件名为ExampleShader.fragmentshader。
- #version 400
- in vec3 Color;
- out vec4 FragColor;
- void main() {
- FragColor = vec4(Color, 1.0);
- }
顶点着色器会在每个顶点上调用一次,而片段着色器则会在每个像素上调用一次。
----------------------------------------------------------------分割线
--------------------------------------------------------------------
编译一个Shader
- // Create the shaders
- GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER);
- GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
然后将shader源代码(source
code)复制到shader对象中。由于这里是从文件里读入代码,因此先将源代码分别读入到一个string类型的变量里
(VertexShaderCode和FragmentShaderCode),再把指针传递给shader对象:
- // Read the Vertex Shader code from the file
- std::string VertexShaderCode;
- std::ifstream VertexShaderStream(vertex_file_path, std::ios::in);
- if(VertexShaderStream.is_open())
- {
- std::string Line = "";
- while(getline(VertexShaderStream, Line))
- VertexShaderCode += "n" + Line;
- VertexShaderStream.close();
- }
- char const * VertexSourcePointer = VertexShaderCode.c_str();
- glShaderSource(VertexShaderID, 1, &VertexSourcePointer , NULL);
最后,编译shader。通常我们需要检查一下shader是否编译成功,不成功的话再打出错误信息,这通过两个变量Result和InfoLogLength来实现:
- GLint Result = GL_FALSE;
- int InfoLogLength;
- glCompileShader(VertexShaderID);
- // Check Vertex Shader
- glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result);
- glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- std::vector VertexShaderErrorMessage(InfoLogLength);
- glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]);
- fprintf(stdout, "%sn", &VertexShaderErrorMessage[0]);
上面的筛选的vertex shader的编译过程,当然fragment shader的编译是一样的。
链接一个Shader
出匹配问题。例如这里,ExampleShader.vertexshader里的输出Color需要和
ExampleShader.fragmentshader里的输入Color相匹配。
- GLuint ProgramID = glCreateProgram();
然后将之前创建好的shader object附加给它。
- glAttachShader(ProgramID, VertexShaderID);
- glAttachShader(ProgramID, FragmentShaderID);
最后,进行链接。
- glLinkProgram(ProgramID);
和之前需要检查编译状态类似,我们也需要检查链接状态,如果链接不成功,就打出提示信息。
- // Check the program
- glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result);
- glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength);
- std::vector ProgramErrorMessage( max(InfoLogLength, int(1)) );
- glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]);
- fprintf(stdout, "%sn", &ProgramErrorMessage[0]);
删除一个Shader
经被附加到一个program对象(比如现在),那么实际上它并没有立刻被删除,而是出于一种挂起状态。当它从附加的program对象上被卸载时,才会
真正从内存中删除。
- glDeleteShader(VertexShaderID);
- glDeleteShader(FragmentShaderID);
指定使用一个Shader Program
ProgramID);函数,ProgramID就是上面函数的返回值,它提供了一个Shadr
program对象的句柄。在一个OpenGL程序中,我们可以使用多个shader
programs,我们可以通过使用glUseProgram在OpenGL管道里进行切入和切除,以便选择不同的程序。
删除一个Shader Program
program时,可以通过调用glDeleteProgram(ProgramID)来删除一个shader
program,以释放OpenGL内存。注意,如果当前的shader
program正在被使用,那么它并不会立刻被删除,而是出于一种挂起状态,直到程序不再使用它(可能切换到另一个Shader
Program)。这时,之前附加的shader对象就会真正被删除了(如果之前调用过glDeleteShader的话)。
【OpenGL】Shader概述的更多相关文章
- OpenGL Shader in OpenCASCADE
OpenGL Shader in OpenCASCADE eryar@163.com Abstract. As implementation of one of the strategic steps ...
- A Simple OpenGL Shader Example II
A Simple OpenGL Shader Example II eryar@163.com Abstract. The OpenGL Shading Language syntax comes f ...
- A Simple OpenGL Shader Example
A Simple OpenGL Shader Example eryar@163.com Abstract. OpenGL Shading Language, the high-level progr ...
- OpenGL Shader源码分享
Opengl shader程序,旗帜混合纹理加载,通过N张图片,能够组合出数百个:http://www.eyesourcecode.com/thread-39015-1-1.html 用GLSL做了一 ...
- 【玩转cocos2d-x之四十】怎样在Cocos2d-x 3.0中使用opengl shader?
有小伙伴提出了这个问题.事实上GLProgramCocos2d-x引擎自带了.全然能够直接拿来用. 先上图吧. 使用opengl前后的对照: watermark/2/text/aHR0cDovL2Js ...
- 「游戏引擎 浅入浅出」4.1 Unity Shader和OpenGL Shader
「游戏引擎 浅入浅出」从零编写游戏引擎教程,是一本开源电子书,PDF/随书代码/资源下载: https://github.com/ThisisGame/cpp-game-engine-book 4.1 ...
- 【Unity Shaders】Shader学习资源和Surface Shader概述
写在前面 写这篇文章的时候,我断断续续学习Unity Shader半年了,其实还是个门外汉.我也能体会很多童鞋那种想要学好Shader却无从下手的感觉.在这个期间,我找到一些学习Shader的教程以及 ...
- Unity Shader概述
一.概述 在Unity中需要配合使用材质和Unity Shader才能达到需要的效果.常见的流程:(1)创建一个材质:(2)创建一个Unity Shader,并把它赋给创建的材质:(3)把材质赋给要渲 ...
- OpenGL shader 中关于顶点坐标值的思考
今天工作中需要做一个事情: 在shader内部做一些空间距离上的计算,而且需要对所有的点进行计算,符合条件的显示,不符合条件的点不显示. 思路很简单,在vertex shader内知道顶点坐标,进行计 ...
随机推荐
- 开发rsync启动脚本
rsync rsync是类unix系统下的数据镜像备份工具——remote sync.一款快速增量备份工具 Remote Sync,远程同步 支持本地复制,或者与其他SSH.rsync主机同步. ...
- ELK初步指南
ELK的简单科普文章,加入了自己的一些理解. 内容包括ELK的基本介绍, 应用场景, 架构设计, 监控及自监控, 业界进展及推荐资料等. 用户故事 场景一 作为一个运维工程师, 某天虚拟机出现故障, ...
- MySQL存储过程入门教程
存储过程介绍 存储过程是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中.用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它.存储过程可由应用程序通过一个调用来执行,而且 ...
- LINQ 学习路程 -- 查询操作 OfType
OfType操作根据集合中的元素是否是给定的类型进行筛选 IList mixedList = new ArrayList(); mixedList.Add(); mixedList.Add(" ...
- 算法(Algorithms)第4版 练习 1.5.9
不可能.如果是weighted quick-union的话,6的父节点应该是5,而不是5的父节点是6.
- java中的Enumeration
Enumeration en = pp.keys(); while(en.hasMoreElements()){ String key = (String)en.nextElement(); Syst ...
- 是否可以重定向到 WEB-INFO 下的页面?
redirect的路径一定不能在WEB-INF路径下,因为redirect是相当于用户直接访问了路径,而用户不能访问WEB-INF目录下的文件,只有程序内部转发的时候才能转发到WEB-INF下的JSP ...
- codeforces 637D D. Running with Obstacles(dp,水题,贪心)
题目链接: D. Running with Obstacles time limit per test 2 seconds memory limit per test 256 megabytes in ...
- Java读取文件的时候,如何让指针重新回到文件的开头
今天在测试IO流的使用的时候发现在reader读取文件之后,再向文件添加内容,再继续读文件,打印出的结果只能读取追加的文件. 如何才能重新读取呢?试了mark和reset,似乎会报异常.记在这以后看是 ...
- HTML5视音频标签参考
本文将介绍HTML5中的视音频标签和对应的DOM对象.是相关资料的中文化版本,可以作为编写相关应用的简易中文参考手册. 一些约定 所有浏览器:指支持HTML5的常见桌面浏览器,包括IE9+.Firef ...