GPUImage阅读笔记-GLProgram

GLProgram 其实是对OpenGL ES的program进行了一次封装

其工作流程主要如下
创建Program(程序)
创建顶点着色器和片元着色器 (很多滤镜效果都是由这2个着色器来实现)
链接 Program
检查 Program是否可以使用
使用Program

image.png
image.png
image.png

初始化方法

- (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);
}

你可能感兴趣的:(GPUImage阅读笔记-GLProgram)