GPUImage詳細解析

從源碼的角度分析、學習GPUImage和OpenGL ES,這是第一篇,介紹GPUImageFilterGPUImageFramebuffer

OpenGL ES準備

回顧下我們之前的OpenGL ES教程,圖像在OpenGL ES中的表示是紋理,會在片元著色器里面進行像素級別的處理。
假設我們自定義一個OpenGL ES程序來處理圖片,那么會有以下幾個步驟:
1、初始化OpenGL ES環境,編譯、鏈接頂點著色器和片元著色器;
2、緩存頂點、紋理坐標數據,傳送圖像數據到GPU;
3、繪制圖元到特定的幀緩存;
4、在幀緩存取出繪制的圖像。
GPUImageFilter負責的是第一、二、三步。
GPUImageFramebuffer負責是第四步。

GPUImageFilter解析

GPUImageFilter和響應鏈的其他元素實現了GPUImageInput協議,他們都可以提供紋理參與響應鏈,或者從響應鏈的前面接收并處理紋理。響應鏈的下一個對象是target,響應鏈可能有多個分支(添加多個targets)。

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.

  • 獲取紋理坐標
+ (const GLfloat *)textureCoordinatesForRotation:(GPUImageRotationMode)rotationMode;
  • 繪制結果輸出
    繪制的結果后輸入到outputframebuffer指定的緩存

usingNextFrameForImageCapture代表著輸出的結果會被用于獲取圖像,所以在繪制之前要加鎖

    if (usingNextFrameForImageCapture)
    {
        [outputFramebuffer lock];
    }
  • 綁定紋理
    glBindTexture(GL_TEXTURE_2D, [firstInputFramebuffer texture]);
    綁定輸入紋理,OpenGL ES才能確定要處理紋理數據

  • 綁定頂點和紋理坐標并繪制圖元

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

GL_TRIANGLE_STRIP模式用于繪制三角形帶。這里有介紹

  • 紋理解鎖
    [firstInputFramebuffer unlock]; 輸入紋理使用完畢,解鎖。在調用這個解鎖之前必須確定之前已經調用加鎖,否則會報錯。
    GPUImageFramebuffer使用引用計數來管理緩存,當引用計數小于0的時候會回收緩存。

  • 信號量
    如果設置了usingNextFrameForImageCapture,則會通過GCD信號量來通知仍在等待繪制完成的函數。

    if (usingNextFrameForImageCapture)
    {
        dispatch_semaphore_signal(imageCaptureSemaphore);
    }
  • 通知targets
    - (void)informTargetsAboutNewFrameAtTime:(CMTime)frameTime;
    當self的幀繪制完成后,通知自己的targets,并將自己的輸出設置為targets的輸入紋理:
    [self setInputFramebufferForTarget:currentTarget atIndex:textureIndex];
    然后解鎖自己使用的輸出緩沖區[[self framebufferForOutput] unlock];
    (在上一個函數已經lock了這個緩沖區,所以這里的unlock不會馬上回收內存,等到targets使用完自己的紋理后調用unlock,緩存會被回收)
    在設置完緩沖區后,self會通知所有targets(除了設置忽略的)
    [currentTarget newFrameReadyAtTime:frameTime atIndex:textureIndex];
  • 等待渲染完成
    if (dispatch_semaphore_wait(imageCaptureSemaphore, convertedTimeout) != 0)
    {
        return NULL;
    }
  • 一系列setter
    - (void)setInteger:(GLint)newInteger forUniformName:(NSString *)uniformName;
    這些函數是設置GLSL里面的變量

GPUImageFramebuffer

管理紋理緩存格式、幀緩存的buffer。

  • 紋理格式
    默認的紋理格式defaultTextureOptions

  • 緩存創建
    generateTexture會創建對應的紋理緩存
    generateFramebuffer會創建對應的幀緩存
    注意:iOS5.0以上會使用CVOpenGLESTextureCache
    否則會使用glTexImage2D(),這個我們更熟悉的函數來傳送CPU圖像數據到GPU

  • 指定渲染目標
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
    把渲染目標指定為圖像

  • 調整視口大小
    先綁定自己的幀緩存,再調整視口大小。

- (void)activateFramebuffer;
{
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
    glViewport(0, 0, (int)_size.width, (int)_size.height);
}
  • 解鎖
    當引用計數小于1的時候,會調用下面的函數把自己放回緩存管理cache。(注意這個和 destroyFramebuffer不一樣,一個是回收再利用,一個是銷毀)
[[GPUImageContext sharedFramebufferCache] returnFramebufferToCache:self];
  • 從幀緩存中讀取圖片
    newCGImageFromFramebufferContents函數獲取圖像數據。
    CoreVideo提供紋理快速緩存glReadPixels都可以獲得圖像數據,根據iOS版本不同調用不同函數。
    最后通過CGImageCreate,創建 CGImageRef,然后返回。

  • CVPixelBuffer
    CVPixelBuffer是CoreVideo像素緩存是一個圖像緩存,在渲染視頻幀、編解碼視頻幀、使用CoreImage的過程都會用到。
    在訪問CPU的像素數據之前,必須調用CVPixelBufferLockBaseAddress,并在訪問后調用CVPixelBufferUnlockBaseAddress。如果lockFLags帶有kCVPixelBufferLock_ReadOnly參數,那么unlocking 的時候也需要。

A Core Video pixel buffer is an image buffer that holds pixels in main memory. Applications generating frames, compressing or decompressing video, or using Core Image can all make use of Core Video pixel buffers.

  • CVOpenGLESTextureCache
    緩存和管理CVOpenGLESTextureRef紋理,這些紋理緩存提供了一個直接讀寫多種顏色格式緩存的方式。

Core Video OpenGLES texture caches are used to cache and manage CVOpenGLESTextureRef textures. These texture caches provide you with a way to directly read and write buffers with various pixel formats, such as 420v or BGRA, from GLES.

  • CVOpenGLESTexture
    CV紋理是紋理圖像緩存,提供OpenGL圖像數據

Core Video OpenGLES textures are texture-based image buffers used for supplying source image data to OpenGL.

在學習Metal過程中,整理的關于CV緩存和紋理的關系

擴展

GPUImage的四大輸入基礎類,都可以作為響應鏈的起點。這些基礎類會把圖像作為紋理,傳給OpenGL ES處理,然后把紋理傳遞給響應鏈的下一個對象。
GPUImageVideoCamera 攝像頭-視頻流
GPUImageStillCamera 攝像頭-照相
GPUImagePicture 圖片
GPUImageMovie 視頻
響應鏈,先要理解幀緩存的概念,這在OpenGL ES教程-幀緩存有提到過。

總結

用一句話來解釋GPUImageFilter就是用來接收源圖像,通過自定義的頂點、片元著色器來渲染新的圖像,并在繪制完成后通知響應鏈的下一個對象。
GPUImageFramebuffer就是用來管理紋理緩存的格式與讀寫幀緩存的buffer。

這里有個GPUImage的簡單工程,可以看到GPUImage的源代碼。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容