一、前言
本篇主要讲解GPUImage底层是如何渲染的,GPUImage底层使用的是OPENGL,操控GPU来实现屏幕展示
由于网上OpenGL实战资料特别少,官方文档对一些方法也是解释不清楚,避免广大同学再次爬坑,本篇讲解了不少OpenGL的知识,并且还讲解了花了大量时间解决bug的注意点,曾经因为对glDrawArrays这个方法不熟悉,遇上Bug,晚上熬到凌晨四点都没解决,还是第二天中午解决的。
如果喜欢我的文章,可以关注我微博:袁峥Seemygo
1
|
-
(
void
)
captureOutput
:
(
AVCaptureOutput *
)
captureOutput
didOutputSampleBuffer
:
(
CMSampleBufferRef
)
sampleBuffer
fromConnection
:
(
AVCaptureConnection *
)
connection
|
1
|
[
videoConnection
setVideoOrientation
:
AVCaptureVideoOrientationPortraitUpsideDown
]
;
|
1
|
-
(
void
)
displayFramebuffer
:
(
CMSampleBufferRef
)
sampleBuffer
;
|
1
2
3
4
5
|
#pragma mark - 1.自定义图层类型
+
(
Class
)
layerClass
{
return
[
CAEAGLLayer
class
]
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#pragma mark - 2.初始化图层
-
(
void
)
setupLayer
{
CAEAGLLayer *
openGLLayer
=
(
CAEAGLLayer *
)
self
.
layer
;
_openGLLayer
=
openGLLayer
;
// 设置不透明,CALayer 默认是透明的,透明性能不好,最好设置为不透明.
openGLLayer
.
opaque
=
YES
;
// 设置绘图属性drawableProperties
// kEAGLColorFormatRGBA8 : red、green、blue、alpha共8位
openGLLayer
.
drawableProperties
=
@
{
kEAGLDrawablePropertyRetainedBacking
:
[
NSNumber
numberWithBool
:
NO
]
,
kEAGLDrawablePropertyColorFormat
:
kEAGLColorFormatRGBA8
}
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
|
#pragma mark - 3、创建OpenGL上下文,并且设置上下文
-
(
void
)
setupContext
{
// 指定OpenGL 渲染 API 的版本,目前都使用 OpenGL ES 2.0
EAGLRenderingAPI
api
=
kEAGLRenderingAPIOpenGLES2
;
// 创建EAGLContext上下文
_context
=
[
[
EAGLContext
alloc
]
initWithAPI
:
api
]
;
// 设置为当前上下文,所有的渲染默认渲染到当前上下文
[
EAGLContext
setCurrentContext
:
_context
]
;
}
|
1
|
函数
void
glGenRenderbuffers
(
GLsizei
n
,
GLuint*
renderbuffers
)
|
1
|
void
glBindRenderbuffer
(
GLenum
target
,
GLuint
renderbuffer
)
|
1
2
3
4
5
6
7
8
9
|
width
和
height:像素单位的宽和高,默认值为
0;
internal
format:内部格式,三大
buffer
格式之一
--
color,
depth
or
stencil;
Color
bit
-
depth:仅当内部格式为
color
时,设置颜色的
bit
-
depth,默认值为
0;
Depth
bit
-
depth:仅当内部格式为
depth时,默认值为
0;
Stencil
bit
-
depth
:
仅当内部格式为
stencil,默认值为
0
|
1
|
EAGLContext方法
-
(
BOOL
)
renderbufferStorage
:
(
NSUInteger
)
target
fromDrawable
:
(
id
)
drawable
|
1
2
|
// 底层调用这个分配内存
glRenderbufferStorage
(
GL_RENDERBUFFER
,
GL_RGBA
,
_openGLLayer
.
bounds
.
size
.
width
,
_openGLLayer
.
bounds
.
size
.
height
)
;
|
1
2
3
4
5
6
7
8
9
10
11
|
#pragma mark - 4、创建渲染缓存
-
(
void
)
setupRenderBuffer
{
glGenRenderbuffers
(
1
,
&
_colorRenderBuffer
)
;
glBindRenderbuffer
(
GL_RENDERBUFFER
,
_colorRenderBuffer
)
;
// 把渲染缓存绑定到渲染图层上CAEAGLLayer,并为它分配一个共享内存。
// 并且会设置渲染缓存的格式,和宽度
[
_context
renderbufferStorage
:
GL_RENDERBUFFER
fromDrawable
:
_openGLLayer
]
;
}
|
1
|
void
glFramebufferRenderbuffer
(
GLenum
target
,
GLenum
attachment
,
GLenum
renderbuffertarget
,
GLuint
renderbuffer
)
|
1
2
3
4
5
6
7
8
|
#pragma mark - 5、创建帧缓冲区
-
(
void
)
setupFrameBuffer
{
glGenFramebuffers
(
1
,
&
_framebuffers
)
;
glBindFramebuffer
(
GL_FRAMEBUFFER
,
_framebuffers
)
;
// 把颜色渲染缓存 添加到 帧缓存的GL_COLOR_ATTACHMENT0上,就会自动把渲染缓存的内容填充到帧缓存,在由帧缓存渲染到屏幕
glFramebufferRenderbuffer
(
GL_FRAMEBUFFER
,
GL_COLOR_ATTACHMENT0
,
GL_RENDERBUFFER
,
_colorRenderBuffer
)
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
// 顶点着色器代码
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
)
;
}
)
;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
#pragma mark - 06、创建着色器
-
(
void
)
setupShader
{
// 创建顶点着色器
_vertShader
=
[
self
loadShader
:
GL_VERTEX_SHADER
withString
:
kVertexShaderString
]
;
// 创建片段着色器
_fragShader
=
[
self
loadShader
:
GL_FRAGMENT_SHADER
withString
:
kYUVFullRangeConversionForLAFragmentShaderString
]
;
}
// 加载着色器
-
(
GLuint
)
loadShader
:
(
GLenum
)
type
withString
:
(
NSString *
)
shaderString
{
// 创建着色器
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
;
// 获取完成状态
glGetShaderiv
(
shader
,
GL_COMPILE_STATUS
,
&
compiled
)
;
if
(
compiled
==
0
)
{
// 没有完成就直接删除着色器
glDeleteShader
(
shader
)
;
return
0
;
}
return
shader
;
}
|
第3步和第5步,绑定属性,必须有顺序,否则绑定不成功,造成黑屏
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#pragma mark - 7、创建着色器程序
-
(
void
)
setupProgram
{
// 创建着色器程序
_program
=
glCreateProgram
(
)
;
// 绑定着色器
// 绑定顶点着色器
glAttachShader
(
_program
,
_vertShader
)
;
// 绑定片段着色器
glAttachShader
(
_program
,
_fragShader
)
;
// 绑定着色器属性,方便以后获取,以后根据角标获取
// 一定要在链接程序之前绑定属性,否则拿不到
glBindAttribLocation
(
_program
,
ATTRIB_POSITION
,
"position"
)
;
glBindAttribLocation
(
_program
,
ATTRIB_TEXCOORD
,
"inputTextureCoordinate"
)
;
// 链接程序
glLinkProgram
(
_program
)
;
// 获取全局参数,注意 一定要在连接完成后才行,否则拿不到
_luminanceTextureAtt
=
glGetUniformLocation
(
_program
,
"luminanceTexture"
)
;
_chrominanceTextureAtt
=
glGetUniformLocation
(
_program
,
"chrominanceTexture"
)
;
_colorConversionMatrixAtt
=
glGetUniformLocation
(
_program
,
"colorConversionMatrix"
)
;
// 启动程序
glUseProgram
(
_program
)
;
}
|
1
2
3
|
void
glTexParameter
{
if
}
[
v
]
(
GLenum
target
,
GLenum
pname
,
TYPE
param
)
;
{
if
}
:表示可能是否
i
,
f
[
v
]
:表示
v可有可无
|
1
2
3
|
/* 控制滤波 */
glTexParameterf
(
GL_TEXTURE_2D
,
GL_TEXTURE_WRAP_S
,
GL_CLAMP
)
;
glTexParameterf
(
GL_TEXTURE_2D
,
GL_TEXTURE_WRAP_T
,
GL_CLAMP
)
;
|
1
|
void
glPixelStorei
(
GLenum
pname
,
GLint
param
)
;
|
GL_PACK_ALIGNMENT
,用于将像素数据打包,一般用于压缩。GL_UNPACK_ALIGNMENT
,用于将像素数据解包,一般生成纹理对象,就需要用到解包.
1
|
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
)
|
1
2
3
4
5
6
|
fotmat格式
描述
GL
_ALPHA
按照
ALPHA值存储纹理单元
GL
_LUMINANCE
按照亮度值存储纹理单元
GL_LUMINANCE
_ALPHA
按照亮度和
alpha值存储纹理单元
GL
_RGB
按照
RGB成分存储纹理单元
GL
_RGBA
按照
RGBA成分存储纹理单元
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#pragma mark - 7、创建纹理对象,渲染采集图片到屏幕
-
(
void
)
setupTexture
:
(
CMSampleBufferRef
)
sampleBuffer
{
// 获取图片信息
CVImageBufferRef
imageBufferRef
=
CMSampleBufferGetImageBuffer
(
sampleBuffer
)
;
// 获取图片宽度
GLsizei
bufferWidth
=
(
GLsizei
)
CVPixelBufferGetWidth
(
imageBufferRef
)
;
_bufferWidth
=
bufferWidth
;
GLsizei
bufferHeight
=
(
GLsizei
)
CVPixelBufferGetHeight
(
imageBufferRef
)
;
_bufferHeight
=
bufferHeight
;
// 创建亮度纹理
// 激活纹理单元0, 不激活,创建纹理会失败
glActiveTexture
(
GL_TEXTURE0
)
;
// 创建纹理对象
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
)
;
}
// 获取纹理对象
_luminanceTexture
=
CVOpenGLESTextureGetName
(
_luminanceTextureRef
)
;
// 绑定纹理
glBindTexture
(
GL_TEXTURE_2D
,
_luminanceTexture
)
;
// 设置纹理滤波
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
)
;
// 创建色度纹理
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
)
;
}
// 获取纹理对象
_chrominanceTexture
=
CVOpenGLESTextureGetName
(
_chrominanceTextureRef
)
;
// 绑定纹理
glBindTexture
(
GL_TEXTURE_2D
,
_chrominanceTexture
)
;
// 设置纹理滤波
glTexParameterf
(
GL_TEXTURE_2D
,
GL_TEXTURE_WRAP_S
,
GL_CLAMP_TO_EDGE
)
;
glTexParameterf
(
GL_TEXTURE_2D
,
GL_TEXTURE_WRAP_T
,
GL_CLAMP_TO_EDGE
)
;
}
|
glDrawArrays如果要绘制着色器上的点和片段,必须和着色器赋值代码放在一个代码块中,否则找不到绘制的信息,就绘制不上去,造成屏幕黑屏
1
|
glUniform1i
(
GLint
location
,
GLint
x
)
|
1
|
glEnableVertexAttribArray
(
GLuint
index
)
|
1
|
glVertexAttribPointer
(
GLuint
indx
,
GLint
size
,
GLenum
type
,
GLboolean
normalized
,
GLsizei
stride
,
const
GLvoid *
ptr
)
|
1
|
glBindAttribLocation
(
GLuint
program
,
GLuint
index
,
const
GLchar *
name
)
|
1
|
glDrawArrays
(
GLenum
mode
,
GLint
first
,
GLsizei
count
)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
// YUV 转 RGB,里面的顶点和片段都要转换
-
(
void
)
convertYUVToRGBOutput
{
// 在创建纹理之前,有激活过纹理单元,就是那个数字.GL_TEXTURE0,GL_TEXTURE1
// 指定着色器中亮度纹理对应哪一层纹理单元
// 这样就会把亮度纹理,往着色器上贴
glUniform1i
(
_luminanceTextureAtt
,
0
)
;
// 指定着色器中色度纹理对应哪一层纹理单元
glUniform1i
(
|