IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    cube格式的LUT滤镜也叫ColorMapFilter在pixi中应用

    张 鑫旭发表于 2023-11-21 16:14:41
    love 0

    by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11060 鑫空间-鑫生活
    本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。

    cube文件示意图

    一、关于LUT滤镜

    接上篇文章,讲讲各类视频剪辑APP中滤镜效果在Web中的实现。

    剪映中的滤镜

    这种滤镜其实也分为两大类,一类是算法滤镜,例如高斯模糊,反相,色调变化,本质上是数学计算,例如pixijs/filters这个项目中的ColorMatrixFilter类效果就是通过矩阵变换计算实现的。

    这类滤镜的算法固定,使用简单,但是效果单一,不够丰富。

    还有一类就是本文要介绍的颜色映射滤镜,也叫LUT滤镜,LUT是Look Up Table的缩写,中文叫查找表,也叫颜色映射表,在pixijs/filters这个项目中使用ColorMapFilter方法实现,这类滤镜效果丰富,细节细腻,可以实现工业电影一般的视觉质感。

    LUT滤镜多以.cube或.3dl后缀结尾,有文本和图片两种形式,互相是可以转换的。

    文本格式的LUT滤镜文件内容如下:

    # Created by Adobe Photoshop CC 2019.0.1
    TITLE "Adobe Photoshop CC 2019.0.1"
    LUT_3D_SIZE 33
    0.000000 0.000000 0.000000
    0.000000 0.000000 0.000000
    ...
    

    包含标题、尺寸和详细的颜色映射信息。

    图片格式的LUT滤镜文件多表现为渐变彩色色块,有方的,横的,还有竖的,例如(17*17*17尺寸大小):

    LUT滤镜图片示意

    需要注意的是,在通常的WebGL JS库中,都是使用横向的LUT滤镜图片,例如pixijs/filters项目中的ColorMapFilter类,使用的就是横向的LUT滤镜图片。

    关于LUT滤镜更多的知识可以参考我4年前写的这篇文章:“3D LUT 滤镜颜色映射原理剖析与JS实现”。

    二、LUT滤镜在pixi中的应用

    说到pixi.js中的滤镜效果,不得不提一下官方这个项目:https://github.com/pixijs/filters

    LUT滤镜本质上就是颜色映射,英文称为 color map,因此,需要使用其提供的ColorMapFilter类。

    使用示意如下:

    import {ColorMapFilter} from '@pixi/filter-color-map';
    import {Container} from 'pixi.js';
    
    const container = new Container();
    const colorMap = new Image();
    colorMap.src = 'foo/bar/colorMap.png';
    
    container.filters = [new ColorMapFilter(colorMap)];

    如果是希望直联使用,可以参考:

    <script src="./pixi.js"></script>
    <script src="./pixi-filters.js"></script>
    <script>
    const container = new PIXI.Container();
    const colorMap = new Image();
    colorMap.src = 'foo/bar/colorMap.png';
    
    container.filters = [new PIXI.filters.ColorMapFilter(colorMap)];
    </script>

    不过支持直联用法的pixi-filters.js的版本好多年没更新了,需要使用旧版的pixi.js,可能会有问题,所以,推荐使用ESM版本的。

    <script src="https://cdn.jsdelivr.net/npm/pixi.js@7.x/dist/pixi.min.js"></script>
    <script type="module">
        import { ColorMapFilter } from 'https://cdn.jsdelivr.net/npm/@pixi/filter-color-map@5.1.1/+esm'
        ...
    </script>

    ColorMapFilter的语法

    ColorMapFilter类的语法如下:

    new PIXI.filters.ColorMapFilter(colorMap: HTMLImageElement | HTMLCanvasElement | PIXI.BaseTexture | PIXI.Texture, nearest: boolean, mix: number) → {}

    其中:

    colorMap
    颜色映射的图像元素,图片元素,canvas元素或者PIXI的纹理都可以。
    nearest
    是否对colorMap纹理使用NEAREST。
    mix
    颜色映射使用的百分率,范围是0~1,0就是原图。

    从上面的语法可以看出,在PIXI中,只支持图像类型的LUT素材的渲染,然而,很多时候,我们已有的素材都是文本格式的素材,一堆.cube后缀的文件,我从老文件夹里找了个示意素材,喏,Candlelight.cube滤镜。

    里面是一戳文本,内容如下:

    4828行颜色示意

    所以,有必要先将文本格式的LUT滤镜转换为图片格式的LUT滤镜才行。

    三、Cube文本LUT转图片

    cube里面的文本内容,其实就是一堆颜色值,每行三个,分别是RGB三个通道的值,范围是0~1,例如:

    0.000000 0.000000 0.000000
    0.000000 0.000000 0.000000
    0.000000 0.000000 0.000000

    就表示连续三个像素点都是纯黑色。

    因此,我们只需要将这些内容转换成RGB色值,然后在Canvas元素上进行绘制就可以了。

    于是,首先第一步,我们需要解析cube文件,得到颜色映射图片的尺寸和所有的色值,使用此方法即可,参考自这个Github项目。

    // 解析cube文件的方法
    const parseCubeLUT = function (str) {
        if (typeof str !== 'string') {
            str = str.toString();
        }
    
        var title = null;
        var type = null;
        var size = 0;
        var domain = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]];
        var data = [];
    
        var lines = str.split('\n');
    
        for (var i=0; i<lines.length; i++) {
            var line = lines[i].trim();
    
            if(line[0] === '#' || line === '') {
                // Skip comments and empty lines
                continue;
            }
    
            var parts = line.split(/\s+/);
    
            switch(parts[0]) {
                case 'TITLE':
                    title = line.slice(7, -1);
                    break;
                case 'DOMAIN_MIN':
                    domain[0] = parts.slice(1).map(Number);
                    break;
                case 'DOMAIN_MAX':
                    domain[1] = parts.slice(1).map(Number);
                    break;
                case 'LUT_1D_SIZE':
                    type = '1D';
                    size = Number(parts[1]);
                    break;
                case 'LUT_3D_SIZE':
                    type = '3D';
                    size = Number(parts[1]);
                    break;
                default:
                    data.push(parts.map(Number));
            }
        }
    
        return {
            title: title,
            type: type,
            size: size,
            domain: domain,
            data: data
        };
    }

    然后,我们需要将这些色值转换为图片,这里使用Canvas元素,代码如下:

    // 将色值转换为图片
    const lut2Img = function (lutData) {
        const { size, data } = lutData;
    
        // 根据size创建canvas元素
        const canvas = document.createElement('canvas');
        canvas.width = size * size;
        canvas.height = size;
    
        // 绘制在Canvas上
        const context = canvas.getContext('2d');
        const imagedata = context.createImageData(canvas.width, canvas.height);
        // 给对应坐标位置的数据设置色值为绿色
        let startX = 0;
        let startY = 0;
        for (var x = 0; x < data.length; x++) {
            // 垂直计算位置
            startY = Math.floor(x / size) % size;
            startX = x % size + size * Math.floor(Math.floor(x / size) / size);
    
            // index 计算
            var index = 4 * (startY * size * size + startX);
    
            imagedata.data[index] = data[x][0] * 255;
            imagedata.data[index + 1] = data[x][1] * 255;
            imagedata.data[index + 2] = data[x][2] * 255;
            imagedata.data[index + 3] = 255;
        }
        // 再重绘
        context.putImageData(imagedata, 0, 0);
    
        return canvas;
    }

    这里有个很多人都会犯错的细节,那就是默认情况下的cube颜色色值是按照垂直宫格呈现的(见下图),而PIXI中需要的颜色映射图形需要横排,所以如果我们单纯一个for循环执行,会发现最终渲染的色彩滤镜效果和真实的cube滤镜渲染效果是有较大偏差的。

    竖排示意

    因此,在for循环的时候,需要精准计算每行色值的位置。

    最后,我们将上面两个方法结合起来,就可以实现cube文本转图片的功能了。

    有个映射图片,滤镜效果自然不在话下了。

    四、眼见为实,demo演示

    OK,进入大家最关心最需要的部分,演示和源码,这里使用Candlelight.cube这个滤镜示意。

    您可以狠狠地点击这里:pixi.js实现lut颜色映射滤镜demo

    可以得到如下图所示的效果。

    demo滤镜效果示意

    速度还是挺快的,webGL渲染果然比2d context速度快多了。

    五、结语

    在pixi众多滤镜中看到了DisplacementFilter滤镜,这个其实以前介绍过类似的,不过是SVG的相关滤镜,可以实现元素的扭曲效果,有兴趣可以了解下:“深入理解SVG feDisplacementMap滤镜及实际应用”

    其实,大家只要在Web视觉表现浸染过了,就会发现所谓的滤镜啊,特效啊,都是一个路子下来的,无论是CSS的、SVG,2D canvas或者WebGL,一通百通。

    恩……SVG滤镜里面还有几个滤镜元素我还不是很熟悉,我觉得年底前可以抽时间研究下,不留学习漏洞,等全部都通透了,那就是SVG滤镜高手,而前端从业者这么多,SVG滤镜高手寥寥无几,岂不是优势在我了?

    优势在我

    本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
    本文地址:https://www.zhangxinxu.com/wordpress/?p=11060

    (本篇完)



沪ICP备19023445号-2号
友情链接