css 中的 lut 是借助 3d 查找表对颜色做批量非线性映射,需通过 svg filter(同源内联、id 合法)或 png+js 实现,无法被原生 css 滤镜等价替代,且不支持直接解析 .cube 文件。

什么是 CSS 中的 LUT?
它不是真正的电影级调色,而是用 filter: url(#my-lut) 或 background-image: url(lut.png) 借助 3D 查找表(Look-Up Table)对颜色做批量映射。浏览器不原生支持 .cube 文件解析,必须转成可渲染格式——比如一张 64×64×64 的 PNG(实际是 64×64 网格,每格代表一个 RGB 输入对应的输出),或 SVG <fecomponenttransfer></fecomponenttransfer> 模拟查表逻辑。
用 filter: url() 加载 SVG LUT 的常见报错
典型错误是控制台报 Failed to load resource: net::ERR_FILE_NOT_FOUND 或 SecurityError: Failed to execute 'querySelector' on 'Document',根本原因是:SVG 必须同源内联或通过 <object></object> 引入,不能用外部 URL 直接引用 ID;且 SVG 中的 <filter></filter> 必须设 id,且不能含空格或特殊字符。
- 把 LUT SVG 写在当前 HTML 文件
<svg></svg>标签里(不要外链) -
<filter></filter>的id只能是字母、数字、下划线,例如id="film-grain-lut" - CSS 中引用必须带完整路径:如果 SVG 在页面底部,
filter: url(#film-grain-lut)才有效;放在里会失效 - Chrome 115+ 对 SVG filter 性能优化明显,但 Safari 仍可能丢帧,尤其叠加多个
filter
用 PNG LUT 实现更可控的颜色映射
这是目前最稳定的方式:把 3D LUT 压缩成一张 512×512 的 PNG(即 8×8 网格,每格 64×64 像素),再用 background-image + mix-blend-mode 或 Canvas 绘制。关键在于采样逻辑不能写死——不同 LUT 尺寸对应不同 UV 偏移。
- 标准 64³ LUT 转 PNG 后通常是 512×512,每个“单元格”为 64×64,R/G/B 分别映射到 X/Y/层级
- Canvas 中读取像素时,要按
floor(r * 63), floor(g * 63), floor(b * 63)计算索引,不是直接乘 255 - CSS 里无法直接解析 PNG LUT,必须配合 JS;纯 CSS 方案只能靠
filter: contrast() brightness() saturate()粗略模拟,和 LUT 无关 - WebGL 方案(如 three.js + ShaderMaterial)精度最高,但引入了额外依赖和上下文切换开销
LUT 和普通 CSS 滤镜的本质区别
filter: brightness(1.2) 是全局线性变换,而 LUT 是非线性逐像素映射——同一个蓝色值,在阴影区和高光区可能被映射成完全不同的新颜色。这意味着:LUT 无法用一组 CSS 滤镜参数等价替代;它依赖预计算图像或 SVG 数据,不可逆、不可调试、不可响应式微调。
立即学习“前端免费学习笔记(深入)”;
- 改一个色调,得重导整个 LUT 图像,不能像
hsl(200, 50%, 60%)那样实时调节 - 移动端 WebKit 对 SVG filter 支持不稳定,iOS 16.4 之前不支持
feComponentTransfer的 tableValues 属性 - 如果只想要胶片感,
sepia(.5) contrast(1.1) hue-rotate(-5deg)往往比硬套 LUT 更轻量、更可控
真正需要 LUT 的场景其实很窄:复刻某部电影的精确影调、做设计系统中的品牌色统一映射、或 WebGL 渲染管线延伸到 DOM 元素。多数网页动效,还是绕不开“看起来像”的权衡。










