从0打造一个GPUImage(6-2)-GPUImage的多滤镜处理逻辑,关于上一章的补充

288 阅读2分钟
原文链接: zhuanlan.zhihu.com

上一章写完之后,由于懒,所以只是做到了从亮度 -> Screen这个过程的纹理传递。

下面很多留言说,想做 亮度 -> 对比度 -> Screen的过程一直失败。

这一小章节主要在代码补充一下这个过程。

代码地址在:https://github.com/zangqilong198812/YGCOpenGLESTutorial

对比度滤镜需要接收的纹理是亮度传递回来的

上一章我们说过了, 多个滤镜的处理关键点在于,每一个滤镜都独享一个framebuffer和一个空置的texture。
所以,不出意外的就是,想要在滤镜处理环节增加一个对比度。
那么我们也应该为对比度声明一个framebuffer和texture。
代码和亮度一样。

- (void)createSaturationFrameBuffer:(UIImage *)image {
    glGenFramebuffers(1, &_saturationFramebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _saturationFramebuffer);
    
    //Create the texture
    
    glGenTextures(1, &saturationTexture);
    glBindTexture(GL_TEXTURE_2D, saturationTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,  image.size.width, image.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    //Bind the texture to your FBO
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, saturationTexture, 0);
    
    //Test if everything failed
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if(status != GL_FRAMEBUFFER_COMPLETE) {
        printf("failed to make complete framebuffer object %x", status);
    }
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

在这里,我们看一下对比度这个filter的代码。

precision mediump float;
uniform sampler2D u_Texture;
varying vec2 v_TexCoordOut;
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
uniform float saturation;

void main(void) {
    vec4 color = texture2D(u_Texture, v_TexCoordOut);
    float lumiance = dot(color.rgb, W);
    vec3 grayScale = vec3(lumiance);
    gl_FragColor = vec4(mix(grayScale, color.rgb, saturation), color.w);
    
}

刨去saturation的原理不谈,我们这个滤镜其实和亮度滤镜唯一的不同点就是,我们需要调节的是saturatio这个uniform。其他的几乎和亮度一样。所以,为了获取saturation这个uniform的地址。

我们只需要在设置对比度shader的时候。这样获取就可以了。

- (void)setupSaturationShader {
     saturationShader = [[ZQLShaderCompiler alloc] initWithVertexShader:@"vertexShader.vsh" fragmentShader:@"Saturation.fsh"];
    [saturationShader prepareToDraw];
    _saturationPositionSlot = [saturationShader attributeIndex:@"a_Position"];
    _saturationTextureSlot = [saturationShader uniformIndex:@"u_Texture"];
    _saturationTextureCoordSlot = [saturationShader attributeIndex:@"a_TexCoordIn"];
    _saturation = [saturationShader uniformIndex:@"saturation"];
}

对比度滤镜唯一不同的地方在哪里呢?

其实就是,对比度滤镜需要接受的纹理,不是原图的纹理,因为在整个链条,他是处于在亮度滤镜处理完之后对亮度图片进行的对比度更改。所以,对比度滤镜接受的纹理应当是亮度纹理。处理逻辑如下。

- (IBAction)saturationValueChanged:(UISlider *)sender {
    glBindFramebuffer(GL_FRAMEBUFFER, _saturationFramebuffer);
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, (GLsizei)processImage.size.width, (GLsizei)processImage.size.height);
    
    // 使用对比度shader
    [saturationShader prepareToDraw];
    // 传递调节对比度的值区间 (0 - 2)
    glUniform1f(_saturation, sender.value);
    // 传递亮度纹理数据
    glActiveTexture(GL_TEXTURE5);
    glBindTexture(GL_TEXTURE_2D, brightnessTexture);
    glUniform1i(_saturationTextureSlot, 5);
    
    // 开始绘制
    [self drawSaturationRawImage];
    
    // 绘制纹理完毕,开始绘制到屏幕上
    
    [self renderToScreenWithTexture:saturationTexture];
}

注意这里glBindTexture我们传递的参数是brightnessTexture.所以,对比度滤镜拿到了亮度滤镜处理过的纹理,这样才实现了完整的流程。

效果如下