Shader 中的颜色混合模式(Blend Mode)

栏目: 后端 · 发布时间: 8年前

内容简介:在之前的文章中提及了Shader 中的颜色计算,介绍了一些基本的颜色混合计算,然而在实际的 Shader 滤镜中,简单到加减乘除并不能很好地还原出我们想要的效果,回顾一下,平时拿到设计师提供的设计稿,都能看到他们在 Photoshop 中应用了大量的图层混合模式,图层混合模式给设计师提供了丰富的图层混合效果,大大减少他们对颜色的操作,更自然地混合不同图层。同样的,当我们希望通过 Shader 给图片增加不一样的滤镜效果时,图层混合模式将非常适用。

在之前的文章中提及了Shader 中的颜色计算,介绍了一些基本的颜色混合计算,然而在实际的 Shader 滤镜中,简单到加减乘除并不能很好地还原出我们想要的效果, mix() 也只是其中一个选择。

回顾一下,平时拿到设计师提供的设计稿,都能看到他们在 Photoshop 中应用了大量的图层混合模式,图层混合模式给设计师提供了丰富的图层混合效果,大大减少他们对颜色的操作,更自然地混合不同图层。

Shader 中的颜色混合模式(Blend Mode)

同样的,当我们希望通过 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/ (图片用的不好,不太好看出效果)

Shader 中的颜色混合模式(Blend Mode)

二、使用

让人欣喜的是,我们不用重复的去实现上面的逻辑了,这个 Github 库已经帮我们实现了大部分的混合模式: github.com/jamieowen/g… Shader 都可以直接看到:

Shader 中的颜色混合模式(Blend Mode)

什么情况下需要图层混合模式?下面举个抖音的例子,可以看到这个视频下面有一个叫做「霓虹」的特效:

Shader 中的颜色混合模式(Blend Mode)

实际应用到效果是这样的:

Shader 中的颜色混合模式(Blend Mode)

那我们可以怎么来实现呢?首先实现出一个霓虹的效果来,简单来说就是一个边缘羽化的圆形,如下所示:

// 封装了一个函数
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.);
}
复制代码

效果如下:

Shader 中的颜色混合模式(Blend Mode)

这个时候,这个效果可以理解为 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.);
}
复制代码
Shader 中的颜色混合模式(Blend Mode)

看样子还行啊,但是如果 Base Layer 是白色背景,则会出现一些问题:

Shader 中的颜色混合模式(Blend Mode)

由于使用了加法,符合 加色系 的规则,颜色的混合最终会往最亮的颜色靠拢,当任何颜色跟一个趋近于白色底相加,都会越来越亮,失去了原来的滤镜颜色。那加法不能同时适用于暗色底和白色底。乘法呢?如果要用乘法,首先必须把 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.);
}
复制代码
Shader 中的颜色混合模式(Blend Mode)

然后使用乘法(虽然不太好看,但好歹是上了色):

Shader 中的颜色混合模式(Blend Mode)

再看看暗色底是什么表现:

Shader 中的颜色混合模式(Blend Mode)

简单来说,通过乘法计算颜色实际上是 减色系 的操作。颜色的混合只会越来越暗,滤镜并不能带来更鲜活的表现。所以不管是加法或是乘法,都没有办法将滤镜和底图很好的融合起来。所以这个时候,图层混合模式才这么重要。

当设计师拿到 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.);
}
复制代码
Shader 中的颜色混合模式(Blend Mode)

这个效果是符合预期的。实际上我们在抖音上得到的「霓虹」效果也是这样:浅色底效果较不明显,而深色底较明显。所以可以简单到猜测:抖音的这个特效同样是通过滤色的方式来添加霓虹效果。

Shader 中的颜色混合模式(Blend Mode)

三、适用场景

上面通过一个简单的案例来说明了混合模式的使用,实际上通过这些图层混合模式,我们可以得到一批能直接上线的滤镜效果了。然而并非所有情况都适合图层混合模式,比如在转场上, mix() 会更适合于两个图层之间的过渡融合。

图层混合模式更适合于那种 局部效果+背景纯色 需要应用在具体图像上的情况。比如上面是一个案例,另外一个案例就是漏光(Light Leak):

Shader 中的颜色混合模式(Blend Mode)

或者镜头光晕(Lens flare):

Shader 中的颜色混合模式(Blend Mode)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

Practical Django Projects, Second Edition

Practical Django Projects, Second Edition

James Bennett / Apress / 2009 / 44.99

Build a django content management system, blog, and social networking site with James Bennett as he introduces version 1.1 of the popular Django framework. You’ll work through the development of ea......一起来看看 《Practical Django Projects, Second Edition》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具