内容简介:在之前的文章中提及了Shader 中的颜色计算,介绍了一些基本的颜色混合计算,然而在实际的 Shader 滤镜中,简单到加减乘除并不能很好地还原出我们想要的效果,回顾一下,平时拿到设计师提供的设计稿,都能看到他们在 Photoshop 中应用了大量的图层混合模式,图层混合模式给设计师提供了丰富的图层混合效果,大大减少他们对颜色的操作,更自然地混合不同图层。同样的,当我们希望通过 Shader 给图片增加不一样的滤镜效果时,图层混合模式将非常适用。
在之前的文章中提及了Shader 中的颜色计算,介绍了一些基本的颜色混合计算,然而在实际的 Shader 滤镜中,简单到加减乘除并不能很好地还原出我们想要的效果, mix()
也只是其中一个选择。
回顾一下,平时拿到设计师提供的设计稿,都能看到他们在 Photoshop 中应用了大量的图层混合模式,图层混合模式给设计师提供了丰富的图层混合效果,大大减少他们对颜色的操作,更自然地混合不同图层。
同样的,当我们希望通过 Shader 给图片增加不一样的滤镜效果时,图层混合模式将非常适用。
一、定义
首先我们先定义下什么是混合模式:
混合模式是图像处理技术中的一个技术名词,不仅用于广泛使用的 Photoshop 中,也应用于 After Effect、illustrator、 Dreamweaver、Fireworks 等软件。主要功效是可以用不同的方法将对象颜色与底层对象的颜色混合。当您将一种混合模式应用于某一对象时,在此对象的图层或组下方的任何对象上都可看到混合模式的效果。 —— 百度百科
Adobe 也专门介绍了混合模式的相关知识: helpx.adobe.com/cn/photosho…
我们简单罗列下不同的混合模式:
- 正常(Normal) :编辑或绘制每个像素,使其成为结果色。这是默认模式。(在处理位图图像或索引颜色图像时,“正常”模式也称为阈值。)
- 溶解(Dissolve) :编辑或绘制每个像素,使其成为结果色。但是,根据任何像素位置的不透明度,结果色由基色或混合色的像素随机替换。
- 变暗(Darken) :查看每个通道中的颜色信息,并选择基色或混合色中较暗的颜色作为结果色。将替换比混合色亮的像素,而比混合色暗的像素保持不变。
- 正片叠底(Multiply) :查看每个通道中的颜色信息,并将基色与混合色进行正片叠底。结果色总是较暗的颜色。任何颜色与黑色正片叠底产生黑色。任何颜色与白色正片叠底保持不变。当您用黑色或白色以外的颜色绘画时,绘画 工具 绘制的连续描边产生逐渐变暗的颜色。这与使用多个标记笔在图像上绘图的效果相似。
- 颜色加深(Color Burn) :查看每个通道中的颜色信息,并通过增加二者之间的对比度使基色变暗以反映出混合色。与白色混合后不产生变化。
- 线性加深(Linear Burn) :查看每个通道中的颜色信息,并通过减小亮度使基色变暗以反映混合色。与白色混合后不产生变化。
- 深色(Darker Color) :比较混合色和基色的所有通道值的总和并显示值较小的颜色。“深色”不会生成第三种颜色(可以通过“变暗”混合获得),因为它将从基色和混合色中选取最小的通道值来创建结果色。
- 变亮(Lighten) :查看每个通道中的颜色信息,并选择基色或混合色中较亮的颜色作为结果色。比混合色暗的像素被替换,比混合色亮的像素保持不变。
- 滤色(Screen) :查看每个通道的颜色信息,并将混合色的互补色与基色进行正片叠底。结果色总是较亮的颜色。用黑色过滤时颜色保持不变。用白色过滤将产生白色。此效果类似于多个摄影幻灯片在彼此之上投影。
- 颜色减淡(Color Dodge) :查看每个通道中的颜色信息,并通过减小二者之间的对比度使基色变亮以反映出混合色。与黑色混合则不发生变化。
- 线性减淡/添加(Linear Dodge) :查看每个通道中的颜色信息,并通过增加亮度使基色变亮以反映混合色。与黑色混合则不发生变化。
- 浅色(Lighter Color) :比较混合色和基色的所有通道值的总和并显示值较大的颜色。“浅色”不会生成第三种颜色(可以通过“变亮”混合获得),因为它将从基色和混合色中选取最大的通道值来创建结果色。
- 叠加(Overlay) :对颜色进行正片叠底或过滤,具体取决于基色。图案或颜色在现有像素上叠加,同时保留基色的明暗对比。不替换基色,但基色与混合色相混以反映原色的亮度或暗度。
- 柔光(Soft Light) :使颜色变暗或变亮,具体取决于混合色。此效果与发散的聚光灯照在图像上相似。如果混合色(光源)比 50% 灰色亮,则图像变亮,就像被减淡了一样。如果混合色(光源)比 50% 灰色暗,则图像变暗,就像被加深了一样。使用纯黑色或纯白色上色,可以产生明显变暗或变亮的区域,但不能生成纯黑色或纯白色。
- 强光(Hard Light) :对颜色进行正片叠底或过滤,具体取决于混合色。此效果与耀眼的聚光灯照在图像上相似。如果混合色(光源)比 50% 灰色亮,则图像变亮,就像过滤后的效果。这对于向图像添加高光非常有用。如果混合色(光源)比 50% 灰色暗,则图像变暗,就像正片叠底后的效果。这对于向图像添加阴影非常有用。用纯黑色或纯白色上色会产生纯黑色或纯白色。
- 亮光(Vivid Light) :通过增加或减小对比度来加深或减淡颜色,具体取决于混合色。如果混合色(光源)比 50% 灰色亮,则通过减小对比度使图像变亮。如果混合色比 50% 灰色暗,则通过增加对比度使图像变暗。
- 线性光(Linear Light) :通过减小或增加亮度来加深或减淡颜色,具体取决于混合色。如果混合色(光源)比 50% 灰色亮,则通过增加亮度使图像变亮。如果混合色比 50% 灰色暗,则通过减小亮度使图像变暗。
- 点光(Pin Light) :根据混合色替换颜色。如果混合色(光源)比 50% 灰色亮,则替换比混合色暗的像素,而不改变比混合色亮的像素。如果混合色比 50% 灰色暗,则替换比混合色亮的像素,而比混合色暗的像素保持不变。这对于向图像添加特殊效果非常有用。
- 实色混合(Hard Mix) :将混合颜色的红色、绿色和蓝色通道值添加到基色的 RGB 值。如果通道的结果总和大于或等于 255,则值为 255;如果小于 255,则值为 0。因此,所有混合像素的红色、绿色和蓝色通道值要么是 0,要么是 255。此模式会将所有像素更改为主要的加色(红色、绿色或蓝色)、白色或黑色。
- 差值(Difference) :查看每个通道中的颜色信息,并从基色中减去混合色,或从混合色中减去基色,具体取决于哪一个颜色的亮度值更大。与白色混合将反转基色值;与黑色混合则不产生变化。
- 排除(Exclusion) :创建一种与“差值”模式相似但对比度更低的效果。与白色混合将反转基色值。与黑色混合则不发生变化。
- 减去(Subtract) :查看每个通道中的颜色信息,并从基色中减去混合色。在 8 位和 16 位图像中,任何生成的负片值都会剪切为零。
- 划分(Divide) :查看每个通道中的颜色信息,并从基色中划分混合色。
- 色相(Hue) :用基色的明亮度和饱和度以及混合色的色相创建结果色。
- 饱和度(Saturation) :用基色的明亮度和色相以及混合色的饱和度创建结果色。在无 (0) 饱和度(灰度)区域上用此模式绘画不会产生任何变化。
- 颜色(Color) :用基色的明亮度以及混合色的色相和饱和度创建结果色。这样可以保留图像中的灰阶,并且对于给单色图像上色和给彩色图像着色都会非常有用。
- 明度(Luminosity) :用基色的色相和饱和度以及混合色的明亮度创建结果色。此模式创建与“颜色”模式相反的效果。
相关的计算公式,也可以直接通过这个在线地址查看混合效果: jamieowen.github.io/glsl-blend/ (图片用的不好,不太好看出效果)
二、使用
让人欣喜的是,我们不用重复的去实现上面的逻辑了,这个 Github 库已经帮我们实现了大部分的混合模式: github.com/jamieowen/g… Shader 都可以直接看到:
什么情况下需要图层混合模式?下面举个抖音的例子,可以看到这个视频下面有一个叫做「霓虹」的特效:
实际应用到效果是这样的:
那我们可以怎么来实现呢?首先实现出一个霓虹的效果来,简单来说就是一个边缘羽化的圆形,如下所示:
// 封装了一个函数 vec3 drawLeaks(vec2 _uv, vec2 position, vec2 speed, vec2 size, vec3 resolution, vec3 color, float t, vec2 range) { vec2 leakst = _uv; vec2 newsize = normalize(size); newsize /= abs(newsize.x) + abs(newsize.y); leakst -= .5; // 坐标系居中 leakst.x *= resolution.x/resolution.y; // 等比例缩放 leakst.x -= position.x; // 位置调整x leakst.y -= position.y; // 位置调整y leakst.x -= speed.x * t * 10.; // 运动速率x leakst.y -= speed.y * t * 10.; // 运动速率y if (newsize.x < newsize.y) // 大小比例调整 leakst.y *= newsize.x / newsize.y; if (newsize.x > newsize.y) leakst.x *= newsize.y / newsize.x; float angle = atan(leakst.y, leakst.x); // 笛卡尔转极坐标 float radius = length(leakst); vec3 finalColor = vec3(smoothstep(range.x, range.y, radius))*color*(1.-t); // 预设size&上色 return finalColor; } void main() { vec3 leakColor = drawLeaks(myst, vec2(.0, .0), vec2(.0, .0), vec2(.0, .0), iResolution, vec3(166./255., 66./255., 65./255.)*1.5, 0., vec2(.3, 0.)); gl_FragColor = vec4(leakColor, 1.); } 复制代码
效果如下:
这个时候,这个效果可以理解为 PS 中的一个「图层」,我们把它叫做混合层(Blend Layer),然后我们需要增加一个基础层(Base Layer)用于混合,我们先试试 加法 :
// 这里只展示主要代码 void main() { vec3 leakColor = drawLeaks(myst, vec2(.0, .0), vec2(.0, .0), vec2(.0, .0), iResolution, vec3(166./255., 66./255., 65./255.)*1.5, 0., vec2(.3, 0.)); vec3 texelColor = texture2D(texure, myst).rgb; gl_FragColor = vec4(leakColor + texelColor, 1.); } 复制代码
看样子还行啊,但是如果 Base Layer 是白色背景,则会出现一些问题:
由于使用了加法,符合 加色系 的规则,颜色的混合最终会往最亮的颜色靠拢,当任何颜色跟一个趋近于白色底相加,都会越来越亮,失去了原来的滤镜颜色。那加法不能同时适用于暗色底和白色底。乘法呢?如果要用乘法,首先必须把 Blend Layer 改成白色底(否则任何颜色和黑色底相乘都是黑色):
void main() { vec3 leakColor = drawLeaks(myst, vec2(.0, .0), vec2(.0, .0), vec2(.0, .0), iResolution, vec3((255.-255.)/255., (255.-95.)/255., (255.-32.)/255.), 0., vec2(.3, 0.)); gl_FragColor = vec4(leakColor, 1.); } 复制代码
然后使用乘法(虽然不太好看,但好歹是上了色):
再看看暗色底是什么表现:
简单来说,通过乘法计算颜色实际上是 减色系 的操作。颜色的混合只会越来越暗,滤镜并不能带来更鲜活的表现。所以不管是加法或是乘法,都没有办法将滤镜和底图很好的融合起来。所以这个时候,图层混合模式才这么重要。
当设计师拿到 Blend Layer 和 Base Layer,他们会毫不犹豫地给两者添加一个「 滤色 」的混合模式,为什么他们会这么做,往往是源于对这个混合模式的了解和日常实践,作为工程师,达不到他们的第六感,我们不妨看看「滤色」的定义:
查看每个通道的颜色信息,并将混合色的互补色与基色进行正片叠底。结果色总是较亮的颜色。用黑色过滤时颜色保持不变。用白色过滤将产生白色。此效果类似于多个摄影幻灯片在彼此之上投影。
滤色的实现是这样的:
float blendScreen(float base, float blend) { return 1.0-((1.0-base)*(1.0-blend)); } 复制代码
简单来说,滤色就是把两个图层中较暗的颜色去掉,取较亮的颜色。我们不妨试试:
float blendScreen(float base, float blend) { return 1.0-((1.0-base)*(1.0-blend)); } vec3 blendScreen(vec3 base, vec3 blend) { return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b)); } void main() { vec3 leakColor = drawLeaks(myst, vec2(.0, .0), vec2(.0, .0), vec2(.0, .0), iResolution, vec3((255.-255.)/255., (255.-95.)/255., (255.-32.)/255.), 0., vec2(.3, 0.)); vec3 texelColor = texture2D(texture, myst).rgb; gl_FragColor = vec4(blendScreen(texelColor, leakColor), 1.); } 复制代码
这个效果是符合预期的。实际上我们在抖音上得到的「霓虹」效果也是这样:浅色底效果较不明显,而深色底较明显。所以可以简单到猜测:抖音的这个特效同样是通过滤色的方式来添加霓虹效果。
三、适用场景
上面通过一个简单的案例来说明了混合模式的使用,实际上通过这些图层混合模式,我们可以得到一批能直接上线的滤镜效果了。然而并非所有情况都适合图层混合模式,比如在转场上, mix()
会更适合于两个图层之间的过渡融合。
图层混合模式更适合于那种 局部效果+背景纯色 需要应用在具体图像上的情况。比如上面是一个案例,另外一个案例就是漏光(Light Leak):
或者镜头光晕(Lens flare):
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- OpenGL 中的颜色混合和使用
- CSS教程:图片使用混合模式和颜色叠加filter滤镜,改变PNG图标颜色
- OpenGL ES入门: 渲染金字塔 - 颜色、纹理、纹理与颜色混合填充以及GLKit实现
- 颜色搭配及颜色科学
- WebGL 纹理颜色原理
- 【Leetcode】75.颜色分类
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Clojure编程
Chas Emerick、Brian Carper、Christophe Grand / 徐明明、杨寿勋 / 电子工业出版社 / 2013-3-26 / 99.00元
Clojure是一种实用的通用语言,它是传奇语言LISP的方言,可与Ruby、Python等动态语言相媲美,更以无缝Java库、服务,以及拥有JVM系统得天独厚的资源优势而胜出。本书既可以用来熟悉Clojure基础知识与常见例子,也可了解其相关的实践领域与话题,更可以看到这一JVM平台上的LISP如何帮助消除不必要的复杂性,为大家在编程实践中解决最具挑战性的问题开辟新的选择——更具灵活性,更适于W......一起来看看 《Clojure编程》 这本书的介绍吧!