这篇文章发布于 2023年11月22日,星期三,00:14,归类于 Graphic相关, JS实例。 阅读 10826 次, 今日 19 次 一条评论
by zhangxinxu from https://www.zhangxinxu.com/wordpress/?p=11060 鑫空间-鑫生活
本文欢迎分享与聚合,全文转载就不必了,尊重版权,圈子就这么大,若急用可以联系授权。
一、关于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尺寸大小):
需要注意的是,在通常的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的语法
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滤镜。
里面是一戳文本,内容如下:
所以,有必要先将文本格式的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
可以得到如下图所示的效果。
速度还是挺快的,webGL渲染果然比2d context速度快多了。
五、结语
在pixi众多滤镜中看到了DisplacementFilter滤镜,这个其实以前介绍过类似的,不过是SVG的相关滤镜,可以实现元素的扭曲效果,有兴趣可以了解下:“深入理解SVG feDisplacementMap滤镜及实际应用”
其实,大家只要在Web视觉表现浸染过了,就会发现所谓的滤镜啊,特效啊,都是一个路子下来的,无论是CSS的、SVG,2D canvas或者WebGL,一通百通。
恩……SVG滤镜里面还有几个滤镜元素我还不是很熟悉,我觉得年底前可以抽时间研究下,不留学习漏洞,等全部都通透了,那就是SVG滤镜高手,而前端从业者这么多,SVG滤镜高手寥寥无几,岂不是优势在我了?
本文为原创文章,欢迎分享,勿全文转载,如果实在喜欢,可收藏,永不过期,且会及时更新知识点及修正错误,阅读体验也更好。
本文地址:https://www.zhangxinxu.com/wordpress/?p=11060
(本篇完)
- 3D LUT 滤镜颜色映射原理剖析与JS实现 (0.563)
- 剪映APP的视频特效如何在Web中JS实现 (0.384)
- SVG feTurbulence滤镜深入介绍 (0.286)
- 深入理解SVG feDisplacementMap滤镜及实际应用 (0.265)
- 用3D LUT滤镜我做了个在线专业电影级别照片调色工具 (0.227)
- canvas实现iPhoneX炫彩壁纸屏保外加pixi.js流体动效 (0.199)
- mp4box.js加WebCodecs 解码MP4视频帧并渲染 (0.178)
- CSS实现跨浏览器的box-shadow盒阴影效果(2) (0.048)
- 小tip: 使用CSS将图片转换成黑白(灰色、置灰) (0.048)
- 使用CSS将图片转换成模糊(毛玻璃)效果 (0.048)
- HTML5 Canvas,WebGL,CSS Shaders,GLSL的暧昧关系 (RANDOM - 0.027)
厉害厉害୧(๑•̀◡•́๑)૭