PS:今天上午,非常郁闷,有很多简单基础的问题搞得我有些迷茫,哎,代码几天不写就忘。在现又不当COO,还是得用心记代码哦!
这篇文章述讲了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
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
glDeleteShader(VertexShaderID); glDeleteShader(FragmentShaderID);
指定应用一个Shader Program
删除一个Shader Program
备注
文章结束给大家分享下程序员的一些笑话语录:
乔布斯:怎么样还是咱安全吧!黑客:你的浏览器支持国内网银吗?苹果可以玩国内的网游吗乔布斯:......不可以黑客:那我研究你的漏洞干嘛,我也需要买奶粉!