GLProgram 其实是对OpenGL ES的program进行了一次封装
其工作流程主要如下
创建Program(程序)
创建顶点着色器和片元着色器 (很多滤镜效果都是由这2个着色器来实现)
链接 Program
检查 Program是否可以使用
使用Program
初始化方法
- (id)initWithVertexShaderString:(NSString *)vShaderString
fragmentShaderString:(NSString *)fShaderString;
{
if ((self = [super init]))
{
//记录program是否初始化完成
_initialized = NO;
//使用NSMutableArray存储了着色器代码中的attributes 和 uniforms变量
attributes = [[NSMutableArray alloc] init];
uniforms = [[NSMutableArray alloc] init];
//创建程序
program = glCreateProgram();
// 这个方法是传入 着色器类型,着色器名字,着色器的标识,然后返回一个着色器 -- 这里是顶点着色器
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");
}
//将着色器添加到program上
glAttachShader(program, vertShader);
glAttachShader(program, fragShader);
}
return self;
}
链接程序
- (BOOL)link
{
//CFAbsoluteTime 是用来计算时间的
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
GLint status;
// 链接
glLinkProgram(program);
// 获取program连接状态
glGetProgramiv(program, GL_LINK_STATUS, &status);
// 如果链接失败则返回NO
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)validate;
{
GLint logLength;
glValidateProgram(program);
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0)
{
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(program, logLength, &logLength, log);
self.programLog = [NSString stringWithFormat:@"%s", log];
free(log);
}
}
根据属性名字 获取attributes或uniformIndex的标识
- (GLuint)attributeIndex:(NSString *)attributeName
{
return (GLuint)[attributes indexOfObject:attributeName];
}
- (GLuint)uniformIndex:(NSString *)uniformName
{
return glGetUniformLocation(program, [uniformName UTF8String]);
}
Attribute管理
在GPUImage中,实际上attribute的index并不是真正从program中获取到的,而是直接通过默认的顺序进行排序的。因为我们可以真正通过使用glBindAttribLocation和glEnableVertexAttribArray来控制每个Attribute的位置。
因此,如果需要写一个Filter的子类,需要先调用父类中的addAttributes方法,才能够保证正确的Attribute index。
- (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];
}
dealloc
#pragma mark -
// START:dealloc
- (void)dealloc
{
//释放顶点着色器
if (vertShader)
glDeleteShader(vertShader);
//释放片元着色器
if (fragShader)
glDeleteShader(fragShader);
//释放程序着色器
if (program)
glDeleteProgram(program);
}
另外
OpenGL ES 的三种变量类型(uniform,attribute和varying)
1.uniform变量
uniform变量是外部application程序传递给(vertex和fragment)shader的变量。因此它是application通过函数glUniform**()函数赋值的。在(vertex和fragment)shader程序内部,uniform变量就像是C语言里面的常量(const ),它不能被shader程序修改。(shader只能用,不能改)
如果uniform变量在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用。(相当于一个被vertex和fragment shader共享的全局变量)
uniform变量一般用来表示:变换矩阵,材质,光照参数和颜色等信息。
以下是例子:
uniform mat4 viewProjMatrix; //投影+视图矩阵
uniform mat4 viewMatrix; //视图矩阵
uniform vec3 lightPosition; //光源位置
2.attribute变量
attribute变量是只能在vertex shader中使用的变量。(它不能在fragment shader中声明attribute变量,也不能被fragment shader中使用)
一般用attribute变量来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。
在application中,一般用函数glBindAttribLocation()来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer()为每个attribute变量赋值。
以下是例子:
uniform mat4 u_matViewProjection;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord;
void main(void)
{
gl_Position = u_matViewProjection * a_position;
v_texCoord = a_texCoord0;
}
3.varying变量
varying变量是vertex和fragment shader之间做数据传递用的。一般vertex shader修改varying变量的值,然后fragment shader使用该varying变量的值。因此varying变量在vertex和fragment shader二者之间的声明必须是一致的。application不能使用此变量。
以下是例子:
// Vertex shader
uniform mat4 u_matViewProjection;
attribute vec4 a_position;
attribute vec2 a_texCoord0;
varying vec2 v_texCoord; // Varying in vertex shader
void main(void)
{
gl_Position = u_matViewProjection * a_position;
v_texCoord = a_texCoord0;
}
// Fragment shader
precision mediump float;
varying vec2 v_texCoord; // Varying in fragment shader
uniform sampler2D s_baseMap;
uniform sampler2D s_lightMap;
void main()
{
vec4 baseColor;
vec4 lightColor;
baseColor = texture2D(s_baseMap, v_texCoord);
lightColor = texture2D(s_lightMap, v_texCoord);
gl_FragColor = baseColor * (lightColor + 0.25);
}