Android OpenGL ES从入门到进阶(八)—— 万能的Lookup滤镜(颜色查找表)

源码:https://github.com/smzhldr/AGLFramework

一、前言

学习OpenGL,一定要学习Lookup滤镜,不光是因为其功能强大,还因为lookup滤镜涵盖OpenGL ES初级阶段的知识点比较全面,只要把lookup滤镜搞懂了,说明基础已经差不多了,也为后面进一步提高铺平了道路。在美颜相机类产品包括视频直播类项目诸如抖音、快手、唱吧、陌陌、Faceu激萌等都能见到诸如“柔和”,“温暖”,“复古”,“清新”,“黑白”…功能丰富的滤镜,其实都是用了lookup滤镜。看起来比较神奇,我们就一块揭开她的神秘面纱。

二、先看一张lookup滤镜的效果

左边是一张原图,右边是一张使用了某一种特效的lookup滤镜处理后的图片,看起来真有一中恋爱的感觉。

三、Lookup滤镜原理分析

1. lookup table颜色替换原理

lookup table(颜色查找表),简而言之就是通过将每一个原始的颜色进行转换之后成为一个新的颜色。打一个比方,比如原始颜色是红色(r:255,g:0,b:0),进行转换后变为绿色(r:0,g:255,b:0),以后所有是红色的地方都会被自动转换为绿色。

我们知道在RGB888色彩每个分量的有效值是[0,256],那么完整采样的色域空间为就256256256约为16M,意味着理论上约有16M种颜色可供调整。显然实际上并不会用到那么多的颜色,通常会通过列举节点来储存,而两个节点之间的颜色通过插值运算出来。

大多数情况下颜色查找表是由如下的图片生成的:

上图是一张原始图像的RGB颜色查找表,是一个二维图像,但RGB是一个三维元素,这其中有一个映射关系,将二维图像转换成三维三色表用来查询,首先看一下三维颜色模型,如下图:

上图中X,Y,Z三个方向分别和R,G,B色值对应
若果垂直于Z轴将这个颜色立方体切割好多次,每次切割的结果将如下图:

上面说了实际是用插值算法来算的,所以并不用切割无数次以表示精确,切割(2n)^2次确保可以组成一个边长偶数的正方形就可以了,一半来说切割64次组成一个8x8的正方形或者切割16次组成4x4的正方形,这中间设计性能和效果的折中。组合的打正方形就如文章最前面展示的8x8的原始颜色查找表一样,每个小正方形的x轴表示R分量,y轴表示G分享,B分量是通过小正方形在大正方形中的位置索引来算出来的,对应的也有64种,那么我们的三维颜色查找表就构建好了,只需要将原始图像的每一个像素的颜色值替换为颜色查找表中的值就可以了。

2. 颜色查找替换算法分析

varying highp vec2 textureCoordinate;

uniform sampler2D inputImageTexture;
uniform sampler2D lookupTexture; // lookup texture

uniform highp float lookupDimension; // 64
uniform lowp float intensity;

void main()
{
    highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
	
	//B通道上对应的数值
    highp float blueColor = textureColor.b * (lookupDimension - 1.0);
    highp float lookupDimensionSqrt = sqrt(lookupDimension);

   // 计算临近两个B通道所在的方形LUT单元格(从左到右从,上到下排列)
    highp vec2 quad1;
    quad1.y = floor(floor(blueColor) / lookupDimensionSqrt);
    quad1.x = floor(blueColor) - (quad1.y * lookupDimensionSqrt);

    highp vec2 quad2;
    quad2.y = floor(ceil(blueColor) / lookupDimensionSqrt);
    quad2.x = ceil(blueColor) - (quad2.y * lookupDimensionSqrt);

	//在对应的小正方形中查找原始图像当前像素锁对应的查找表中的位置
	//0.5是小正方形里的小正方形的位置取均值
    highp vec2 texPos1;
    texPos1.x = (quad1.x / lookupDimensionSqrt) + 0.5/lookupDimensionSqrt/lookupDimension + ((1.0/lookupDimensionSqrt - 1.0/lookupDimensionSqrt/lookupDimension) * textureColor.r);
    texPos1.y = (quad1.y / lookupDimensionSqrt) + 0.5/lookupDimensionSqrt/lookupDimension + ((1.0/lookupDimensionSqrt - 1.0/lookupDimensionSqrt/lookupDimension) * textureColor.g);

    highp vec2 texPos2;
    texPos2.x = (quad2.x / lookupDimensionSqrt) + 0.5/lookupDimensionSqrt/lookupDimension + ((1.0/lookupDimensionSqrt - 1.0/lookupDimensionSqrt/lookupDimension) * textureColor.r);
    texPos2.y = (quad2.y / lookupDimensionSqrt) + 0.5/lookupDimensionSqrt/lookupDimension + ((1.0/lookupDimensionSqrt - 1.0/lookupDimensionSqrt/lookupDimension) * textureColor.g);

	//根据当前像素查找到的相邻的两个小正方形的位置从纹理中取出形影的像素颜色值
    lowp vec4 newColor1 = texture2D(lookupTexture, texPos1);
    lowp vec4 newColor2 = texture2D(lookupTexture, texPos2);

	//根据蓝色通道的值对生成的相邻的两个新图像色值做加权
    lowp vec4 newColor = vec4(mix(newColor1.rgb, newColor2.rgb, fract(blueColor)), textureColor.w);
    //根据intensity控制效果程度
    gl_FragColor = mix(textureColor, newColor, intensity);
}

对着上面shader里的注释和上面讲到的原理,相信很容易就能看懂其中的道理了。

三、LookupFilter实现


public class LookupFilter extends AGLFilter {

    private int glUniIntensity;
    private int glUniLookupTexture;
    private float intensity = 1f;
    private Bitmap bitmap;
    private volatile boolean isNewFilter;


    public LookupFilter(Context context) {
        super(context, R.raw.lookup_f);
    }

    @Override
    protected void onInit() {
        glUniIntensity = GLES20.glGetUniformLocation(programId, "intensity");
        glUniLookupTexture = GLES20.glGetUniformLocation(programId, "lookupTexture");
    }

    @Override
    protected void onDrawArraysPre(Frame frame) {
        GLES20.glUniform1f(glUniIntensity, intensity);
        if (isNewFilter && bitmap != null) {
            int lookupTexture = OpenGlUtils.loadTexture(bitmap, OpenGlUtils.NO_TEXTURE);

            GLES20.glActiveTexture(GLES20.GL_TEXTURE4);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, lookupTexture);
            GLES20.glUniform1i(glUniLookupTexture, 4);
            isNewFilter = false;
        }
    }

    public void setIntensity(float intensity) {
        this.intensity = intensity;
    }

    public void setBitmap(@NonNull Bitmap bitmap) {
        if (!isNewFilter) {
            this.bitmap = bitmap;
            isNewFilter = true;
        }
    }
}

代码不过短短的几十行,却能实现如此神奇的效果,还能调节所需的颜色的比例。此Filter是基于前面章节封装号的框架写的,有了前面的章节的内容,此Filter的内容理解起来应该非常容易。

源码 里有我从网上收集的几十种颜色查找表的图片,利用现有的资源很容易做出小清新的滤镜

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
对于Unity中的颜色查找(Color Lookup Table)和富文本颜色,我可以为你提供一些基本信息。 1. 颜色查找(Color Lookup Table):颜色查找是一种用于调整图像颜色的技术。在Unity中,你可以使用LUT(纹理)来实现颜色查找。LUT是一个二维纹理,它包含了一系列的颜色映射。通过使用LUT,你可以将图像的颜色映射到新的颜色。 在Unity中,使用颜色查找可以通过以下步骤实现: - 创建一个LUT纹理,这可以是一个自定义的纹理或者是从外部资源导入的。 - 将LUT纹理应用到你想要调整颜色的材质上。 - 使用Shader编写一个自定义的颜色映射函数,将原始图像的颜色映射到LUT纹理上的颜色。 2. 富文本颜色:在Unity中,你可以使用富文本(Rich Text)来为文本添加样式,包括改变文本的颜色。富文本使用一些标记语法来实现文本样式的设置。 以下是一些常用的富文本标记语法来设置文本的颜色: - `<color>` 标签:使用该标签可以改变文本的颜色。例如,`<color=red>` 将文本设置为红色,`<color=#FF0000>` 将文本设置为十六进制颜色码为FF0000的颜色。 - `<b>` 和 `</b>` 标签:使用这对标签可以将文本设置为粗体。 - `<i>` 和 `</i>` 标签:使用这对标签可以将文本设置为斜体。 - `<size>` 和 `</size>` 标签:使用这对标签可以改变文本的大小。例如,`<size=20>` 将文本设置为大小为20。 你可以将这些标记语法与文本内容结合起来,来实现不同的富文本样式和颜色设置。 希望以上信息能对你有所帮助!如果你还有其他问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

[](){}

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值