设置GLSL
这一节讲述在OpenGL中配置GLSL,假设你已经写好了顶点shader和像素shader。如果你还没有准备好,可以从如下网址获得相关内容:
http://www.3dshaders.com/home/
http://www.opengl.org/sdk/tools/ShaderDesigner/
http://developer.amd.com/archive/gpu/rendermonkey/pages/default.aspx
在OpenGL中,GLSL的shader使用的流程与C语言相似,每个shader类似一个C模块,首先需要单独编译(compile),然后一组编译好的shader连接(link)成一个完整程序。
这里将忽略ARB扩展,只列举OpenGL2.0的代码。建议使用GLEW库:
下面的代码检查OpenGL 2.0是否可用:
-
-
-
-
void main(int argc, char **argv)
-
{
-
glutInit(&argc, argv);
-
...
-
glewInit();
-
-
if (glewIsSupported( "GL_VERSION_2_0"))
-
printf( "Ready for OpenGL 2.0\n");
-
else
-
{
-
printf( "OpenGL 2.0 not supported\n");
-
exit( 1);
-
}
-
setShaders();
-
-
glutMainLoop();
-
}
下图显示了创建shader的必要步骤,函数的具体使用方法将在下面各小结描述:
创建shader
下图显示了创建shader的步骤:
首先创建一个对象作为shader的容器,这个创建函数将返回容器的句柄。
-
GLuint glCreateShader(GLenum shaderType);
-
参数:
-
·shaderType – GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
下一步将添加源代码。shader的源代码是一个字符串数组,添加的语法如下:
-
void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lenOfStrings);
-
参数:
-
·shader – the handler to the shader.
-
·numOfStrings – the number of strings in the array.
-
·strings – the array of strings.
-
·lenOfStrings – an array with the length of each string, or NULL, meaning that the strings are NULL terminated.
-
void glCompileShader(GLuint shader);
-
参数:
-
•shader – the handler to the shader.
创建程序
下图显示了获得一个可以运行的shader程序的步骤:
首先创建一个对象,作为程序的容器。此函数返回容器的句柄。
GLuint glCreateProgram(void);
你可以创建任意多个程序,在渲染时,可以在不同程序中切换,甚至在某帧返回固定功能流水线。比如你想用折射和反射shader绘制一个茶杯,然后回到固定功能生成立方体环境贴图(cube map)显示背景。
下面将把上一节编译的shader附加到刚刚创建的程序中。方法如下:
-
void glAttachShader(GLuint program, GLuint shader);
-
参数:
-
·program – the handler to the program.
-
·shader – the handler to the shader you want to attach.
你也可以把一个shader附加到多个程序,比如你想在不同程序中使用某个相同的shader。
最后一步是连接程序。方法如下:
-
void glLinkProgram(GLuint program);
-
参数:
-
·program – the handler to the program.
程序连接后,可以调用glUseProgram来使用程序。每个程序都分配了一个句柄,你可以事先连接多个程序以备使用。
-
void glUseProgram(GLuint prog);
-
参数:
-
·prog – the handler to the program you want to use, or zero to return to fixed functionality.
例子
下面的代码包含了上面描述的所有步骤,参数p,f,v是全局的GLuint型变量。
-
void setShaders()
-
{
-
char *vs,*fs;
-
-
v = glCreateShader(GL_VERTEX_SHADER);
-
f = glCreateShader(GL_FRAGMENT_SHADER);
-
-
vs = textFileRead( "toon.vert");
-
fs = textFileRead( "toon.frag");
-
-
const char *vv = vs;
-
const char *ff = fs;
-
-
glShaderSource(v, 1, &vv, NULL);
-
glShaderSource(f, 1, &ff, NULL);
-
-
free(vs); free(fs);
-
-
glCompileShader(v);
-
glCompileShader(f);
-
-
p = glCreateProgram();
-
-
glAttachShader(p, v);
-
glAttachShader(p, f);
-
-
glLinkProgram(p);
-
glUseProgram(p);
-
}
http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl_2.0.zip
完整例子中包含了shader代码及文本文件读入程序。
错误处理
调试shader是很困难的。目前还没有像printf这样的东西,虽然未来可能出现有调试功能的开发工具。
编译阶段的状态可以用如下函数获得:
-
void glGetShaderiv(GLuint object, GLenum type, int *param);
-
参数:
-
·object – the handler to the object. Either a shader or a program
-
·type – GL_COMPILE_STATUS.
-
·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
-
void glGetProgramiv(GLuint object, GLenum type, int *param);
-
参数:
-
·object – the handler to the object. Either a shader or a program
-
·type – GL_LINK_STATUS.
-
·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.
为了获得特定shader或程序的日志,可以使用如下程序:
-
void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);
-
void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);
-
参数:
-
·object – the handler to the object. Either a shader or a program
-
·maxLen – The maximum number of chars to retrieve from the InfoLog.
-
·len – returns the actual length of the retrieved InfoLog.
-
· log – The log itself.
-
void glGetShaderiv(GLuint object, GLenum type, int *param);
-
void glGetProgramiv(GLuint object, GLenum type, int *param);
-
参数:
-
·object – the handler to the object. Either a shader or a program
-
·type – GL_INFO_LOG_LENGTH.
-
·param – the return value, the length of the InfoLog.
下面的函数可以用来打印InfoLog的内容:
-
void printShaderInfoLog(GLuint obj)
-
{
-
int infologLength = 0;
-
int charsWritten = 0;
-
char *infoLog;
-
-
glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
-
-
if (infologLength > 0)
-
{
-
infoLog = ( char *) malloc(infologLength);
-
glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
-
printf( "%s\n",infoLog);
-
free(infoLog);
-
}
-
}
-
-
void printProgramInfoLog(GLuint obj)
-
{
-
int infologLength = 0;
-
int charsWritten = 0;
-
char *infoLog;
-
-
glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
-
-
if (infologLength > 0)
-
{
-
infoLog = ( char *) malloc(infologLength);
-
glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
-
printf( "%s\n",infoLog);
-
free(infoLog);
-
}
-
}
清理
前面的小节讲到了附加一个shader到一个程序中,这里的调用是将shader从程序中分离:
-
void glDetachShader(GLuint program, GLuint shader);
-
参数:
-
·program – The program to detach from.
-
·shader – The shader to detach.
-
void glDeleteShader(GLuint id);
-
void glDeleteProgram(GLuint id);
-
参数:
-
·id – The hanuler of the shader or program to delete.
如果一个shader还附加在某个程序中,这个shader并不能真正删除,只能标记为删除。当这个shader从所有程序中分离之后,才会被最终删除。