初始化工程完成正常的紋理加載
著色器部分
頂點著色器
因為濾鏡主要是對紋理進行處理。因此,頂點著色器代碼不用變更。
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實現(xiàn)即可。
濾鏡處理初始化
初始化上下文
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:_context];
初始化頂點數(shù)據(jù)
//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]; -
綁定渲染緩沖區(qū)及幀緩沖區(qū)
//1.渲染緩存區(qū),幀緩存區(qū)對象 GLuint renderBuffer; GLuint frameBuffer; //2.獲取幀渲染緩存區(qū)名稱,綁定渲染緩存區(qū)以及將渲染緩存區(qū)與layer建立連接 glGenRenderbuffers(1, &renderBuffer); glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer); [self.context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; //3.獲取幀緩存區(qū)名稱,綁定幀緩存區(qū)以及將渲染緩存區(qū)附著到幀緩存區(qū)上 glGenFramebuffers(1, &frameBuffer); glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
加載紋理
綁定頂點緩沖區(qū)
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);
初始化著色器程序
-
實現(xiàn)著色器的編譯
//編譯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. 創(chuàng)建shader->根據(jù)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; } -
實現(xiàn)鏈接程序
#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; } -
加載程序并處理數(shù)據(jù)通道
// 初始化著色器程序 - (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 屬性并且傳遞數(shù)據(jù)到positionSlot中(頂點坐標) glEnableVertexAttribArray(positionSlot); glVertexAttribPointer(positionSlot, 3, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, positionCoord)); //7.打開textureCoordsSlot 屬性并傳遞數(shù)據(jù)到textureCoordsSlot(紋理坐標) glEnableVertexAttribArray(textureCoordsSlot); glVertexAttribPointer(textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, sizeof(SenceVertex), NULL + offsetof(SenceVertex, textureCoord)); //8.保存program,界面銷毀則釋放 self.program = program; }
通過處理片元著色器代碼完成濾鏡處理
二分屏濾鏡
原理
當實現(xiàn)二分屏濾鏡時,圖片紋理坐標的x值是沒有任何變化的,主要是y值變化
- 當 y 在[0, 0.5]范圍時,屏幕的(0,0)坐標需要對應(yīng)圖片的(0,0.25),所以y = y+0.25
- 當 y 在[0.5, 1]范圍時,屏幕的(0,0.5)坐標需要對應(yīng)圖片的(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);
}
三分屏濾鏡
原理
當實現(xiàn)三分屏濾鏡時,圖片紋理坐標的x值是沒有任何變化的,主要是y值變化
- 當 y 在[0, 1/3]范圍時,屏幕的(0,0)坐標需要對應(yīng)圖片的(0,1/3),所以y = y+1/3
- 當 y 在[1/3, 2/3]范圍時,屏幕的(0,1/3)坐標需要對應(yīng)圖片的(0,1/3),所以y 不變
- 當 y 在[2/3, 1]范圍時,屏幕的(0,2/3)坐標需要對應(yīng)圖片的(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);
}
四分屏濾鏡
原理
當實現(xiàn)四分屏?xí)r,紋理坐標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);
}
六分屏濾鏡
原理
當實現(xiàn)六分屏?xí)r,紋理坐標x、y均需要變化,其變化規(guī)則如下:
- 當 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);
}
九分屏濾鏡
原理
當實現(xiàn)九分屏?xí)r與四分屏類似,紋理坐標x、y均需要變化,其變化規(guī)則如下:
- 當 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);
}