我的OpenGL学习进阶之旅着色器和程序(下)------ 程序对象

Posted 欧阳鹏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的OpenGL学习进阶之旅着色器和程序(下)------ 程序对象相关的知识,希望对你有一定的参考价值。

在 博客 【我的OpenGL学习进阶之旅】着色器和程序(上)------着色器 中我们已经展示了如何创建着色器对象,下一步是创建一个程序对象。

如前所述,程序对象是一个容器对象,可以将着色器与之连接,并链接一个最终的可执行程序。

操作程序对象的函数调用类似于着色器对象。

1.1 创建一个程序对象

可以使用 glCreateProgram 创建一个程序对象。

GLuint glCreateProgram (void);


你可能已经注意到, glCreateProgram没有任何参数;它简单地返回一个指向新程序对象的句柄。

1.2 删除一个程序对象

使用glDeleteProgram 可以删除一个程序对象。

void glDeleteProgram (GLuint program);

参数program : 指向 需要删除的程序对象的句柄。

1.3 连接着色器和程序

一旦创建了程序对象,下一步就是就是将着色器与之连接。在OpenGL ES 3.0 中,每个程序对象必须连接一个顶点着色器和片段着色器。可以使用glAttachShader 连接着色器和程序。

void glAttachShader (GLuint program, GLuint shader);

参数说明:

  • program
    指向程序对象的句柄
  • shader
    指向程序连接的着色器对象的句柄

这个函数将着色器连接到指定的程序。 注意,着色器可以在任何时候连接------ 在连接到程序之前不一定需要编译,甚至可以没有源代码。唯一要求是,每个程序对象必须有且只有一个顶点着色去和片段着色器与之连接。

1.4 断开着色器的连接

除了连接着色器之外,你还可以用glDetachShader 断开着色器的连接。

void glDetachShader (GLuint program, GLuint shader);


参数说明:

  • program
    指向程序对象的句柄
  • shader
    指向程序断开连接的着色器对象的句柄

1.5 链接程序

一旦连接了着色器(并且着色器已经成功编译),我们就最终为链接着色器做好了准备。

程序对象的链接用 glLinkProgram完成。

void glLinkProgram (GLuint program);
  • program
    指向程序对象的句柄


链接操作负责生成最终的可执行程序。链接程序将检查各种对象的数量,确保成功链接。

  • 链接程序将确保顶点着色器写入片段着色器使用的所有顶点着色器输出变量(并用相同的类型声明)
  • 链接程序还讲确保任何在顶点和片段着色器中都声明的统一变量和统一变量缓冲区的类型相符。
  • 链接程序将确保最终的程序符合具体实现的限制(例如,属性、统一变量或者输入输出着色器变量的数量)

一般来说,链接阶段是生成在硬件上运行的最终硬件指令的时候。

1.6 检查状态

链接程序之后,你必须检查链接是否成功,可以使用glGetProgramiv检查链接状态。

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

参数说明:

  • program
    需要获取信息的程序对象句柄
  • pname
    获取信息的参数,可以是:
    • GL_ACTIVE_ATTRIBUTES
    • GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
    • GL_ACTIVE_UNIFORMS
    • GL_ACTIVE_UNIFORM_MAX_LENGTH
    • GL_ACTIVE_UNIFORM_BLOCKS
    • GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH
    • GL_ATTACHED_SHADERS
    • GL_DELETE_STATUS
    • GL_INFO_LOG_LENGTH
    • GL_LINK_STATUS
    • GL_PROGRAM_BINARY_RETRIEVABLE_HINT
    • GL_TRANSFORM_FEEDBACK_BUFFER_MODE
    • GL_TRANSFORM_FEEDBACK_VARYINGS
    • GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH
    • GL_VALIDATE_STATUS
  • params
    指向查询结果整数存储位置的指针

1.6.1 查询GL_LINK_STATUS

要检查链接是否成功,可以查询 GL_LINK_STATUS

1.6.2 查询GL_ACTIVE_ATTRIBUTES

查询GL_ACTIVE_ATTRIBUTES返回顶点着色器中活动属性的数量。

1.6.3 查询GL_ACTIVE_ATTRIBUTE_MAX_LENGTH

查询GL_ACTIVE_ATTRIBUTE_MAX_LENGTH返回最大属性名称的最大长度(以字符数表示);这一信息可以用于确定存储属性名字符串所需的内存量。

1.6.4 查询GL_ACTIVE_UNIFORMS

查询GL_ACTIVE_UNIFORMS返回活动统一变量的数量

1.6.5 查询GL_ACTIVE_UNIFORM_MAX_LENGTH

查询GL_ACTIVE_UNIFORM_MAX_LENGTH返回最大统一变量名称的最大长度。

1.6.6 查询GL_ATTACHED_SHADERS

查询GL_ATTACHED_SHADERS返回连接到程序对象的着色器数量。

1.6.7 查询GL_DELETE_STATUS

查询GL_DELETE_STATUS返回程序对象是否已经标记为删除。

1.6.8 查询GL_INFO_LOG_LENGTH

查询GL_INFO_LOG_LENGTH返回程序对象存储的信息日志的长度。

1.6.9 查询GL_TRANSFORM_FEEDBACK_BUFFER_MODE

查询GL_TRANSFORM_FEEDBACK_BUFFER_MODE返回GL_SEPARATE_ATTRIBS或者GL_INTERLEAVED_ATTRIBS,表示变化反馈启用时的缓冲区模式。

1.6.10 查询GL_TRANSFORM_FEEDBACK_VARYINGS

查询GL_TRANSFORM_FEEDBACK_VARYINGS返回程序的变化反馈模式中捕捉的输出变量数量。

1.6.11 查询GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH

查询GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH返回程序的变化反馈模式中捕捉的输出变量名称的最大长度。

1.6.12 查询GL_ACTIVE_UNIFORM_BLOCKS

查询GL_ACTIVE_UNIFORM_BLOCKS返回 包含活动统一变量的程序中统一变量块数量

1.6.13 查询GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH

查询GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH返回 包含活动统一变量的程序中统一变量块名称的最大长度。

1.6.14 查询 GL_PROGRAM_BINARY_RETRIEVABLE_HINT

查询 GL_PROGRAM_BINARY_RETRIEVABLE_HINT返回一个表示程序目前是否启用二进制检索提示的值。

1.6.15 查询 GL_VALIDATE_STATUS

查询 GL_VALIDATE_STATUS 返回最后一个校验操作的状态。

1.7 获取信息日志

在链接程序之后,我们接下来要从程序的信息日志中获得信息(特别是链接发生问题时)。这和获得着色器对象的信息日志类似。

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


参数说明:

  • program
    指向需要获取信息的程序对象的句柄
  • bufSize
    存储信息日志的缓冲区的大小
  • length
    写入的信息日志长度(减去null终止符);如果不需要知道长度,这个参数可以为NULL
  • infoLog
    指向存储信息日志的字符缓冲区的指针

1.8 校验

一旦成功链接程序,我们就疾呼已经为使用它渲染做好了准备。但是,在此之前,我们可能想要检查程序是否有效。也就是说,成功链接不能保证执行的某些方面。例如,应用程序可能没有把有效的纹理绑定到采样器。这种行为在链接的时候无从得知,但是在绘图的时候很明显。

为了检查程序能以当前状态执行,可以调用glValidateProgram

void glValidateProgram (GLuint program);

参数program: 需要校验的程序对象的句柄

校验的结果可以用前面介绍的GL_VALIDATE_STATUS检查,信息日志也将更新。

注意: 你实际上执行glValidateProgram 用于调试的目的。这是一个速度很慢的操作,不是在每次渲染之前都想执行的检查。 实际上,如果应用程序成功渲染,你可以完全不使用它。 但是,我们希望你意识到这个函数的存在!

1.9 设置为活动程序

目前,我们已经介绍了创建程序对象、连接着色器、链接以及获取信息日志所需的函数。

在渲染之前,你还需要对程序对象做一件事情,就是用glUseProgram将其设置为活动程序。

void glUseProgram (GLuint program);

参数program: 设置为活动程序的程序对象句柄

现在我们的程序已经设置为活动,可以开始渲染了!

二、实例:创建程序、连接着色器并链接程序

/**
 * Loads the given source code as a shader of the given type.
 *
 * 负责 加载着色器源代码、编译并检查错误。他返回一个着色器对象
 */
static GLuint loadShader(GLenum shaderType, const char** source) 
	// Create the shader object
	GLuint shader;
	GLint compiled;

	// Create the shader object
	// shaderType 可以是  GL_VERTEX_SHADER  或者  GL_FRAGMENT_SHADER
	shader = glCreateShader(shaderType);
	if (shader == 0) 
		return 0;
	

	// Load the shader source
	glShaderSource(shader, 1, source, nullptr);

	// 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 (infoLen > 1) 
			char* infoLog = (char*)malloc(sizeof(char) * infoLen);
			// 检索信息日志
			glGetShaderInfoLog(shader, infoLen, nullptr, infoLog);
			LOGE("Error compiling shader:\\n%s\\n", infoLog);

			free(infoLog);
		
		// 删除Shader
		glDeleteShader(shader);
		return 0;
	
	return shader;




GLuint GLUtils::createProgram(const char** vertexSource, const char** fragmentSource) 
	// Load the Vertex shader
	GLuint vertexShader = loadShader(GL_VERTEX_SHADER, vertexSource);
	if (vertexShader == 0) 
		return 0;
	
	// Load the Fragment shader
	GLuint fragmentShader = loadShader(GL_FRAGMENT_SHADER, fragmentSource);
	if (fragmentShader == 0) 
		return 0;
	

	// Create the program object
	GLuint program = glCreateProgram();
	if (program == 0) 
		return 0;
	

	// Bind the vertex shader to the program
	glAttachShader(program, vertexShader);

	// Bind the fragment shader to the program.
	glAttachShader(program, fragmentShader);

	// Link the program
	glLinkProgram(program);

	// Check the link status
	GLint linkStatus;
	glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);

	if (!linkStatus) 
		// Retrieve compiler error message when linking fails
		GLint infoLen = 0;
		glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLen);
		if (infoLen > 1) 
			char* infoLog = (char*)malloc(sizeof(char) * infoLen);
			//获取信息
			glGetProgramInfoLog(program, infoLen, nullptr, infoLog);
			LOGE("Error linking program:\\n%s\\n", infoLog);

			free(infoLog);
		
		// 删除程序对象
		glDeleteProgram(program);
		return 0;
	
	// Free up no longer needed shader resources
	glDeleteShader(vertexShader);
	glDeleteShader(fragmentShader);
	return program;




// ...  使用程序对象

// Use the program object
mProgram = GLUtils::createProgram(&VERTEX_SHADER, &FRAGMENT_SHADER);
glUseProgram(mProgram );

以上是关于我的OpenGL学习进阶之旅着色器和程序(下)------ 程序对象的主要内容,如果未能解决你的问题,请参考以下文章

我的OpenGL学习进阶之旅着色器和程序(上)------着色器

我的OpenGL学习进阶之旅着色器和程序(上)------着色器

我的OpenGL学习进阶之旅如何抽取着色器代码到assets目录下的GLSL文件,以及如何通过Java或者C++代码来加载着GLSL文件?

我的OpenGL学习进阶之旅OpenGL ES 着色语言 (下)

我的OpenGL学习进阶之旅OpenGL ES 着色语言 (下)

我的OpenGL学习进阶之旅着色器编译器和程序二进制码