GPUImage渲染流程

2017-08-08  本文已影响134人  huicuihui

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 <GPUImageInput>)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<GPUImageInput> 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<GPUImageInput> 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<GPUImageInput> 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类下的)

类的说明:

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.
*/

    /**
     *  初始化 pipeline
     *
     *  @param filters 滤镜数组
     *  @param input   被渲染的输入源,可以是GPUImagePicture、GPUImageVideoCamera等
     *  @param output  渲染后的输出容器,一般是显示的视图
     *
     *  @return GPUImageFilterPipeline的对象
     */
- (id) initWithOrderedFilters:(NSArray*) filters input:(GPUImageOutput*)input output:(id <GPUImageInput>)output;
上一篇 下一篇

猜你喜欢

热点阅读