• 【GLSL教程】(二)在OpenGL中使用GLSL 【转】


    http://blog.csdn.net/racehorse/article/details/6616256

    设置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库:

    http://glew.sourceforge.net/

    下面的代码检查OpenGL 2.0是否可用:

    1. #include <GL/glew.h>  
    2. #include <GL/glut.h>  
    3.   
    4. void main(int argc, char **argv)  
    5. {  
    6.     glutInit(&argc, argv);  
    7.     ...  
    8.     glewInit();  
    9.   
    10.     if (glewIsSupported("GL_VERSION_2_0"))  
    11.         printf("Ready for OpenGL 2.0 ");  
    12.     else  
    13.     {  
    14.         printf("OpenGL 2.0 not supported ");  
    15.         exit(1);  
    16.     }  
    17.     setShaders();  
    18.   
    19.     glutMainLoop();  
    20. }  

    下图显示了创建shader的必要步骤,函数的具体使用方法将在下面各小结描述:

    创建shader

    下图显示了创建shader的步骤:

    首先创建一个对象作为shader的容器,这个创建函数将返回容器的句柄。

    1. GLuint glCreateShader(GLenum shaderType);  
    2. 参数:  
    3. ·shaderType – GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.  

    你可以创建许多shader,但记住所有的顶点shader只能有一个main函数,所有像素shader也一样。

    下一步将添加源代码。shader的源代码是一个字符串数组,添加的语法如下:

    1. void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lenOfStrings);  
    2. 参数:  
    3. ·shader – the handler to the shader.  
    4. ·numOfStrings – the number of strings in the array.  
    5. ·strings – the array of strings.  
    6. ·lenOfStrings – an array with the length of each string, or NULL, meaning that the strings are NULL terminated.  

    最后编译shader:

    1. void glCompileShader(GLuint shader);  
    2. 参数:  
    3. •shader – the handler to the shader.  

    创建程序

    下图显示了获得一个可以运行的shader程序的步骤:

    首先创建一个对象,作为程序的容器。此函数返回容器的句柄。

    1. GLuint glCreateProgram(void);  

    你可以创建任意多个程序,在渲染时,可以在不同程序中切换,甚至在某帧返回固定功能流水线。比如你想用折射和反射shader绘制一个茶杯,然后回到固定功能生成立方体环境贴图(cube map)显示背景。

    下面将把上一节编译的shader附加到刚刚创建的程序中。方法如下:

    1. void glAttachShader(GLuint program, GLuint shader);  
    2. 参数:  
    3. ·program – the handler to the program.  
    4. ·shader – the handler to the shader you want to attach.  

    如果同时有顶点shader和片断shader,你需要把它们都附加到程序中。你可以把多个相同类型(顶点或像素)的shader附加到一个程序中,如同一个C程序可以有多个模块一样,但它们只能有一个main函数。

    你也可以把一个shader附加到多个程序,比如你想在不同程序中使用某个相同的shader。

    最后一步是连接程序。方法如下:

    1. void glLinkProgram(GLuint program);  
    2. 参数:  
    3. ·program – the handler to the program.  

    在连接操作之后,shader的源代码可以被修改并重编译,并不会影响到整个程序。

    程序连接后,可以调用glUseProgram来使用程序。每个程序都分配了一个句柄,你可以事先连接多个程序以备使用。

    1. void glUseProgram(GLuint prog);  
    2. 参数:  
    3. ·prog – the handler to the program you want to use, or zero to return to fixed functionality.  

    当一个程序被使用后,如果被再次连接,它将被自动替换并投入使用,所以没有必要再次调用上面这个函数。如果使用的参数为0,表示将使用固定功能流水线。

     例子

    下面的代码包含了上面描述的所有步骤,参数p,f,v是全局的GLuint型变量。

    1. void setShaders()  
    2. {  
    3.     char *vs,*fs;  
    4.   
    5.     v = glCreateShader(GL_VERTEX_SHADER);  
    6.     f = glCreateShader(GL_FRAGMENT_SHADER);    
    7.   
    8.     vs = textFileRead("toon.vert");  
    9.     fs = textFileRead("toon.frag");  
    10.   
    11.     const char *vv = vs;  
    12.     const char *ff = fs;  
    13.   
    14.     glShaderSource(v, 1, &vv, NULL);  
    15.     glShaderSource(f, 1, &ff, NULL);  
    16.   
    17.     free(vs);free(fs);  
    18.   
    19.     glCompileShader(v);  
    20.     glCompileShader(f);  
    21.   
    22.     p = glCreateProgram();  
    23.   
    24.     glAttachShader(p, v);  
    25.     glAttachShader(p, f);  
    26.   
    27.     glLinkProgram(p);  
    28.     glUseProgram(p);  
    29. }  

    GLUT版的完整例子如下:

    http://lighthouse3d.com/wptest/wp-content/uploads/2011/03/glutglsl_2.0.zip

    完整例子中包含了shader代码及文本文件读入程序。

    错误处理

    调试shader是很困难的。目前还没有像printf这样的东西,虽然未来可能出现有调试功能的开发工具。

    编译阶段的状态可以用如下函数获得:

    1. void glGetShaderiv(GLuint object, GLenum type, int *param);  
    2. 参数:  
    3. ·object – the handler to the object. Either a shader or a program  
    4. ·type – GL_COMPILE_STATUS.  
    5. ·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.  

    连接阶段的状态可以用如下函数获得:

    1. void glGetProgramiv(GLuint object, GLenum type, int *param);  
    2. 参数:  
    3. ·object – the handler to the object. Either a shader or a program  
    4. ·type – GL_LINK_STATUS.  
    5. ·param – the return value, GL_TRUE if OK, GL_FALSE otherwise.  

    如 果发生错误,就需要从InfoLog中找到更多的信息。这个日志保存了最后一次操作的信息,比如编译时的警告、错误,连接时发生的各种问题。这个日志甚至 可以告诉你硬件是否支持你的shader。不幸的是InfoLog没有一个规范,所以不同的驱动/硬件可能产生不同的日志信息。

    为了获得特定shader或程序的日志,可以使用如下程序:

    1. void glGetShaderInfoLog(GLuint object, int maxLen, int *len, char *log);  
    2. void glGetProgramInfoLog(GLuint object, int maxLen, int *len, char *log);  
    3. 参数:  
    4. ·object – the handler to the object. Either a shader or a program  
    5. ·maxLen – The maximum number of chars to retrieve from the InfoLog.  
    6. ·len – returns the actual length of the retrieved InfoLog.  
    7. ·log – The log itself.  

    GLSL规范有必要在这里进行一些改进:你必须知道接收InfoLog的长度。为了找到这个准确的值,使用下面的函数:

    1. void glGetShaderiv(GLuint object, GLenum type, int *param);  
    2. void glGetProgramiv(GLuint object, GLenum type, int *param);  
    3. 参数:  
    4. ·object – the handler to the object. Either a shader or a program  
    5. ·type – GL_INFO_LOG_LENGTH.  
    6. ·param – the return value, the length of the InfoLog.  

    下面的函数可以用来打印InfoLog的内容:

    1. void printShaderInfoLog(GLuint obj)  
    2. {  
    3.     int infologLength = 0;  
    4.     int charsWritten  = 0;  
    5.     char *infoLog;  
    6.    
    7.     glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);  
    8.    
    9.     if (infologLength > 0)  
    10.     {  
    11.         infoLog = (char *)malloc(infologLength);  
    12.         glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);  
    13.         printf("%s ",infoLog);  
    14.         free(infoLog);  
    15.     }  
    16. }   
    17.   
    18. void printProgramInfoLog(GLuint obj)  
    19. {  
    20.     int infologLength = 0;  
    21.     int charsWritten  = 0;  
    22.     char *infoLog;  
    23.    
    24.     glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);  
    25.    
    26.     if (infologLength > 0)  
    27.     {  
    28.         infoLog = (char *)malloc(infologLength);  
    29.         glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);  
    30.         printf("%s ",infoLog);  
    31.         free(infoLog);  
    32.     }  
    33. }  

    清理

    前面的小节讲到了附加一个shader到一个程序中,这里的调用是将shader从程序中分离:

    1. void glDetachShader(GLuint program, GLuint shader);  
    2. 参数:  
    3. ·program – The program to detach from.  
    4. ·shader – The shader to detach.  

    注意,只有没有附加到任何程序的shader可以被删除,删除shader和程序的调用如下:

    1. void glDeleteShader(GLuint id);  
    2. void glDeleteProgram(GLuint id);  
    3. 参数:  
    4. ·id – The hanuler of the shader or program to delete.  

    如果一个shader还附加在某个程序中,这个shader并不能真正删除,只能标记为删除。当这个shader从所有程序中分离之后,才会被最终删除。

  • 相关阅读:
    GeoServer发布Shapfile、PostGIS数据
    Geoserver+Openlayers+MySQL设计思想,GeoServer服务器搭建(Docker构建镜像)
    Linux 输入输出重定向, &>file, 2>&1, 1>&2
    Git常用命令
    Java 操作 HBase 教程
    NATAPP--实现SSH内网穿透
    Spring boot banner
    ingress 密码验证
    rook 记录
    容器网络之网桥
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/5580916.html
Copyright © 2020-2023  润新知