GPUImage源码解读(二)

这篇文章主要是阅读GPUImage框架中的 GLProgram、GPUImageContext 两个重要类的源码。这两个类是 GPUImage 框架的基础,里面涉及的知识也有 OpenGL ES 基础 和 多线程 基础。以下是源码内容:

GLProgram

一 GLProgram

GLProgram专门处理OpenGL ES程序的创建等相关工作。

  • 初始化方法,可以根据需要传入顶点着色器的路径或字符串以及片源着色器的路径及字符串进行初始化。

- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderString:(NSString *)fShaderString;
- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderFilename:(NSString *)fShaderFilename;
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename
fragmentShaderFilename:(NSString *)fShaderFilename;

初始化的过程包含了顶点片源着色器的创建、编译,着色器程序的创建,顶点片源着色器附着到着色器程序等过程。以文件初始化方式最后都会调用 initWithVertexShaderString:fragmentShaderString: 字符串的初始化方式,

- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderString:(NSString *)fShaderString;
{
if ((self = [super init]))
{
_initialized = NO;
// 初始化属性数组
attributes = [[NSMutableArray alloc] init];
// 初始化uniform属性数组
uniforms = [[NSMutableArray alloc] init];
// 创建着色器程序
program = glCreateProgram();
// 编译顶点着色器
if (![self compileShader:&vertShader
type:GL_VERTEX_SHADER
string:vShaderString])
{
NSLog(@"Failed to compile vertex shader");
}
// 编译片源着色器
// Create and compile fragment 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;
}
  • 在给着色器传值的时候,我们需要获取相关属性的位置。GLProgram也提供了相关接口,方便我们获取变量的位置。.h 中定义如下:

//着色器变量的保存和读取
- (void)addAttribute:(NSString *)attributeName;
- (GLuint)attributeIndex:(NSString *)attributeName;
- (GLuint)uniformIndex:(NSString *)uniformName;

具体实现:

- (void)addAttribute:(NSString *)attributeName
{
// 先判断当前的属性是否已存在
if (![attributes containsObject:attributeName])
{
// 如果不存在先加入属性数组,然后绑定该属性的位置为在属性数组中的位置
[attributes addObject:attributeName];
glBindAttribLocation(program,
(GLuint)[attributes indexOfObject:attributeName],
[attributeName UTF8String]);
}
}
// END:addattribute
// START:indexmethods
- (GLuint)attributeIndex:(NSString *)attributeName
{
// 获取着色器属性变量的位置,即在数组的位置(根据之前的绑定关系)
return (GLuint)[attributes indexOfObject:attributeName];
}
- (GLuint)uniformIndex:(NSString *)uniformName
{
// 获取Uniform变量的位置
return glGetUniformLocation(program, [uniformName UTF8String]);
}
  • 链接程序,和编译型语言一样,OpenGL程序也需要链接。

//连接 program
- (BOOL)link;
- (BOOL)link
{
// CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
GLint status;
// 链接着色器程序
glLinkProgram(program);
// 获取链接状态
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
return NO;
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;
}
  • 使用着色器程序

//使用 program
- (void)use;
- (void)use
{
glUseProgram(program);
}
  • log 信息 都会调用内部方法 logForOpenGLObject, 在连接阶段使用glGetProgramInfoLog获取连接错误

//取到log string
- (NSString *)vertexShaderLog;
- (NSString *)fragmentShaderLog;
- (NSString *)programLog;
// START:privatelog 打印 LOG
- (NSString *)logForOpenGLObject:(GLuint)object
infoCallback:(GLInfoFunction)infoFunc
logFunc:(GLLogFunction)logFunc
{
GLint logLength = 0, charsWritten = 0;
infoFunc(object, GL_INFO_LOG_LENGTH, &logLength);
if (logLength < 1)
return nil;
char *logBytes = malloc(logLength);
logFunc(object, logLength, &charsWritten, logBytes);
NSString *log = [[NSString alloc] initWithBytes:logBytes
length:logLength
encoding:NSUTF8StringEncoding];
free(logBytes);
return log;
}
  • 释放资源。在析构的时候,将着色器等相关资源清理。

- (void)dealloc
{
if (vertShader)
glDeleteShader(vertShader);
if (fragShader)
glDeleteShader(fragShader);
if (program)
glDeleteProgram(program);
}