前言
这里是一篇OpenGl ES教程:把绘制一张图片到屏幕上,对它进行色温,饱和度调节。
正文
核心思路
通过OpenGL ES,实现把一张图片调节色温和饱和度后进行渲染到屏幕上。核心的内容包括:设置渲染管道、设置顶点和纹理缓存、shader编写。
效果展示:
效果图 效果图 效果图具体步骤:
1.通过CoreGraphics将图片数据转为二进制并设置成纹理。
//设置纹理从图片
- (void)setupTextureWithImage:(UIImage*)image {
//1.获取图片宽\高
size_t width = CGImageGetWidth(image.CGImage);
size_t height = CGImageGetHeight(image.CGImage);
//2.获取颜色组件
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapContextCreate(void * __nullable data,
size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow,
CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo)
CGContextRef context = CGBitmapContextCreate(imageData, width, height, 8,4* width,colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrder32Big);
//创建完context,可以释放颜色空间colorSpace
CGColorSpaceRelease( colorSpace );
CGContextClearRect( context,CGRectMake(0,0, width, height ) );
//CTM--从用户空间和设备空间存在一个转换矩阵CTM
CGContextTranslateCTM(context,0, height);
//缩小
CGContextScaleCTM(context,1.0,-1.0);
//绘制图片
CGContextDrawImage( context,CGRectMake(0,0, width, height ), image.CGImage);
//释放context
CGContextRelease(context);
//在绑定纹理之前,激活纹理单元 glActiveTexture
glActiveTexture(GL_TEXTURE1);
//生成纹理标记
glGenTextures(1, &_texture);
//绑定纹理
glBindTexture(GL_TEXTURE_2D, _texture);
//设置纹理参数
//环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//放大\缩小过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
//将图片载入纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLint)width, (GLint)height,0,GL_RGBA,GL_UNSIGNED_BYTE,imageData);
//释放imageData
free(imageData);
}
2.设置色温和饱和度,图层,上下文
- (void)setupData {
_temperature = 0.5;
_saturation = 0.5;
// 用于显示的layer
_eaglLayer = (CAEAGLLayer *)self.layer;
// CALayer默认是透明的,而透明的层对性能负荷很大。所以将其关闭。
_eaglLayer.opaque = YES;
if (!_context) {
// 创建GL环境上下文
// EAGLContext 管理所有通过 OpenGL 进行 Draw 的信息.
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
NSAssert(_context && [EAGLContext setCurrentContext:_context], @"初始化GL环境失败");
}
3.设置RenderBuffer
// 释放旧的 renderbuffer
if (_renderbuffer) {
glDeleteRenderbuffers(1, &_renderbuffer);
_renderbuffer = 0;
}
// 生成renderbuffer ( renderbuffer = 用于展示的窗口 )
glGenRenderbuffers(1, &_renderbuffer);
// 绑定renderbuffer
glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
// GL_RENDERBUFFER 的内容存储到实现 EAGLDrawable 协议的 CAEAGLLayer
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
4.设置并检查frameBuffer
// 释放旧的 framebuffer
if (_framebuffer) {
glDeleteFramebuffers(1, &_framebuffer);
_framebuffer = 0;
}
// 生成 framebuffer ( framebuffer = 画布 )
glGenFramebuffers(1, &_framebuffer);
// 绑定 fraembuffer
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
// framebuffer 不对绘制的内容做存储, 所以这一步是将 framebuffer 绑定到renderbuffer ( 绘制的结果就存在 renderbuffer )
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER,_renderbuffer);
NSError *error;
NSAssert1([self checkFramebuffer:&error],@"%@",error.userInfo[@"ErrorMessage"]);
5.链接shader 色温
//1.vertex shader路径
GLuint vertexShader = [self compileShader:@"XCVertexShader.vsh"withType:GL_VERTEX_SHADER];
//2.fragment shder路径
GLuint fragmentShader = [self compileShader:@"XCTemperature.fsh"withType:GL_FRAGMENT_SHADER];
//创建program
_programHandle = glCreateProgram();
//将vertes Shader 和 fragment Shader 附着到program上
glAttachShader(_programHandle, vertexShader);
glAttachShader(_programHandle, fragmentShader);
//链接program
glLinkProgram(_programHandle);
//获取链接状态
GLintlinkSuccess;
glGetProgramiv(_programHandle,GL_LINK_STATUS, &linkSuccess);
//链接失败处理
if(linkSuccess ==GL_FALSE) {
GLcharmessages[256];
glGetShaderInfoLog(_programHandle,sizeof(messages),0, &messages[0]);
NSString*messageString = [NSStringstringWithUTF8String:messages];
NSLog(@"glGetProgramiv ShaderIngoLog: %@", messageString);
exit(1);
}
//使用program
glUseProgram(_programHandle)
//顶点坐标
glViewAttributes[ATTRIBUTE_POSITION] = glGetAttribLocation(_programHandle, "position");
//输入的纹理坐标
glViewAttributes[ATTRIBUTE_INPUT_TEXTURE_COORDINATE] =glGetAttribLocation(_programHandle, "inputTextureCoordinate");
//输入的图像纹理
glViewUniforms[UNIFORM_INPUT_IMAGE_TEXTURE] = glGetUniformLocation(_programHandle, "inputImageTexture");
//色温值
glViewUniforms[UNIFORM_TEMPERATURE] = glGetUniformLocation(_programHandle, "temperature"); glEnableVertexAttribArray(glViewAttributes[ATTRIBUTE_POSITION]);
glEnableVertexAttribArray(glViewAttributes[ATTRIBUTE_INPUT_TEXTURE_COORDINATE]);
6.链接shader 饱和度
//获取路径
//Vertex Shader是一样的
GLuint vertexShader = [self compileShader:@"XCVertexShader.vsh"withType:GL_VERTEX_SHADER];
//片元着色器是不一样的
GLuint fragmentShader = [self compileShader:@"XCSaturation.fsh"withType:GL_FRAGMENT_SHADER];
//创建program
_tempProgramHandle = glCreateProgram();
//将vertes Shader 和 fragment Shader 附着到program上
glAttachShader(_tempProgramHandle, vertexShader);
glAttachShader(_tempProgramHandle, fragmentShader);
//链接Program
glLinkProgram(_tempProgramHandle);
//获取link状态
GLint linkSuccess;
glGetProgramiv(_tempProgramHandle,GL_LINK_STATUS, &linkSuccess);
//link失败处理
if(linkSuccess ==GL_FALSE) {
GLcharmessages[256];
glGetShaderInfoLog(_tempProgramHandle,sizeof(messages),0, &messages[0]);
NSString*messageString = [NSStringstringWithUTF8String:messages];
NSLog(@"glGetProgramiv ShaderIngoLog: %@", messageString);
exit(1);
}
//使用program
glUseProgram(_tempProgramHandle);
//顶点坐标
glViewAttributes[TEMP_ATTRIBUTE_POSITION] = glGetAttribLocation(_tempProgramHandle, "position");
// //输入的纹理坐标
glViewAttributes[TEMP_ATTRIBUTE_INPUT_TEXTURE_COORDINATE] =glGetAttribLocation(_tempProgramHandle, "inputTextureCoordinate");
//输入的图像纹理
glViewUniforms[TEMP_UNIFORM_INPUT_IMAGE_TEXTURE] = glGetUniformLocation(_tempProgramHandle, "inputImageTexture");
//饱和度
glViewUniforms[UNIFORM_SATURATION] = glGetUniformLocation(_tempProgramHandle, "saturation");
glEnableVertexAttribArray(glViewAttributes[TEMP_ATTRIBUTE_POSITION]);
glEnableVertexAttribArray(glViewAttributes[TEMP_ATTRIBUTE_INPUT_TEXTURE_COORDINATE]);
7.设置VBO (Vertex Buffer Objects)
//顶点坐标和纹理坐标
static const CustomVertexvertices[] =
{
{ .position= { -1.0, -1.0,0,1}, .textureCoordinate= {0.0,0.0} },
{ .position= { 1.0, -1.0,0,1}, .textureCoordinate= {1.0,0.0} },
{ .position= { -1.0, 1.0,0,1}, .textureCoordinate= {0.0,1.0} },
{ .position= { 1.0, 1.0,0,1}, .textureCoordinate= {1.0,1.0} }
};
GLuint vertexBuffer;
// STEP 1 创建缓存对象并返回缓存对象的标识符
glGenBuffers(1, &vertexBuffer);
// STEP 2 将缓存对象对应到相应的缓存上
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
// STEP 3 数据拷贝到缓存对象
glBufferData(GL_ARRAY_BUFFER,sizeof(vertices), vertices,GL_STATIC_DRAW);
8.设置纹理
//申请_tempFramesBuffe标记
glGenFramebuffers(1, &_tempFramebuffer);
//绑定纹理之前,激活纹理
glActiveTexture(GL_TEXTURE0);
//申请纹理标记
glGenTextures(1, &_tempTexture);
//绑定纹理
glBindTexture(GL_TEXTURE_2D, _tempTexture);
//将图片载入纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, self.frame.size.width * self.contentScaleFactor, self.frame.size.height * self.contentScaleFactor, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
//设置纹理参数
//放大\缩小过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//绑定FrameBuffer
glBindFramebuffer(GL_FRAMEBUFFER, _tempFramebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _tempTexture, 0);
9.渲染
//绘制第一个滤镜
//使用program
glUseProgram(_tempProgramHandle);
//绑定frameBuffer
glBindFramebuffer(GL_FRAMEBUFFER, _tempFramebuffer);
//设置视口
glViewport(0, 0, self.frame.size.width * self.contentScaleFactor, self.frame.size.height * self.contentScaleFactor);
//设置清屏颜色
glClearColor(0,0,1,1);
//清理屏幕
glClear(GL_COLOR_BUFFER_BIT);
//为当前的program指定uniform值
//纹理
glUniform1i(glViewUniforms[TEMP_UNIFORM_INPUT_IMAGE_TEXTURE], 1);
//饱和度
glUniform1f(glViewUniforms[UNIFORM_SATURATION], _saturation);
//顶点数据
glVertexAttribPointer(glViewAttributes[TEMP_ATTRIBUTE_POSITION], 4, GL_FLOAT, GL_FALSE, sizeof(CustomVertex), 0);
//纹理数据 glVertexAttribPointer(glViewAttributes[TEMP_ATTRIBUTE_INPUT_TEXTURE_COORDINATE], 2, GL_FLOAT, GL_FALSE, sizeof(CustomVertex), (GLvoid *)(sizeof(float) * 4));
//绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 绘制第二个滤镜
//使用program
glUseProgram(_programHandle);
//绑定FrameBuffer
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
//绑定RenderBuffer
glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
//设置清屏颜色
glClearColor(1,0,0,1);
//清除颜色缓存区
glClear(GL_COLOR_BUFFER_BIT);
//设置视口
glViewport(0, 0, self.frame.size.width * self.contentScaleFactor, self.frame.size.height * self.contentScaleFactor);
//为当前的program指定uniform值
//纹理
glUniform1i(glViewUniforms[UNIFORM_INPUT_IMAGE_TEXTURE], 0);
//色温
glUniform1f(glViewUniforms[UNIFORM_TEMPERATURE], _temperature);
//顶点数据
glVertexAttribPointer(glViewAttributes[ATTRIBUTE_POSITION], 4, GL_FLOAT, GL_FALSE, sizeof(CustomVertex), 0);
//纹理数据 glVertexAttribPointer(glViewAttributes[ATTRIBUTE_INPUT_TEXTURE_COORDINATE], 2, GL_FLOAT, GL_FALSE, sizeof(CustomVertex), (GLvoid *)(sizeof(float) * 4));
//绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//要求本地窗口系统显示OpenGL ES渲染缓存绑定到RenderBuffer上
[_context presentRenderbuffer:GL_RENDERBUFFER];
GLSL 编写
1.顶点着色器
attribute vec4 position;
attribute vec2 inputTextureCoordinate;
varying lowp vec2 textureCoordinate;
voidmain(void) {
textureCoordinate = inputTextureCoordinate;
gl_Position = position;
}
2.饱和度着色器
varying lowp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float saturation;
const lowp vec3 warmFilter =vec3(0.93,0.54,0.0);
const mediump vec3 luminanceWeighting =vec3(0.2125,0.7154,0.0721);
voidmain()
{
lowp vec4 source =texture2D(inputImageTexture, textureCoordinate);
lowp float luminance =dot(source.rgb, luminanceWeighting);
lowp vec3 greyScaleColor =vec3(luminance,0,0);
gl_FragColor=vec4(mix(greyScaleColor, source.rgb, saturation), source.w);
}
3.色温着色器
varying lowp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp float temperature;
const lowp vec3 warmFilter =vec3(0.93,0.54,0.0);
const mediump mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.596, -0.274, -0.322, 0.212, -0.523, 0.311);
const mediump mat3 YIQtoRGB = mat3(1.0, 0.956, 0.621, 1.0, -0.272, -0.647, 1.0, -1.105, 1.702);
const mediump vec3 luminanceWeighting = vec3(0.2125,0.7154,0.0721);
void main()
{
lowp vec4 source = texture2D(inputImageTexture, textureCoordinate);
mediump vec3 yiq = RGBtoYIQ * source.rgb;
yiq.b = clamp(yiq.b,-0.5226,0.5226);
lowp vec3 rgb = YIQtoRGB * yiq;
lowp floatA = (rgb.r <0.5? (2.0* rgb.r * warmFilter.r) : (1.0-2.0* (1.0- rgb.r) * (1.0- warmFilter.r)));
lowp floatB = (rgb.g <0.5? (2.0* rgb.g * warmFilter.g) : (1.0-2.0* (1.0- rgb.g) * (1.0- warmFilter.g)));
lowp floatC = (rgb.b <0.5? (2.0* rgb.b * warmFilter.b) : (1.0-2.0* (1.0- rgb.b) * (1.0- warmFilter.b)));
lowp vec3 processed = vec3(A,B,C);
gl_FragColor = vec4(mix(rgb, processed, temperature), source.a);
}
网友评论