GLProgram
GLProgram的实现相对简单,没有与其他类产生依赖关系,主要封装了着色器与着色器程序的创建、编译、链接、使用和销毁。
GPUImage中负责创建GLProgram的只有GLImageContext,其他类型需要用到GLProgram的时候,会通过GLImageContext来获取。
关于着色器的介绍,可以参考LearnOpenGL-着色器
下面简单介绍一下GPUImage的方法:
- 初始化方法,根据需要传入顶点着色器和片源着色器的路径或存放在NSBundle的文件名进行初始化。
- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderString:(NSString *)fShaderString;
- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderFilename:(NSString *)fShaderFilename;
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
fragmentShaderFilename:(NSString *)fShaderFilename;
初始化的过程包含了顶点着色器和片段着色器的创建、编译,着色器程序的创建并绑定着色器。
// 其他的初始化方法都会调用这个初始化方法
- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderString:(NSString *)fShaderString;
{
if ((self = [super init]))
{
_initialized = NO;
attributes = [[NSMutableArray alloc] init]; // 局部变量列表
uniforms = [[NSMutableArray alloc] init]; // 全局变量列表
program = glCreateProgram(); // 创建Program
// 编译顶点着色器
if (![self compileShader:&vertShader
type:GL_VERTEX_SHADER
string:vShaderString])
{
NSLog(@"Failed to compile vertex shader");
}
// 编译片段着色器
if (![self compileShader:&fragShader
type:GL_FRAGMENT_SHADER
string:fShaderString])
{
NSLog(@"Failed to compile fragment shader");
}
// 绑定着色器到着色器程序
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
}
return self;
}
着色器编译过程,主要包括着色器创建并绑定源代码、编译和输出错误日志。
- (BOOL)compileShader:(GLuint *)shader
type:(GLenum)type
string:(NSString *)shaderString
{
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
GLint status;
const GLchar *source;
// 获取着色器代码
source =
(GLchar *)[shaderString UTF8String];
if (!source)
{
NSLog(@"Failed to load vertex shader");
return NO;
}
*shader = glCreateShader(type); // 创建指定类型的着色器
glShaderSource(*shader, 1, &source, NULL); // 绑定着色器代码到着色器
glCompileShader(*shader); // 编译着色器
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); // 获取编译状态
// 判断着色器编译是否成功,失败则输出日志
if (status != GL_TRUE)
{
GLint logLength;
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); // 获取编译日志
if (logLength > 0)
{
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(*shader, logLength, &logLength, log); // 获取日志信息
if (shader == &vertShader)
{
self.vertexShaderLog = [NSString stringWithFormat:@"%s", log];
}
else
{
self.fragmentShaderLog = [NSString stringWithFormat:@"%s", log];
}
free(log);
}
}
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
// NSLog(@"Compiled in %f ms", linkTime * 1000.0);
return status == GL_TRUE;
}
- 链接着色器程序
- (BOOL)link
{
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
GLint status;
// 链接着色器程序
glLinkProgram(program);
// 获取链接状态
glGetProgramiv(program, GL_LINK_STATUS, &status);
// 判断链接是否成功,失败则返回
if (status == GL_FALSE)
return NO;
// 链接成功,就可以删掉相关的shader,释放资源
if (vertShader)
{
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader)
{
glDeleteShader(fragShader);
fragShader = 0;
}
// 设置初始化成功标识
self.initialized = YES;
// CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
// NSLog(@"Linked in %f ms", linkTime * 1000.0);
return YES;
}
- 使用着色器程序
- (void)use
{
glUseProgram(program);
}
- 获取相关属性的索引,用于为着色器传值。
- (void)addAttribute:(NSString *)attributeName
{
// 判断当前的属性是否已存在
if (![attributes containsObject:attributeName])
{
// 如果不存在先加入属性数组,然后绑定该属性的索引为在属性数组中的索引
[attributes addObject:attributeName];
glBindAttribLocation(program,
(GLuint)[attributes indexOfObject:attributeName],
[attributeName UTF8String]);
}
}
// 获取属性的索引
- (GLuint)attributeIndex:(NSString *)attributeName
{
return (GLuint)[attributes indexOfObject:attributeName];
}
// 获取uniform的索引
- (GLuint)uniformIndex:(NSString *)uniformName
{
return glGetUniformLocation(program, [uniformName UTF8String]);
}