# GPUImage源码解读(二十四)

GPUImageLookupFilter 是GPUImage中的颜色查找滤镜，在一般的相机应用中使用得最广泛，它的作用是通过颜色变换从而产生出新风格的图片,在philm项目中也有大量使用

LUT （Lookup Tables）即查找表 。LUT是个非常简单的数值转换表，不同的色彩输入数值“映射”到一套输出数值，用来改变图像的色彩。例如：红色在LUT中可能被映射成蓝色，于是，应用LUT的图像中每个红的地方将由蓝色取代。不过，LUT的实际应用要比这种情况更微妙一些。LUT通常用来矫正域外色的问题。例如，一个以RGB色彩空间存储的图像要打印到纸上时，必须首先将文件转换为CMYK色彩空间。这可以用一个LUT将每个RGB色彩转换为等效的CMYK色彩，或者与域外色最接近的色彩（LUT还能够用缩放的方法在一定程度改变所有的色彩，因此可使图像在视觉上与原来相同）。 另外需要注意的一个问题是，尽管查找表经常效率很高，但是如果所替换的计算相当简单的话就会得不偿失，这不仅仅因为从内存中提取结果需要更多的时间，而且因为它增大了所需的内存并且破坏了高速缓存。如果查找表太大，那么几乎每次访问查找表都会导致高速缓存缺失，这在处理器速度超过内存速度的时候愈发成为一个问题.LUT查找表图可以使用 PS 生成.

在GPUImage中使用的是二维的LUT图片，它是一张512x512大小的图片。它由8x8个大正方形格子组成，每个大格子又是由64x64个小正方形组成。因此，LUT图片的宽度：8*64=512，高度：8*64=512。对于每个小正方形，它的宽度值即为R通道值，高度即为G通道值。由于R通道的范围为0\~255，因此，从宽度方面来看，每个小正方形的R通道的差为：256/64=4，R通道的集合为\[0，4，8，12，16，...，255]，同理从高度上看，每个小正方形的G通道的差为：256/64=4，G通道的集合为\[0，4，8，12，16，...，255]，由于RG通道值存放在每个64x64的大正方形中，那么B通道的值存放在哪里呢？答案就是在8x8=64个大正方形中，不难看出每个RGB通道都是用64个点来存放，占用空间为：64x64x64=262144。不同于RG通道的是，B通道存放时使用了宽度和高度分别为8个大正方形。 ![](https://1795314173-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LAdZ7gFz4IllRQpWbzu%2F-LAdZ8MxWrHaExfP4jSI%2F-LAdZXMi8_Eq0Wz5f8FT%2Fgpuimageyuan-ma-jie-8bfb28-er-shi-56db29_1.png?generation=1524337093941910\&alt=media)

LUT 排列图如下 ![](https://1795314173-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LAdZ7gFz4IllRQpWbzu%2F-LAdZ8MxWrHaExfP4jSI%2F-LAdZXN7poc3orLlrfsU%2Fgpuimageyuan-ma-jie-8bfb28-er-shi-56db29_2.png?generation=1524337093977920\&alt=media)

LUT生成代码

```
 Additional Info:
 Lookup texture is organised as 8x8 quads of 64x64 pixels representing all possible RGB colors:
 for (int by = 0; by < 8; by++) {
    for (int bx = 0; bx < 8; bx++) {
        for (int g = 0; g < 64; g++) {
            for (int r = 0; r < 64; r++) {
                image.setPixel(r + bx * 64, g + by * 64, qRgb((int)(r * 255.0 / 63.0 + 0.5),
                                                              (int)(g * 255.0 / 63.0 + 0.5),
                                                              (int)((bx + by * 8.0) * 255.0 / 63.0 + 0.5)));
            }
        }
    }
}
```

GPUImageLookupFilter 就是根据LUT获取相应的变换颜色，我们可以使用PS直接改变lookup颜色表，从而改变图片的风格。常用的PS工具有「曲线」，「饱和度」、「色彩平衡」、「可选颜色」，但是GPUImageLookupFilter的局限在于只能进行颜色上的调整（曲线，色彩平衡等），其它效果调整也只限于利用图层间混合模式的更改，例如做暗角、漏光等效果。GPUImageLookupFilter继承自GPUImageTwoInputFilter，接受LUT纹理和待滤镜的图片输入. GPUImageAmatorkaFilter, GPUImageMissEtikateFilter, and GPUImageSoftEleganceFilter它们的原理都是使用Color Lookup Tables，只是使用的纹理不同而已。 源码中主要有两着色器

* 两路输入的顶点着色器,在父类中

  ```
    NSString *const kGPUImageTwoInputTextureVertexShaderString = SHADER_STRING
   (
    attribute vec4 position;
    attribute vec4 inputTextureCoordinate;
    attribute vec4 inputTextureCoordinate2;

    varying vec2 textureCoordinate;
    varying vec2 textureCoordinate2;

    void main()
    {
        gl_Position = position;
        textureCoordinate = inputTextureCoordinate.xy;
        textureCoordinate2 = inputTextureCoordinate2.xy;
    }
   );
  ```
* 片元着色器 区分MAC & iphone环境

```
 NSString *const kGPUImageLookupFragmentShaderString = SHADER_STRING
(
 varying highp vec2 textureCoordinate;
 varying highp vec2 textureCoordinate2; // TODO: This is not used

 uniform sampler2D inputImageTexture;
 uniform sampler2D inputImageTexture2; // lookup texture

 void main()
 {
     highp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);

     highp float blueColor = textureColor.b * 63.0;

     // 根据B通道获取小正方形格子（64x64格子）
     highp vec2 quad1;
     quad1.y = floor(floor(blueColor) / 8.0);
     quad1.x = floor(blueColor) - (quad1.y * 8.0);

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

     // 根据小正方形格子和RG通道，获取纹理坐标，每个大格子的大小:1/8=0.125，每个小格子的大小:1/512
     highp vec2 texPos1;
     texPos1.x = (quad1.x * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.r);
     texPos1.y = (quad1.y * 0.125) + 0.5/512.0 + ((0.125 - 1.0/512.0) * textureColor.g);

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

     lowp vec4 newColor1 = texture2D(inputImageTexture2, texPos1);
     lowp vec4 newColor2 = texture2D(inputImageTexture2, texPos2);

     lowp vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
     gl_FragColor = vec4(newColor.rgb, textureColor.w);
 }
);
```
