美文网首页
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