實(shí)現(xiàn)GPUImage【OpenGL渲染原理】

本篇主要講解GPUImage底層是如何渲染的,GPUImage底層使用的是OPENGL,操控GPU來實(shí)現(xiàn)屏幕展示

一、由于網(wǎng)上OpenGL實(shí)戰(zhàn)資料特別少,官方文檔對(duì)一些方法也是解釋不清楚,我通過查找一些博客,文檔整理相關(guān)的知識(shí)如下,有些不到位的地方希望大家指教,我虛心學(xué)習(xí)

二、GPUImageVideoCamera

可以捕獲采集的視頻數(shù)據(jù)

關(guān)鍵是捕獲到一幀一幀視頻數(shù)據(jù)如何展示?

通過這個(gè)方法可以獲取采集的視頻數(shù)據(jù)

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection

采集視頻注意點(diǎn):要設(shè)置采集豎屏,否則獲取的數(shù)據(jù)是橫屏

通過AVCaptureConnection就可以設(shè)置

[connection setVideoOrientation:AVCaptureVideoOrientationPortrait];

三、自定義OpenGLView渲染視頻? //以下的一些采集原理GPUImage框架GPUImageMovieWriter基本封裝的比較好了(直接查看GPUImageMovieWriter方法也行),下面是講的是采集到視頻的幀數(shù)據(jù) 渲染實(shí)現(xiàn)的原理和步驟

暴露一個(gè)接口,獲取采集到的幀數(shù)據(jù),然后把幀數(shù)據(jù)傳遞給渲染View,展示出來

- (void)displayFramebuffer:(CMSampleBufferRef)sampleBuffer;

四、利用OpenGL渲染幀數(shù)據(jù)并顯示

導(dǎo)入頭文件#import ,GLKit.h底層使用了OpenGLES,導(dǎo)入它,相當(dāng)于自動(dòng)導(dǎo)入了OpenGLES

步驟

01-自定義圖層類型

02-初始化CAEAGLLayer圖層屬性(可以參考GPUImageView里面的內(nèi)容)

03-創(chuàng)建EAGLContext

04-創(chuàng)建渲染緩沖區(qū)

05-創(chuàng)建幀緩沖區(qū)

06-創(chuàng)建著色器

07-創(chuàng)建著色器程序

08-創(chuàng)建紋理對(duì)象

09-YUV轉(zhuǎn)RGB繪制紋理

10-渲染緩沖區(qū)到屏幕

11-清理內(nèi)存

01-自定義圖層類型

為什么要自定義圖層類型CAEAGLLayer? CAEAGLLayer是OpenGL專門用來渲染的圖層,使用OpenGL必須使用這個(gè)圖層

#pragma mark - 1.自定義圖層類型

+ (Class)layerClass

{

return [CAEAGLLayer class];

}

02-初始化CAEAGLLayer圖層屬性

1.不透明度(opaque)=YES,CALayer默認(rèn)是透明的,透明性能不好,最好設(shè)置為不透明.

2.設(shè)置繪圖屬性

kEAGLDrawablePropertyRetainedBacking :NO (告訴CoreAnimation不要試圖保留任何以前繪制的圖像留作以后重用)

kEAGLDrawablePropertyColorFormat :kEAGLColorFormatRGBA8 (告訴CoreAnimation用8位來保存RGBA的值)

其實(shí)設(shè)置不設(shè)置都無所謂,默認(rèn)也是這個(gè)值,只不過GPUImage設(shè)置了

#pragma mark - 2.初始化圖層

- (void)setupLayer

{

CAEAGLLayer *openGLLayer = (CAEAGLLayer *)self.layer;

_openGLLayer = openGLLayer;

// 設(shè)置不透明,CALayer 默認(rèn)是透明的,透明性能不好,最好設(shè)置為不透明.

openGLLayer.opaque = YES;

// 設(shè)置繪圖屬性drawableProperties

// kEAGLColorFormatRGBA8 : red、green、blue、alpha共8位

openGLLayer.drawableProperties = @{

kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO],

kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8

};

}

03-創(chuàng)建EAGLContext

需要將它設(shè)置為當(dāng)前context,所有的OpenGL ES渲染默認(rèn)渲染到當(dāng)前上下文

EAGLContext管理所有使用OpenGL ES進(jìn)行描繪的狀態(tài),命令以及資源信息,要繪制東西,必須要有上下文,跟圖形上下文類似。

當(dāng)你創(chuàng)建一個(gè)EAGLContext,你要聲明你要用哪個(gè)version的API。這里,我們選擇OpenGL ES 2.0

#pragma mark - 3、創(chuàng)建OpenGL上下文,并且設(shè)置上下文

- (void)setupContext

{

// 指定OpenGL 渲染 API 的版本,目前都使用 OpenGL ES 2.0

EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;

// 創(chuàng)建EAGLContext上下文

_context = [[EAGLContext alloc] initWithAPI:api];

// 設(shè)置為當(dāng)前上下文,所有的渲染默認(rèn)渲染到當(dāng)前上下文

[EAGLContext setCurrentContext:_context];

}

04-創(chuàng)建渲染緩沖區(qū)

有了上下文,openGL還需要在一塊buffer進(jìn)行描繪,這塊buffer就是RenderBuffer

OpenGLES 總共有三大不同用途的color buffer,depth buffer 和 stencil buffer.

最基本的是color buffer,創(chuàng)建它就好了

函數(shù)glGenRenderbuffers

函數(shù) void glGenRenderbuffers (GLsizei n, GLuint* renderbuffers)

它是為renderbuffer(渲染緩存)申請(qǐng)一個(gè)id(名字),創(chuàng)建渲染緩存

參數(shù)n表示申請(qǐng)生成renderbuffer的個(gè)數(shù)

參數(shù)renderbuffers返回分配給renderbuffer(渲染緩存)的id

。 注意:返回的id不會(huì)為0,id 0 是OpenGL ES保留的,我們也不能使用id 為0的renderbuffer(渲染緩存)。

函數(shù)glBindRenderbuffer

void glBindRenderbuffer (GLenum target, GLuint renderbuffer)

告訴OpenGL:我在后面引用GL_RENDERBUFFER的地方,其實(shí)是引用_colorRenderBuffer

參數(shù)target必須為GL_RENDERBUFFER

參數(shù)renderbuffer就是使用glGenRenderbuffers生成的id

。 當(dāng)指定id的renderbuffer第一次被設(shè)置為當(dāng)前renderbuffer時(shí),會(huì)初始化該 renderbuffer對(duì)象,其初始值為:

width 和 height:像素單位的寬和高,默認(rèn)值為0;

internal format:內(nèi)部格式,三大 buffer 格式之一 -- color,depth or stencil;

Color bit-depth:僅當(dāng)內(nèi)部格式為 color 時(shí),設(shè)置顏色的 bit-depth,默認(rèn)值為0;

Depth bit-depth:僅當(dāng)內(nèi)部格式為 depth時(shí),默認(rèn)值為0;

Stencil bit-depth: 僅當(dāng)內(nèi)部格式為 stencil,默認(rèn)值為0

函數(shù)renderbufferStorage

EAGLContext方法 - (BOOL)renderbufferStorage:(NSUInteger)target fromDrawable:(id)drawable

把渲染緩存(renderbuffer)綁定到渲染圖層(CAEAGLLayer)上,并為它分配一個(gè)共享內(nèi)存。

參數(shù)target,為哪個(gè)renderbuffer分配存儲(chǔ)空間

參數(shù)drawable,綁定在哪個(gè)渲染圖層,會(huì)根據(jù)渲染圖層里的繪圖屬性生成共享內(nèi)存。

//? ? 底層調(diào)用這個(gè)分配內(nèi)存

glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, _openGLLayer.bounds.size.width, _openGLLayer.bounds.size.height);

實(shí)戰(zhàn)代碼

#pragma mark - 4、創(chuàng)建渲染緩存

- (void)setupRenderBuffer

{

glGenRenderbuffers(1, &_colorRenderBuffer);

glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);

// 把渲染緩存綁定到渲染圖層上CAEAGLLayer,并為它分配一個(gè)共享內(nèi)存。

// 并且會(huì)設(shè)置渲染緩存的格式,和寬度

[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_openGLLayer];

}

05-創(chuàng)建幀緩沖區(qū)

它相當(dāng)于buffer(color, depth, stencil)的管理者,三大buffer可以附加到一個(gè)framebuffer上

本質(zhì)是把framebuffer內(nèi)容渲染到屏幕

函數(shù)glFramebufferRenderbuffer

void glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)

該函數(shù)是將相關(guān)buffer()三大buffer之一)attach到framebuffer上,就會(huì)自動(dòng)把渲染緩存的內(nèi)容填充到幀緩存,在由幀緩存渲染到屏幕

參數(shù)target,哪個(gè)幀緩存

參數(shù)attachment是指定renderbuffer被裝配到那個(gè)裝配點(diǎn)上,其值是GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT中的一個(gè),分別對(duì)應(yīng) color,depth和 stencil三大buffer。

renderbuffertarget:哪個(gè)渲染緩存

renderbuffer渲染緩存id

#pragma mark - 5、創(chuàng)建幀緩沖區(qū)

- (void)setupFrameBuffer

{

glGenFramebuffers(1, &_framebuffers);

glBindFramebuffer(GL_FRAMEBUFFER, _framebuffers);

// 把顏色渲染緩存 添加到 幀緩存的GL_COLOR_ATTACHMENT0上,就會(huì)自動(dòng)把渲染緩存的內(nèi)容填充到幀緩存,在由幀緩存渲染到屏幕

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);

}

06-創(chuàng)建著色器

著色器

什么是著色器? 通常用來處理紋理對(duì)象,并且把處理好的紋理對(duì)象渲染到幀緩存上,從而顯示到屏幕上。

提取紋理信息,可以處理頂點(diǎn)坐標(biāo)空間轉(zhuǎn)換,紋理色彩度調(diào)整(濾鏡效果)等操作。

著色器分為頂點(diǎn)著色器,片段著色器

頂點(diǎn)著色器用來確定圖形形狀

片段著色器用來確定圖形渲染顏色

步驟: 1.編輯著色器代碼 2.創(chuàng)建著色器 3.編譯著色器

只要?jiǎng)?chuàng)建一次,可以在一開始的時(shí)候創(chuàng)建

著色器代碼

// 頂點(diǎn)著色器代碼

NSString *const kVertexShaderString = SHADER_STRING

(

attribute vec4 position;

attribute vec2 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main()

{

gl_Position = position;

textureCoordinate = inputTextureCoordinate;

}

);

// 片段著色器代碼

NSString *const kYUVFullRangeConversionForLAFragmentShaderString = SHADER_STRING

(

varying highp vec2 textureCoordinate;

precision mediump float;

uniform sampler2D luminanceTexture;

uniform sampler2D chrominanceTexture;

uniform mediump mat3 colorConversionMatrix;

void main()

{

mediump vec3 yuv;

lowp vec3 rgb;

yuv.x = texture2D(luminanceTexture, textureCoordinate).r;

yuv.yz = texture2D(chrominanceTexture, textureCoordinate).ra - vec2(0.5, 0.5);

rgb = colorConversionMatrix * yuv;

gl_FragColor = vec4(rgb, 1);

}

);

實(shí)戰(zhàn)代碼

#pragma mark - 06、創(chuàng)建著色器

- (void)setupShader

{

// 創(chuàng)建頂點(diǎn)著色器

_vertShader = [self loadShader:GL_VERTEX_SHADER withString:kVertexShaderString];

// 創(chuàng)建片段著色器

_fragShader = [self loadShader:GL_FRAGMENT_SHADER withString:kYUVFullRangeConversionForLAFragmentShaderString];

}

// 加載著色器

- (GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString

{

// 創(chuàng)建著色器

GLuint shader = glCreateShader(type);

if (shader == 0) {

NSLog(@"Error: failed to create shader.");

return 0;

}

// 加載著色器源代碼

const char * shaderStringUTF8 = [shaderString UTF8String];

glShaderSource(shader, 1, &shaderStringUTF8, NULL);

// 編譯著色器

glCompileShader(shader);

// 檢查是否完成

GLint compiled = 0;

// 獲取完成狀態(tài)

glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);

if (compiled == 0) {

// 沒有完成就直接刪除著色器

glDeleteShader(shader);

return 0;

}

return shader;

}

07-創(chuàng)建著色器程序

步驟: 1.創(chuàng)建程序 2.貼上頂點(diǎn)和片段著色器 3.綁定attribute屬性 4.連接程序 5.綁定uniform屬性 6.運(yùn)行程序

注意點(diǎn):第3步和第5步,綁定屬性,必須有順序,否則綁定不成功,造成黑屏

#pragma mark - 7、創(chuàng)建著色器程序

- (void)setupProgram

{

// 創(chuàng)建著色器程序

_program = glCreateProgram();

// 綁定著色器

// 綁定頂點(diǎn)著色器

glAttachShader(_program, _vertShader);

// 綁定片段著色器

glAttachShader(_program, _fragShader);

// 綁定著色器屬性,方便以后獲取,以后根據(jù)角標(biāo)獲取

// 一定要在鏈接程序之前綁定屬性,否則拿不到

glBindAttribLocation(_program, ATTRIB_POSITION, "position");

glBindAttribLocation(_program, ATTRIB_TEXCOORD, "inputTextureCoordinate");

// 鏈接程序

glLinkProgram(_program);

// 獲取全局參數(shù),注意 一定要在連接完成后才行,否則拿不到

_luminanceTextureAtt = glGetUniformLocation(_program, "luminanceTexture");

_chrominanceTextureAtt = glGetUniformLocation(_program, "chrominanceTexture");

_colorConversionMatrixAtt = glGetUniformLocation(_program, "colorConversionMatrix");

// 啟動(dòng)程序

glUseProgram(_program);

}

08-創(chuàng)建紋理對(duì)象

紋理

采集的是一張一張的圖片,可以把圖片轉(zhuǎn)換為OpenGL中的紋理, 然后再把紋理畫到OpenGL的上下文中

什么是紋理?一個(gè)紋理其實(shí)就是一幅圖像。

紋理映射,我們可以把這幅圖像的整體或部分貼到我們先前用頂點(diǎn)勾畫出的物體上去.

比如繪制一面磚墻,就可以用一幅真實(shí)的磚墻圖像或照片作為紋理貼到一個(gè)矩形上,這樣,一面逼真的磚墻就畫好了。如果不用紋理映射的方法,則墻上的每一塊磚都必須作為一個(gè)獨(dú)立的多邊形來畫。另外,紋理映射能夠保證在變換多邊形時(shí),多邊形上的紋理圖案也隨之變化。

紋理映射是一個(gè)相當(dāng)復(fù)雜的過程,基本步驟如下:

1)激活紋理單元、2)創(chuàng)建紋理 、3)綁定紋理 、4)設(shè)置濾波

注意:紋理映射只能在RGBA方式下執(zhí)行

函數(shù)glTexParameter

void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param);

{if}:表示可能是否i,f

[v]:表示v可有可無

控制濾波,濾波就是去除沒用的信息,保留有用的信息

一般來說,紋理圖像為正方形或長(zhǎng)方形。但當(dāng)它映射到一個(gè)多邊形或曲面上并變換到屏幕坐標(biāo)時(shí),紋理的單個(gè)紋素很少對(duì)應(yīng)于屏幕圖像上的像素。根據(jù)所用變換和所用紋理映射,屏幕上單個(gè)象素可以對(duì)應(yīng)于一個(gè)紋素的一小部分(即放大)或一大批紋素(即縮小)

固定寫法

/* 控制濾波 */

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

函數(shù)glPixelStorei

void glPixelStorei(GLenum pname, GLint param);

設(shè)置像素存儲(chǔ)方式

pname:像素存儲(chǔ)方式名

一種是GL_PACK_ALIGNMENT,用于將像素?cái)?shù)據(jù)打包,一般用于壓縮。

另一種是GL_UNPACK_ALIGNMENT,用于將像素?cái)?shù)據(jù)解包,一般生成紋理對(duì)象,就需要用到解包.

param:用于指定存儲(chǔ)器中每個(gè)像素行有多少個(gè)字節(jié)對(duì)齊。這個(gè)數(shù)值一般是1、2、4或8,

一般填1,一個(gè)像素對(duì)應(yīng)一個(gè)字節(jié);

函數(shù)CVOpenGLESTextureCacheCreateTextureFromImage

CVOpenGLESTextureCacheCreateTextureFromImage(CFAllocatorRef? _Nullable allocator, CVOpenGLESTextureCacheRef? _Nonnull textureCache, CVImageBufferRef? _Nonnull sourceImage, CFDictionaryRef? _Nullable textureAttributes, GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, size_t planeIndex, CVOpenGLESTextureRef? _Nullable * _Nonnull textureOut)

根據(jù)圖片生成紋理

參數(shù)allocator kCFAllocatorDefault,默認(rèn)分配內(nèi)存

參數(shù)textureCache 紋理緩存

參數(shù)sourceImage 圖片

參數(shù)textureAttributes NULL

參數(shù)target , GL_TEXTURE_2D(創(chuàng)建2維紋理對(duì)象)

參數(shù)internalFormat GL_LUMINANCE,亮度格式

參數(shù)width 圖片寬

參數(shù)height 圖片高

參數(shù)format GL_LUMINANCE 亮度格式

參數(shù)type 圖片類型 GL_UNSIGNED_BYTE

參數(shù)planeIndex 0,切面角標(biāo),表示第0個(gè)切面

參數(shù)textureOut 輸出的紋理對(duì)象

fotmat格式? ? ? ? ? ? ? ? ? ? 描述

GL_ALPHA? ? ? ? ? ? 按照ALPHA值存儲(chǔ)紋理單元

GL_LUMINANCE? ? ? ? 按照亮度值存儲(chǔ)紋理單元

GL_LUMINANCE_ALPHA? ? 按照亮度和alpha值存儲(chǔ)紋理單元

GL_RGB? ? ? ? ? ? ? ? 按照RGB成分存儲(chǔ)紋理單元

GL_RGBA? ? ? ? ? ? ? ? 按照RGBA成分存儲(chǔ)紋理單元

實(shí)戰(zhàn)代碼

#pragma mark - 7、創(chuàng)建紋理對(duì)象,渲染采集圖片到屏幕

- (void)setupTexture:(CMSampleBufferRef)sampleBuffer

{

// 獲取圖片信息

CVImageBufferRef imageBufferRef = CMSampleBufferGetImageBuffer(sampleBuffer);

// 獲取圖片寬度

GLsizei bufferWidth = (GLsizei)CVPixelBufferGetWidth(imageBufferRef);

_bufferWidth = bufferWidth;

GLsizei bufferHeight = (GLsizei)CVPixelBufferGetHeight(imageBufferRef);

_bufferHeight = bufferHeight;

// 創(chuàng)建亮度紋理

// 激活紋理單元0, 不激活,創(chuàng)建紋理會(huì)失敗

glActiveTexture(GL_TEXTURE0);

// 創(chuàng)建紋理對(duì)象

CVReturn err;

err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCacheRef, imageBufferRef, NULL, GL_TEXTURE_2D, GL_LUMINANCE, bufferWidth, bufferHeight, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0, &_luminanceTextureRef);

if (err) {

NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);

}

// 獲取紋理對(duì)象

_luminanceTexture = CVOpenGLESTextureGetName(_luminanceTextureRef);

// 綁定紋理

glBindTexture(GL_TEXTURE_2D, _luminanceTexture);

// 設(shè)置紋理濾波

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// 激活單元1

glActiveTexture(GL_TEXTURE1);

// 創(chuàng)建色度紋理

err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, _textureCacheRef, imageBufferRef, NULL, GL_TEXTURE_2D, GL_LUMINANCE_ALPHA, bufferWidth / 2, bufferHeight / 2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 1, &_chrominanceTextureRef);

if (err) {

NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);

}

// 獲取紋理對(duì)象

_chrominanceTexture = CVOpenGLESTextureGetName(_chrominanceTextureRef);

// 綁定紋理

glBindTexture(GL_TEXTURE_2D, _chrominanceTexture);

// 設(shè)置紋理濾波

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

}

09-YUV轉(zhuǎn)RGB繪制紋理

紋理映射只能在RGBA方式下執(zhí)行

而采集的是YUV,所以需要把YUV 轉(zhuǎn)換 為 RGBA,

本質(zhì)其實(shí)就是改下矩陣結(jié)構(gòu)

注意點(diǎn):glDrawArrays如果要繪制著色器上的點(diǎn)和片段,必須和著色器賦值代碼放在一個(gè)代碼塊中,否則找不到繪制的信息,就繪制不上去,造成屏幕黑屏

之前是把glDrawArrays和YUV轉(zhuǎn)RGB方法分開,就一直黑屏.

函數(shù)glUniform1i

glUniform1i(GLint location, GLint x)

指定著色器中亮度紋理對(duì)應(yīng)哪一層紋理單元

參數(shù)location:著色器中紋理坐標(biāo)

參數(shù)x:指定那一層紋理

函數(shù)glEnableVertexAttribArray

glEnableVertexAttribArray(GLuint index)

開啟頂點(diǎn)屬性數(shù)組,只有開啟頂點(diǎn)屬性,才能給頂點(diǎn)屬性信息賦值

函數(shù)glVertexAttribPointer

glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *ptr)

設(shè)置頂點(diǎn)著色器屬性,描述屬性的基本信息

參數(shù)indx:屬性ID,給哪個(gè)屬性描述信息

參數(shù)size:頂點(diǎn)屬性由幾個(gè)值組成,這個(gè)值必須位1,2,3或4;

參數(shù)type:表示屬性的數(shù)據(jù)類型

參數(shù)normalized:GL_FALSE表示不要將數(shù)據(jù)類型標(biāo)準(zhǔn)化

參數(shù)stride 表示數(shù)組中每個(gè)元素的長(zhǎng)度;

參數(shù)ptr 表示數(shù)組的首地址

10-渲染緩沖區(qū)到屏幕

注意點(diǎn):必須設(shè)置窗口尺寸glViewport

注意點(diǎn):渲染代碼必須調(diào)用[EAGLContext setCurrentContext:_context]

原因:因?yàn)槭嵌嗑€程,每一個(gè)線程都有一個(gè)上下文,只要在一個(gè)上下文繪制就好,設(shè)置線程的上下文為我們自己的上下文,就能繪制在一起了,否則會(huì)黑屏.

注意點(diǎn):每次創(chuàng)建紋理前,先把之前的紋理引用清空[self cleanUpTextures],否則卡頓

函數(shù)glViewport

glViewport(GLint x, GLint y, GLsizei width, GLsizei height)

設(shè)置OpenGL渲染窗口的尺寸大小,一般跟圖層尺寸一樣.

注意:在我們繪制之前還有一件重要的事情要做,我們必須告訴OpenGL渲染窗口的尺寸大小

方法presentRenderbuffer

- (BOOL)presentRenderbuffer:(NSUInteger)target

是將指定renderbuffer呈現(xiàn)在屏幕上

11-清理內(nèi)存

注意:只要有Ref結(jié)尾的,都需要自己手動(dòng)管理,清空

函數(shù)glClearColor

glClearColor (GLclampf red, GLclampf green, GLclampf blue, GLclampfalpha)

設(shè)置一個(gè)RGB顏色和透明度,接下來會(huì)用這個(gè)顏色涂滿全屏.

函數(shù)glClear

glClear (GLbitfieldmask)

用來指定要用清屏顏色來清除由mask指定的buffer,mask可以是 GL_COLOR_BUFFER_BIT,GL_DEPTH_BUFFER_BIT和GL_STENCIL_BUFFER_BIT的自由組合。

在這里我們只使用到 color buffer,所以清除的就是 clolor buffer。

#pragma mark - 11.清理內(nèi)存

- (void)dealloc

{

// 清空緩存

[self destoryRenderAndFrameBuffer];

// 清空紋理

[self cleanUpTextures];

}

#pragma mark - 銷毀渲染和幀緩存

- (void)destoryRenderAndFrameBuffer

{

glDeleteRenderbuffers(1, &_colorRenderBuffer);

_colorRenderBuffer = 0;

glDeleteBuffers(1, &_framebuffers);

_framebuffers = 0;

}

// 清空紋理

- (void)cleanUpTextures

{

// 清空亮度引用

if (_luminanceTextureRef) {

CFRelease(_luminanceTextureRef);

_luminanceTextureRef = NULL;

}

// 清空色度引用

if (_chrominanceTextureRef) {

CFRelease(_chrominanceTextureRef);

_chrominanceTextureRef = NULL;

}

// 清空紋理緩存

CVOpenGLESTextureCacheFlush(_textureCacheRef, 0);

}

GPUImage工作原理

GPUImage最關(guān)鍵在于GPUImageFramebuffer這個(gè)類,這個(gè)類會(huì)保存當(dāng)前處理好的圖片信息。

GPUImage是通過一個(gè)鏈條處理圖片,每個(gè)鏈條通過target連接,每個(gè)target處理完圖片后,會(huì)生成一個(gè)GPUImageFramebuffer對(duì)象,并且把圖片信息保存到GPUImageFramebuffer。

這樣比如targetA處理好,要處理targetB,就會(huì)先取出targetA的圖片,然后targetB在targetA的圖片基礎(chǔ)上在進(jìn)行處理.

總結(jié):GPUImage是基于OpenGL ES 處理,我一直在持續(xù)學(xué)習(xí)過程中,分析和總結(jié)的東西那些不到位的地方,希望讀友們指點(diǎn),我會(huì)虛心學(xué)習(xí),我們一起進(jìn)步。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,837評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,196評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,688評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,654評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,456評(píng)論 6 406
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,955評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,044評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,195評(píng)論 0 287
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,725評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,608評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,802評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,318評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,048評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,422評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,673評(píng)論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,424評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,762評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容