OpenGL ES之滤镜处理(1)_分屏滤镜
2020-08-11 本文已影响0人
_涼城
初始化工程完成正常的纹理加载
着色器部分
顶点着色器
因为滤镜主要是对纹理进行处理。因此,顶点着色器代码不用变更。
attribute vec4 Position;
attribute vec2 TextureCoords;
varying vec2 TextureCoordsVarying;
void main (void) {
gl_Position = Position;
TextureCoordsVarying = TextureCoords;
}
普通纹理加载的片元着色器
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec4 mask = texture2D(Texture, TextureCoordsVarying);
gl_FragColor = vec4(mask.rgb, 1.0);
}
OpenGL ES 部分
初始化滤镜工具栏
用collectionView实现即可。
滤镜处理初始化
初始化上下文
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:_context];
初始化顶点数据
//3.初始化顶点(0,1,2,3)的顶点坐标以及纹理坐标
self.vertices = malloc(sizeof(SenceVertex) * 4);
self.vertices[0] = (SenceVertex){{-1, 1, 0}, {0, 1}};
self.vertices[1] = (SenceVertex){{-1, -1, 0}, {0, 0}};
self.vertices[2] = (SenceVertex){{1, 1, 0}, {1, 1}};
self.vertices[3] = (SenceVertex){{1, -1, 0}, {1, 0}};
初始化并绑定渲染图层
-
初始化
CAEAGLLayer *layer = [[CAEAGLLayer alloc] init]; layer.frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width); layer.contentsScale = [UIScreen mainScreen].scale; [self.view.layer addSublayer:layer];
-
绑定渲染缓冲区及帧缓冲区
//1.渲染缓存区,帧缓存区对象 GLuint renderBuffer; GLuint frameBuffer; //2.获取帧渲染缓存区名称,绑定渲染缓存区以及将渲染缓存区与layer建立连接 glGenRenderbuffers(1, &renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer); [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; //3.获取帧缓存区名称,绑定帧缓存区以及将渲染缓存区附着到帧缓存区上 glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
加载纹理
绑定顶点缓冲区
glViewport(0, 0, self.drawableWidth, self.drawableHeight);
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(SenceVertex) * 4, self.vertices, GL_STATIC_DRAW);
初始化着色器程序
-
实现着色器的编译
//编译shader代码 - (GLuint)compileShaderWithName:(NSString *)name type:(GLenum)shaderType { //1.获取shader 路径 NSString *shaderPath = [[NSBundle mainBundle] pathForResource:name ofType:shaderType == GL_VERTEX_SHADER ? @"vsh" : @"fsh"]; NSError *error; NSString *shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error]; if (!shaderString) { NSAssert(NO, @"读取shader失败"); exit(1); } //2. 创建shader->根据shaderType GLuint shader = glCreateShader(shaderType); //3.获取shader source const char *shaderStringUTF8 = [shaderString UTF8String]; int shaderStringLength = (int)[shaderString length]; glShaderSource(shader, 1, &shaderStringUTF8, &shaderStringLength); //4.编译shader glCompileShader(shader); //5.查看编译是否成功 GLint compileSuccess; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileSuccess); if (compileSuccess == GL_FALSE) { GLchar messages[256]; glGetShaderInfoLog(shader, sizeof(messages), 0, &messages[0]); NSString *messageString = [NSString stringWithUTF8String:messages]; NSAssert(NO, @"shader编译失败:%@", messageString); exit(1); } //6.返回shader return shader; }
-
实现链接程序
#pragma mark -shader compile and link //link Program - (GLuint)programWithShaderName:(NSString *)shaderName { //1. 编译顶点着色器/片元着色器 GLuint vertexShader = [self compileShaderWithName:shaderName type:GL_VERTEX_SHADER]; GLuint fragmentShader = [self compileShaderWithName:shaderName type:GL_FRAGMENT_SHADER]; //2. 将顶点/片元附着到program GLuint program = glCreateProgram(); glAttachShader(program, vertexShader); glAttachShader(program, fragmentShader); //3.linkProgram glLinkProgram(program); //4.检查是否link成功 GLint linkSuccess; glGetProgramiv(program, GL_LINK_STATUS, &linkSuccess); if (linkSuccess == GL_FALSE) { GLchar messages[256]; glGetProgramInfoLog(program, sizeof(messages), 0, &messages[0]); NSString *messageString = [NSString stringWithUTF8String:messages]; NSAssert(NO, @"program链接失败:%@", messageString); exit(1); } //5.返回program return program; }
-
加载程序并处理数据通道
// 初始化着色器程序 - (void)setupShaderProgramWithName:(NSString *)name { //1. 获取着色器program GLuint program = [self programWithShaderName:name]; //2. use Program glUseProgram(program); //3. 获取Position,Texture,TextureCoords 的索引位置 GLuint positionSlot = glGetAttribLocation(program, "Position"); GLuint textureSlot = glGetUniformLocation(program, "Texture"); GLuint textureCoordsSlot = glGetAttribLocation(program, "TextureCoords"); //4.激活纹理,绑定纹理ID glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, self.textureID); //5.纹理sample glUniform1i(textureSlot, 0); //6.打开positionSlot 属性并且传递数据到positionSlot中(顶点坐标) glEnableVertexAttribArray(positionSlot); glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, positionCoord)); //7.打开textureCoordsSlot 属性并传递数据到textureCoordsSlot(纹理坐标) glEnableVertexAttribArray(textureCoordsSlot); glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, textureCoord)); //8.保存program,界面销毁则释放 self.program = program; }
通过处理片元着色器代码完成滤镜处理
二分屏滤镜
原理
当实现二分屏滤镜时,图片纹理坐标的x值是没有任何变化的,主要是y值变化
- 当 y 在[0, 0.5]范围时,屏幕的(0,0)坐标需要对应图片的(0,0.25),所以y = y+0.25
- 当 y 在[0.5, 1]范围时,屏幕的(0,0.5)坐标需要对应图片的(0,0.25),所以y = y-0.25
片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec2 uv = TextureCoordsVarying.xy;
float y;
if (uv.y >= 0.0 && uv.y <= 0.5) {
y = uv.y + 0.25;
}else{
y = uv.y - 0.25;
}
vec4 mask = texture2D(Texture, vec2(uv.x,y));
gl_FragColor = vec4(mask.rgb, 1.0);
}
三分屏滤镜
原理
当实现三分屏滤镜时,图片纹理坐标的x值是没有任何变化的,主要是y值变化
- 当 y 在[0, 1/3]范围时,屏幕的(0,0)坐标需要对应图片的(0,1/3),所以y = y+1/3
- 当 y 在[1/3, 2/3]范围时,屏幕的(0,1/3)坐标需要对应图片的(0,1/3),所以y 不变
- 当 y 在[2/3, 1]范围时,屏幕的(0,2/3)坐标需要对应图片的(0,1/3),所以y = y-1/3
片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec2 uv = TextureCoordsVarying.xy;
float y;
if (uv.y < 1.0 / 3.0) {
y = uv.y + 1.0 / 3.0;
}else if(uv.y > 2.0 / 3.0){
y = uv.y -1.0 / 3.0;
}else{
y = uv.y;
}
vec4 mask = texture2D(Texture, vec2(uv.x,y));
gl_FragColor = vec4(mask.rgb, 1.0);
}
四分屏滤镜
原理
当实现四分屏时,纹理坐标x、y均需要变化,且屏幕坐标需要与纹理坐标一一映射。
- 当 x 在[0, 0.5]范围时,x = x*2
- 当 x在[0.5, 1]范围时,x = (x-0.5)*2
- 当 y 在[0, 0.5]范围时,y = y*2
- 当 y 在[0.5, 1]范围时,y = (y-0.5)*2
片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec2 uv = TextureCoordsVarying.xy;
if (uv.x <= 0.5) {
uv.x = uv.x * 2.0;
}else{
uv.x = (uv.x - 0.5) * 2.0;
}
if (uv.y <= 0.5) {
uv.y = uv.y * 2.0;
}else{
uv.y = (uv.y - 0.5) * 2.0;
}
vec4 mask = texture2D(Texture, uv);
gl_FragColor = vec4(mask.rgb, 1.0);
}
六分屏滤镜
原理
当实现六分屏时,纹理坐标x、y均需要变化,其变化规则如下:
- 当 x 在[0, 1/3]范围时,x = x+1/3
- 当 x 在[1/3, 2/3]范围时,x 不变
- 当 x 在[2/3, 1]范围时,x = x-1/3
- 当 y 在[0, 0.5]范围时,y = y+0.25
- 当 y 在[0.5, 1]范围时,y = y-0.25
片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying highp vec2 TextureCoordsVarying;
void main (void) {
vec2 uv = TextureCoordsVarying.xy;
if (uv.x <= 1.0 / 3.0) {
uv.x = uv.x + 1.0 / 3.0;
}else if(uv.x >= 2.0 / 3.0){
uv.x = uv.x -1.0 / 3.0;
}
if (uv.y <= 0.5) {
uv.y = uv.y + 0.25;
}else{
uv.y = uv.y - 0.25;
}
gl_FragColor = texture2D(Texture, uv);
}
九分屏滤镜
原理
当实现九分屏时与四分屏类似,纹理坐标x、y均需要变化,其变化规则如下:
- 当 x 在[0, 1/3]范围时,x = x*3
- 当 x 在[1/3, 2/3]范围时,x = (x-1/3)*3
- 当 x 在[2/3, 1]范围时,x = (x-2/3)*3
- 当 y 在[0, 1/3]范围时,y= y*3
- 当 y 在[1/3, 2/3]范围时,y = (y-1/3)*3
- 当 y 在[2/3, 1]范围时,y = (y-2/3)*3
片元着色器代码
precision highp float;
uniform sampler2D Texture;
varying vec2 TextureCoordsVarying;
void main (void) {
vec2 uv = TextureCoordsVarying.xy;
if (uv.x <= 1.0 / 3.0) {
uv.x = uv.x * 3.0;
}else if (uv.x <= 2.0 / 3.0){
uv.x = (uv.x - 1.0 / 3.0) * 3.0;
}else{
uv.x = (uv.x - 2.0 / 3.0) * 3.0;
}
if (uv.y <= 1.0 / 3.0) {
uv.y = uv.y * 3.0;
}else if (uv.y <= 2.0 / 3.0){
uv.y = (uv.y - 1.0 / 3.0) * 3.0;
}else{
uv.y = (uv.y - 2.0 / 3.0) * 3.0;
}
vec4 mask = texture2D(Texture, uv);
gl_FragColor = vec4(mask.rgb, 1.0);
}