OpenGL ES 3.0编程指南:第四章. Shaders and Programs --- (一)Shaders and Programs

[TOC]

摘要

shader对象和program对象是OpenGL ES中两个基础概念,这一章详细介绍了怎样创建shader,编译shader,将shader和program链接起来。本章将介绍的几个主题:

  • Shader and program object overview
  • Creating and compiling a shader
  • Creating and linking a program
  • Getting and setting uniforms
  • Getting and setting attributes
  • Shader compiler and program binaries

1. Shaders and programs

当使用shader进行渲染时,我们需要创建两个基础对象:shader对象和program对象。一个好的理解方式就是将它们比作一个C编辑器和链接器,C编译器将一段源代码编译成目标代码(例如.obj文件或者.o文件),然后C链接器将目标文件链接到一个最终的程序中。

一个shader对象包含一个单独的shader(文件或者代码块),shader的源代码被赋予给这个shader对象,然后这个shader对象被编译成目标格式(类似一个.obj文件)。编译完成之后,这个shader对象就可以被附加到一个program对象上。一个program对象能附加多个shader对象,但是在OpenGL ES中,每个program对象需要一个顶点shader对象和一个片段shader对象,不能多也不能少,这点和桌面OpenGL不一样。program对象被链接到一个最终的“可执行”(程序中),然后就可以用来进行渲染了。

简要来说,主要包括以下六个步骤:

  1. 创建一个顶点shader对象和一个片段shader对象
  2. 将源码附加到每一个shader对象上
  3. 编译shader对象
  4. 创建一个program对象
  5. 将编译后的shader对象附加到这个program对象上
  6. 链接这个program对象

2. Creating and Compiling a Shader

创建和删除 shader 对象:

GLuint glCreateShader (GLenum type)

  • type :要创建的shader类型,可取值为
    GL_VERTEX_SHADER : 顶点shader
    GL_FRAGMENT_SHADER : 片段shader
  • 函数返回值是一个GLuint,指向新创建的shader对象

void glDeleteShader (GLuint shader)

  • shader : 要删除的shader对象
  • 注意,如果一个shader已经被附加到一个program对象上,那么当调用glCreateShader方法时不会立即删除这个shader对象,而是将这个shader对象标记为删除,当这个shader对象不再被附加到任何一个program对象上时,它的内存才会被释放。

shader对象创建好之后,就可以为这个shader指定源码:

void glShaderSource (GLuint shader, GLsizei count, const GLchar * const * string, const GLint * length)

  • shader : 要指定源码的shader对象
  • count : shader源码字符串的数量。一个shader对象可以由多个源码字符串组成,但是每一个shader只能有一个main方法。
  • string : 一个指向由count个源码字符串组成的数组的指针
  • length : 一个指向由count个整数组成的数组的指针,每一个整数储存着对应源码字符串的长度。(不是很明白,这个length貌似是用来保存字符串长度的,如果不需要保存,那么可以写NULL,书上原文是:pointer to an array of count integers that holds the size of each respective shader string. If length is NULL, the shader strings are assumed to be null terminated. If length is not NULL, then each element of length holds the number of characters in the corresponding shader in the string array. If the value of length for any element is less than zero, then that string is assumed to be null terminated.)

为shader对象指定源码后,就可以编译这个shader对象:

void glCompileShader (GLuint shader)

  • shader : 要进行编译的shader对象

编译完之后,可以查询各个信息:

void glGetShaderiv (GLuint shader, GLenum pname, GLint * params)

  • shader : 要查询的shader对象
  • pname :要查询的参数,可取值为
    GL_COMPILE_STATUS : 查询编译状态,编译成功则返回值为GL_TRUE,编译失败则返回值为GL_FALSE
    GL_DELETE_STATUS : 查询是否已被标记为删除状态
    GL_INFO_LOG_LENGTH : 查询日志长度,包括结尾的空字符
    GL_SHADER_SOURCE_LENGTH : 查询shader源码长度
    GL_SHADER_TYPE : 查询shader类型,返回值为GL_VERTEX_SHADER或者GL_FRAGMENT_SHADER
  • params : 用来保存查询结果的一个GLint指针

可以通过查询GL_INFO_LOG_LENGTH获得日志长度,然后根据日志长度获取日志:

void glGetShaderInfoLog (GLuint shader, GLsizei maxLength, GLsizei * length, GLchar * infoLog)

  • shader : 要查询的shader对象
  • maxLength : 储存日志信息的缓冲区的长度(通过GL_INFO_LOG_LENGTH获得)
  • length : 用来保存日志的实际长度(不包括结尾空字符),如果不需要可以写NULL
  • infoLog : 用来保存日志内容的指针

Example 4-1

GLuint LoadShader (GLenum type, const char * shaderSrc)
{
    GLuint shader;
    GLint compiled;
    
    // Create the shader object
    shader = glCreateShader(type)
    
    if (shader == 0)
    {
        return 0;
    }

    // Load the shader source
    glShaderSource(shader, 1, &shaderSrc, NULL);

    // Compile the shader
    glCompileShader(shader);

    // Check the compile status
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

    if (!compiled)
    {
        GLint infoLen = 0;
        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
    
        if (infoLem > 1)
        {
            char* infoLog = malloc (sizeof(char) * infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog);
            esLogMessage("Error compiling shader:\n%s\n", infoLog);
            free(infoLog);
        }
        
        glDeleteShader(shader);
        return 0;
    }

    return shader;
}

3. Creating and Linking a Program

一个program对象实际上是一个使shader对象附着的容器对象,并且链接着一个最终可执行程序。
创建和删除program对象:

GLuint glCreateProgram ()

  • 返回值是一个GLuint,指向新创建的program对象

void glDeleteProgram (GLuint program)

  • program : 要删除的program对象

program对象创建好之后,就可以将shader对象附着(也可以分开)给这个program对象:

void glAttachShader (GLuint program, GLuint shader)

  • program : program对象
  • shader : shader对象
  • 一个shader可以在任何时候附着给program对象,可以在编译之前附着,也可以在指定源码之前附着。

void glDetachShader (GLuint program, GLuint shader)

  • program : program对象
  • shader : shader对象

然后,就是链接这个program对象:

void glLinkProgram (GLuint program)

  • program : 要链接的program对象

查询各项信息:

void glGetProgramiv (GLuint program, GLenum pname, GLint * params)

  • program : 要查询的program对象
  • pname : 要查询的参数,可取值为
    GL_ACTIVE_ATTRIBUTES : 查询顶点shader中active attribute的数量
    GL_ACTIVE_ATTRIBUTE_MAX_LENGTH : 查询attribute名称的最大长度,可以用来决定申请多少空间来保存attribute名称字符串
    GL_ACTIVE_UNIFORM_BLOCK : 查询 uniform block的数量
    GL_ACTIVE_UNIFORM_BLOCK_MAX_LENGTH : 查询uniform block名字的最大长度
    GL_ACTIVE_UNIFORMS : 查询active uniform的数量
    GL_ACTIVE_UNIFORM_MAX_LENGTH :查询uniform名称的最大长度
    GL_ATTACHED_SHADERS :查询附着在这个program对象上的shader数量
    GL_DELETE_STATUS : 查询program是否已被标记为删除
    GL_INFO_LOG_LENGTH : 查询日志长度
    GL_LINK_STATUS : 查询链接状态
    GL_PROGRAM_BINARY_RETRIEVABLE_HINT : 查询program是否启用了二进制检索提示
    GL_TRANSFORM_FEEDBACK_BUFFER_MODE : 查询变换反馈的缓冲模式是GL_SEPARATE_ATTRIBS还是GL_INTERLEAVED_ATTRIBS
    GL_TRANSFORM_FEEDBACK_VARYINGS : 查询变换反馈模式中要捕获的输出变量的数量
    GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH:查询变换反馈模式中要捕获的输出变量的名字的最大长度
    GL_VALIDATE_STATUS : 查询验证状态
  • params : 用来保存查询结果的一个GLint指针

获取program对象日志,同上文获取shader对象日志类似:

void glGetProgramInfoLog (GLuint program, GLsizei maxLength, GLsizei * length, GLchar * infoLog)

  • program : 要查询的program对象
  • maxLength : 储存日志信息的缓冲区的长度(通过GL_INFO_LOG_LENGTH获得)
  • length : 用来保存日志的实际长度(不包括结尾空字符),如果不需要可以写NULL
  • infoLog : 用来保存日志内容的指针

在链接program对象成功完成之后,可以验证这个program对象,因为可能有其他方面的错误导致无法渲染图像。但要注意的是,验证program对象是一个缓慢的过程,只适合用于debug,如果可以成功渲染,完全可以不使用这个:

void glValidateProgram (GLuint program)

  • program : 要验证的program对象

最后,激活这个program:

void glUseProgram (GLuint program)

  • program : 要使用的program对象

Example 4-2

// Create the program object
programObject = glCreateProgram();

if (programObject == 0)
{
    return 0;
}

glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);

// Link the program
glLinkProgram(programObject);

// Check the link status
glGetProgramiv(programObject, GL_LINK_STATUS, &linked);

if (!linked)
{
    GLint infoLen = 0;
    glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen);

    if (infoLen > 1) 
    {
        char * infoLog = malloc(sizeof(char) * infoLen)
        glGetProgramInfoLog(programObject, infoLen, NULL, infoLog);
        esLogMessage("Error linking program:\n%s\n", infoLog);
        free(infoLog);
    }

    glDeleteProgram(programObject);
    return FALSE;
}

// ...

// Use the program object
glUseProgram(programObject);

你可能感兴趣的:(OpenGL ES 3.0编程指南:第四章. Shaders and Programs --- (一)Shaders and Programs)