美文网首页
OpenGL ES 实现图片的饱和度,色温调节

OpenGL ES 实现图片的饱和度,色温调节

作者: iOS小肖 | 来源:发表于2018-11-29 16:30 被阅读0次

前言

这里是一篇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);

}

github地址

相关文章

网友评论

      本文标题:OpenGL ES 实现图片的饱和度,色温调节

      本文链接:https://www.haomeiwen.com/subject/zdkfcqtx.html