GPUImage渲染流程

GPUImage 的代码结构是链式处理结构. 所以查看的时候也是从最开始选图片一步一步向下走的.

步骤:

1.从相册选择一张图片 把几个参数传给修改图像效果的控制器.
2.使用刚才传过来的图片 创建GPUImagePicture对象.
3.创建filter对象 GPUImageXXXFilter 创建哪种filter类型, 由前面传过来的判断.
4.创建GPUImageFilterPipeline.

/**
 创建GPUImageFilterPipeline

 @param filters 传创建的filter对象
 @param input staticPicture就是上面创建的GPUImagePicture对象
 @param output 传GPUImageView类型的view
 @return 返回GPUImageFilterPipeline对象
 */
- (id) initWithOrderedFilters:(NSArray*) filters input:(GPUImageOutput*)input output:(id )output;

5.设置不同种类的filter,修改它的属性大小
5.1例如 [(GPUImageExposureFilter *)filter setExposure:[(UISlider *)sender value]]; 是修改曝光度的.
5.1.1设置filter属性的时候在setter方法中调用方法:

- (void)setFloat:(GLfloat)floatValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;

传的每个参数的值和含义:
floatValue: 设置的曝光度的值.
uniform: 传的是[filterProgram uniformIndex:@"exposure"];这个方法的返回值. 其中filterProgram是GLProgram类型的一个对象.
shaderProgram:就传filterProgram.
这个方法是其父类GPUImageFilter中的实现为:

- (void)setFloat:(GLfloat)floatValue forUniform:(GLint)uniform program:(GLProgram *)shaderProgram;
{
    dispatch_async([GPUImageOpenGLESContext sharedOpenGLESQueue], ^{
        [GPUImageOpenGLESContext setActiveShaderProgram:shaderProgram];
        glUniform1f(uniform, floatValue);
    });
}

6.渲染图像. 调用GPUImagePicture对象的processImage方法 和 useNextFrameForImageCapture方法 (此方法是GPUImageOutput下也就是单个滤镜). 去渲染图像. 实现不同的程度的各种效果. GPUImagePicture继承自GPUImageOutput.
GPUImageOutput类型的对象filter调用方法[filter addTarget:filterView]; 添加的target是GPUImageView类型的对象
渲染图片的两个方法:
6.1例如 [staticPicture processImage]; 其中staticPicture是GPUImagePicture类型的一个对象. 方法的实现为:

- (void)processImage;
{
    hasProcessedImage = YES;
  
    dispatch_async([GPUImageOpenGLESContext sharedOpenGLESQueue], ^{
        for (id currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
            
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }    
    });    
}

解析方法的实现:

    dispatch_async([GPUImageOpenGLESContext sharedOpenGLESQueue], ^{
        for (id currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
            
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }    
    }); 

是一个GCD异步函数, 第一个参数一个队列. 第二个是block块实现方法.

        for (id currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];
            
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }

targets是GPUImagePicture父类(GPUImageOutput)的一个成员变量, 并且遵循GPUImageOpenGLESContext类中的GPUImageInput协议. forin循环中的currentTarget调用的两个方法, 就是GPUImageOpenGLESContext类中的GPUImageInput协议中的两个方法.GPUImageInput协议主要包含一些输入需要渲染目标的操作.

@protocol GPUImageInput
//说明:准备下一个要使用的新帧
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
- (void)setInputTexture:(GLuint)newInputTexture atIndex:(NSInteger)textureIndex;
//说明:下一个有效的纹理索引
- (NSInteger)nextAvailableTextureIndex;
//设置需要渲染目标的尺寸
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;
//说明:设置旋转模式
- (void)setInputRotation:(GPUImageRotationMode)newInputRotation atIndex:(NSInteger)textureIndex;
//说明:输出缓冲区的最大尺寸
- (CGSize)maximumOutputSize;
//说明:输入处理结束
- (void)endProcessing;
//说明:是否应该忽略渲染目标的更新
- (BOOL)shouldIgnoreUpdatesToThisTarget;
//说明:是否启用渲染目标
- (BOOL)enabled;
@end

绘制
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;方法是源图像已经准备好,开始绘制。
在滑动滑块, 调节曝光度的时候, GPUImageFilter和GPUImageView会不断调用GPUImageInput协议的方法.

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
- (void)setInputSize:(CGSize)newSize atIndex:(NSInteger)textureIndex;

然后开始分析上面的两个方法:
GPUImageFilter中方法的实现为:

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
    static const GLfloat imageVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f,  1.0f,
        1.0f,  1.0f,
    };  //顶点数据,两个三角形组成texture区域
    //然后filter调用自己的两个方法函数
//进行GL绘制
    [self renderToTextureWithVertices:imageVertices textureCoordinates:[[self class] textureCoordinatesForRotation:inputRotation] sourceTexture:filterSourceTexture];
//绘制完成通知所有的target处理
    [self informTargetsAboutNewFrameAtTime:frameTime];
}

/**
激活该 filter 中的 filterProgram(已经 attach 过 顶点 shader 和 片元 shader),然后绑定输入的 texture 并渲染。

@param vertices imageVertices
@param textureCoordinates [[self class] textureCoordinatesForRotation:inputRotation] 一个类方法, 纹理顶点坐标 根据不同的方向设置不同的值
@param sourceTexture filterSourceTexture
*/
方法的具体实现如下:

- (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates:(const GLfloat *)textureCoordinates sourceTexture:(GLuint)sourceTexture;
{
    if (self.preventRendering)
    {
        return;
    }
    
    [GPUImageOpenGLESContext setActiveShaderProgram:filterProgram];
    [self setFilterFBO];
    
    glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
    glClear(GL_COLOR_BUFFER_BIT);

    glActiveTexture(GL_TEXTURE2);//选择GL_TEXTURE2
    glBindTexture(GL_TEXTURE_2D, sourceTexture);//绑定当前输入的framebuffer中的texture
    
    glUniform1i(filterInputTextureUniform, 2);  
  //分别设置顶点shader中的顶点数据,和将来用于片元shader中的texture坐标数据
    glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
    glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

glActiveTexture 是选择一个纹理单元。先选择纹理单元2,然后把源图像数据绑定到GL_TEXTURE_2D的位置上。最后告诉片元着色器,纹理单元是2。

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, sourceTexture);
    
    glUniform1i(filterInputTextureUniform, 2);

这两行是分别绑定顶点坐标数据和纹理坐标数据。

    glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, vertices);
    glVertexAttribPointer(filterTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates);

在准备好着色器、纹理data、顶点位置坐标和纹理坐标后,就可以调用

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

GPUImageView中方法的实现为:

- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
    runSynchronouslyOnVideoProcessingQueue(^{
        [GPUImageOpenGLESContext setActiveShaderProgram:displayProgram];
        [self setDisplayFramebuffer];
        
        glClearColor(backgroundColorRed, backgroundColorGreen, backgroundColorBlue, backgroundColorAlpha);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        glActiveTexture(GL_TEXTURE4);
        glBindTexture(GL_TEXTURE_2D, inputTextureForDisplay);
        glUniform1i(displayInputTextureUniform, 4);
        
        glVertexAttribPointer(displayPositionAttribute, 2, GL_FLOAT, 0, 0, imageVertices);
        glVertexAttribPointer(displayTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, [GPUImageView textureCoordinatesForRotation:inputRotation]);
        
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        
        [self presentFramebuffer];
    });
}

7.获取或者保存到相册修改后的图片. 使用currentFilteredFrame(此方法是GPUImageFilterPipeline类下的)

类的说明:

  • GPUImageFilter:继承 GPUImageOutput,同时遵循 GPUImageInput 协议,类的说明如下:
    /** GPUImage's base filter class

Filters and other subsequent elements in the chain conform to the GPUImageInput protocol, which lets them take in the supplied or processed texture from the previous link in the chain and do something with it. Objects one step further down the chain are considered targets, and processing can be branched by adding multiple targets to a single output or filter.
*/

  • GPUImageOutput:最重要的一个类 (所有的 filter 的父类,其他也有继承它的,如GPUImageUIElement,UIKit 元素通过 CG 转 gles 贴图 等等);
  • GPUImageInput:协议(或者接口)。类的说明其实已经很明了,视频采集,拍照等都是以它为基类,同意套路:源(视频,静态图)上传图片帧给 OpenGL ES 作为 textures,这些 textures 作为下一个 filter 的输入,形成处理 texture 的链式结构。
  • GPUImagePicture类静态图像处理操作,它可以是需要处理的静态图像,也可以是一张作为纹理使用的图片,调用向它发送processImage消息,进行图像滤镜处理。
  • GPUImageFilterPipeline为静态图片添加组合滤镜. 就是说几种不同的图片效果 filters. 继承自NSObject,它的主要作用是管理滤镜链,自身不能参与响应链中。可以用来构建简单的滤镜组合。如果滤镜比较复杂或是涉及到多个纹理的处理,GPUImageFilterGroup则是更好的选择。
    注意:组合滤镜的添加顺序不同,处理结果也不同!
    初始化方法:
    /**
     *  初始化 pipeline
     *
     *  @param filters 滤镜数组
     *  @param input   被渲染的输入源,可以是GPUImagePicture、GPUImageVideoCamera等
     *  @param output  渲染后的输出容器,一般是显示的视图
     *
     *  @return GPUImageFilterPipeline的对象
     */
- (id) initWithOrderedFilters:(NSArray*) filters input:(GPUImageOutput*)input output:(id )output;
  • GPUImageFilterGroup:继承自GPUImageOutput ,实现了GPUImageInput协议。因此,可以自身可以作为独立的滤镜参与响应链中。相比GPUImageFilterPipeline,GPUImageFilterGroup功能更强大。
  • GPUImageView: 用它来显示图片. 类似UIImageView.作为最终的输出 target 只实现了 GPUImageInput 的协议,只能接受 source 或者 filter 传过来的数据,不再作为输出了.其中的 setInputFramebuffer 和 newFrameReadyAtTime 和 filter 中处理如出一辙,但是加了一个调用;如下,正如开头提到的 framebuffer 也可以绑定到 renderBuffer,也常称为 colorbuffer,renderbuffer 直接显示在 CAEAGLLayer 上了;最终通过设置屏幕大小的缓冲区,直接显示在手机屏幕上。
  • GPUImageOpenGLESContext:继承自NSObject.
  • GLProgram 与着色器程序创建息息相关,其中包括创建、编译、链接、使用等过程。

你可能感兴趣的:(GPUImage渲染流程)