Figma 混合终极指南 GLSL

The Ultimate Guide to Figma Blending Modes in GLSL

May 09, 25

混合模式公式

Blending Equations

No.NameColor ComponentAlpha ComponentUsage
1NormalCs+Cd(1as)C_s + C_d \cdot (1 - a_s)as+ad(1as)a_s + a_d \cdot (1 - a_s)
2DarkenCs+Cdmax(Csad, Cdas)C_s + C_d - max(C_s\cdot a_d,\ C_d\cdot a_s)as+ad(1as)a_s + a_d \cdot (1 - a_s)
3MultiplyCsCd+Cs(1ad)+Cd(1as)C_s \cdot C_d + C_s \cdot (1 - a_d) + C_d \cdot (1 - a_s)as+ad(1as)a_s + a_d \cdot (1 - a_s)
4Plus Darkermax(0,a(adCd)(asCs))max(0, a - (a_d - C_d) - (a_s - C_s))as+ad(1as)a_s + a_d \cdot (1 - a_s)
5Color BurnComplex component-wise calculationas+ad(1as)a_s + a_d \cdot (1 - a_s)
6LightenCs+Cdmin(Csad,Cdas)C_s + C_d - min(C_s \cdot a_d, C_d \cdot a_s)as+ad(1as)a_s + a_d \cdot (1 - a_s)
7Screen1(1Cs)(1Cd)1 - (1 - C_s) \cdot (1 - C_d)as+ad(1as)a_s + a_d \cdot (1 - a_s)
8Plus Lightermin(1,Cs+Cd)min(1, C_s + C_d)as+ad(1as)a_s + a_d \cdot (1 - a_s)
9Color DodgeComplex component-wise calculationas+ad(1as)a_s + a_d \cdot (1 - a_s)
10OverlayComponent-wise conditional calculationas+ad(1as)a_s + a_d \cdot (1 - a_s)
11Soft LightComplex component-wise calculationas+ad(1as)a_s + a_d \cdot (1 - a_s)
12Hard LightSame as Overlay but with swapped inputsas+ad(1as)a_s + a_d \cdot (1 - a_s)
13DifferenceCs+Cd2min(Csad,Cdas)C_s + C_d - 2 \cdot min(C_s \cdot a_d, C_d \cdot a_s)as+ad(1as)a_s + a_d \cdot (1 - a_s)
14ExclusionCs+Cd2CsCdC_s + C_d - 2 \cdot C_s \cdot C_das+ad(1as)a_s + a_d \cdot (1 - a_s)
15HueHSL-based calculationas+adasada_s + a_d - a_s \cdot a_d
16SaturationHSL-based calculationas+adasada_s + a_d - a_s \cdot a_d
17ColorHSL-based calculationas+adasada_s + a_d - a_s \cdot a_d
18LuminosityHSL-based calculationas+adasada_s + a_d - a_s \cdot a_d

Shader及验证

Shader and Verification

1. Normal

RenderAnalysis
vec4 blendNormal(vec4 src, vec4 dst) { vec3 c = src.rgb + dst.rgb * (1.0 - src.a); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

2. Darken

RenderAnalysis
vec4 blendDarken(vec4 src, vec4 dst) { vec3 c = src.rgb + dst.rgb - max(src.rgb * dst.a, dst.rgb * src.a); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

3. Multiply

RenderAnalysis
vec4 blendMultiply(vec4 src, vec4 dst) { return src * (1.0 - dst.a) + dst * (1.0 - src.a) + src * dst; }

4. Plus Darker

RenderAnalysis
vec4 blendPlusDarker(vec4 src, vec4 dst) { float a = src.a + (1.0 - src.a) * dst.a; vec3 color = max(vec3(0.0), a - (dst.a - dst.rgb) - (src.a - src.rgb)); return vec4(color, a); }

5. Color Burn

RenderAnalysis
float blendColorBurnComponent(vec2 src, vec2 dst) { float t = dst.y == dst.x ? dst.y : 0.0; float d = abs(src.x) > 0.0 ? dst.y - min(dst.y, (dst.y - dst.x) * src.y / (src.x + 0.001)) : t; return (d * src.y + src.x * (1.0 - dst.y)) + dst.x * (1.0 - src.y); } vec4 blendColorBurn(vec4 src, vec4 dst) { vec3 c = vec3(blendColorBurnComponent(src.ra, dst.ra), blendColorBurnComponent(src.ga, dst.ga), blendColorBurnComponent(src.ba, dst.ba)); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

6. Lighten

RenderAnalysis
vec4 blendLighten(vec4 src, vec4 dst) { vec3 c = src.rgb + dst.rgb - min(src.rgb * dst.a, dst.rgb * src.a); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

7. Screen

RenderAnalysis
vec4 blendScreen(vec4 src, vec4 dst) { return vec4( 1.0 - (1.0 - src.rgb) * (1.0 - dst.rgb), src.a + dst.a * (1.0 - src.a) ); }

8. Plus Lighter

RenderAnalysis
vec4 blendPlusLighter(vec4 src, vec4 dst) { vec3 color = min(src.rgb + dst.rgb, vec3(1.0)); float alpha = src.a + (1.0 - src.a) * dst.a; return vec4(color, alpha); }

9. Color Dodge

RenderAnalysis
float blendColorDodgeComponent(vec2 src, vec2 dst) { float dxScale = dst.x == 0.0 ? 0.0 : 1.0; float delta = dxScale * min(dst.y, abs(src.y - src.x) > 0.0 ? (dst.x * src.y) / ((src.y - src.x) + 0.001) : dst.y); return (delta * src.y + src.x * (1.0 - dst.y)) + dst.x * (1.0 - src.y); } vec4 blendColorDodge(vec4 src, vec4 dst) { vec3 c = vec3(blendColorDodgeComponent(src.ra, dst.ra), blendColorDodgeComponent(src.ga, dst.ga), blendColorDodgeComponent(src.ba, dst.ba)); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

10. Overlay

RenderAnalysis
float blendOverlayComponent(vec2 src, vec2 dst) { return 2.0 * dst.x <= dst.y ? (2.0 * src.x) * dst.x : src.y * dst.y - (2.0 * (dst.y - dst.x)) * (src.y - src.x); } vec4 blendOverlay(vec4 src, vec4 dst) { vec3 c = vec3(blendOverlayComponent(src.ra, dst.ra), blendOverlayComponent(src.ga, dst.ga), blendOverlayComponent(src.ba, dst.ba)); float a = src.a + dst.a * (1.0 - src.a); c += dst.rgb * (1.0 - src.a) + src.rgb * (1.0 - dst.a); return vec4(c, a); }

11. Soft Light

RenderAnalysis
float blendSoftLightComponent(vec2 src, vec2 dst) { const float EPSILON = 0.0; if (2.0 * src.x <= src.y) { return (((dst.x * dst.x) * (src.y - 2.0 * src.x)) / (dst.y + EPSILON) + (1.0 - dst.y) * src.x) + dst.x * ((-src.y + 2.0 * src.x) + 1.0); } else if (4.0 * dst.x <= dst.y) { float dSqd = dst.x * dst.x; float dCub = dSqd * dst.x; float daSqd = dst.y * dst.y; float daCub = daSqd * dst.y; return (((daSqd * (src.x - dst.x * ((3.0 * src.y - 6.0 * src.x) - 1.0)) + ((12.0 * dst.y) * dSqd) * (src.y - 2.0 * src.x)) - (16.0 * dCub) * (src.y - 2.0 * src.x)) - daCub * src.x) / (daSqd + EPSILON); } else { return ((dst.x * ((src.y - 2.0 * src.x) + 1.0) + src.x) - sqrt(dst.y * dst.x) * (src.y - 2.0 * src.x)) - dst.y * src.x; } } vec4 blendSoftLight(vec4 src, vec4 dst) { vec3 c = vec3(blendSoftLightComponent(src.ra, dst.ra), blendSoftLightComponent(src.ga, dst.ga), blendSoftLightComponent(src.ba, dst.ba)); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

12. Hard Light

RenderAnalysis
vec4 blendHardLight(vec4 src, vec4 dst) { return blendOverlay(dst, src); }

13. Difference

RenderAnalysis
vec4 blendDifference(vec4 src, vec4 dst) { vec3 c = src.rgb + dst.rgb - 2.0 * min(src.rgb * dst.a, dst.rgb * src.a); float a = src.a + dst.a * (1.0 - src.a); return vec4(c, a); }

14. Exclusion

RenderAnalysis
vec4 blendExclusion(vec4 src, vec4 dst) { vec3 c = (dst.rgb + src.rgb) - (2.0 * dst.rgb * src.rgb); float a = src.a + (1.0 - src.a) * dst.a; return vec4(c, a); }

15. Hue

RenderAnalysis
float blendColorSaturation(vec3 color) { return max(max(color.x, color.y), color.z) - min(min(color.x, color.y), color.z); } vec4 blendHSLColor(vec2 flipSat, vec4 src, vec4 dst) { const float EPSILON = 0.0; const float MIN_NORMAL_HALF = 6.10351562e-05; float alpha = dst.a * src.a; vec3 sda = src.rgb * dst.a; vec3 dsa = dst.rgb * src.a; vec3 l = bool(flipSat.x) ? dsa : sda; vec3 r = bool(flipSat.x) ? sda : dsa; if (bool(flipSat.y)) { float mn = min(min(l.x, l.y), l.z); float mx = max(max(l.x, l.y), l.z); l = mx > mn ? ((l - mn) * blendColorSaturation(r)) / (mx - mn) : vec3(0.0); r = dsa; } float lum = dot(vec3(0.3, 0.59, 0.11), r); vec3 result = (lum - dot(vec3(0.3, 0.59, 0.11), l)) + l; float minComp = min(min(result.x, result.y), result.z); float maxComp = max(max(result.x, result.y), result.z); if (minComp < 0.0 && lum != minComp) { result = lum + (result - lum) * (lum / ((lum - minComp + MIN_NORMAL_HALF) + EPSILON)); } if (maxComp > alpha && maxComp != lum) { result = lum + ((result - lum) * (alpha - lum)) / ((maxComp - lum + MIN_NORMAL_HALF) + EPSILON); } return vec4( ((result + dst.rgb) - dsa + src.rgb) - sda, src.a + dst.a - alpha ); } vec4 blendHue(vec4 src, vec4 dst) { return blendHSLColor(vec2(0.0, 1.0), src, dst); }

16. Saturation

RenderAnalysis
float blendColorSaturation(vec3 color) { return max(max(color.x, color.y), color.z) - min(min(color.x, color.y), color.z); } vec4 blendHSLColor(vec2 flipSat, vec4 src, vec4 dst) { const float EPSILON = 0.0; const float MIN_NORMAL_HALF = 6.10351562e-05; float alpha = dst.a * src.a; vec3 sda = src.rgb * dst.a; vec3 dsa = dst.rgb * src.a; vec3 l = bool(flipSat.x) ? dsa : sda; vec3 r = bool(flipSat.x) ? sda : dsa; if (bool(flipSat.y)) { float mn = min(min(l.x, l.y), l.z); float mx = max(max(l.x, l.y), l.z); l = mx > mn ? ((l - mn) * blendColorSaturation(r)) / (mx - mn) : vec3(0.0); r = dsa; } float lum = dot(vec3(0.3, 0.59, 0.11), r); vec3 result = (lum - dot(vec3(0.3, 0.59, 0.11), l)) + l; float minComp = min(min(result.x, result.y), result.z); float maxComp = max(max(result.x, result.y), result.z); if (minComp < 0.0 && lum != minComp) { result = lum + (result - lum) * (lum / ((lum - minComp + MIN_NORMAL_HALF) + EPSILON)); } if (maxComp > alpha && maxComp != lum) { result = lum + ((result - lum) * (alpha - lum)) / ((maxComp - lum + MIN_NORMAL_HALF) + EPSILON); } return vec4( ((result + dst.rgb) - dsa + src.rgb) - sda, src.a + dst.a - alpha ); } vec4 blendSaturation(vec4 src, vec4 dst) { return blendHSLColor(vec2(1.0), src, dst); }

17. Color

RenderAnalysis
float blendColorSaturation(vec3 color) { return max(max(color.x, color.y), color.z) - min(min(color.x, color.y), color.z); } vec4 blendHSLColor(vec2 flipSat, vec4 src, vec4 dst) { const float EPSILON = 0.0; const float MIN_NORMAL_HALF = 6.10351562e-05; float alpha = dst.a * src.a; vec3 sda = src.rgb * dst.a; vec3 dsa = dst.rgb * src.a; vec3 l = bool(flipSat.x) ? dsa : sda; vec3 r = bool(flipSat.x) ? sda : dsa; if (bool(flipSat.y)) { float mn = min(min(l.x, l.y), l.z); float mx = max(max(l.x, l.y), l.z); l = mx > mn ? ((l - mn) * blendColorSaturation(r)) / (mx - mn) : vec3(0.0); r = dsa; } float lum = dot(vec3(0.3, 0.59, 0.11), r); vec3 result = (lum - dot(vec3(0.3, 0.59, 0.11), l)) + l; float minComp = min(min(result.x, result.y), result.z); float maxComp = max(max(result.x, result.y), result.z); if (minComp < 0.0 && lum != minComp) { result = lum + (result - lum) * (lum / ((lum - minComp + MIN_NORMAL_HALF) + EPSILON)); } if (maxComp > alpha && maxComp != lum) { result = lum + ((result - lum) * (alpha - lum)) / ((maxComp - lum + MIN_NORMAL_HALF) + EPSILON); } return vec4( ((result + dst.rgb) - dsa + src.rgb) - sda, src.a + dst.a - alpha ); } vec4 blendColor(vec4 src, vec4 dst) { return blendHSLColor(vec2(0.0), src, dst); }

18. Luminosity

RenderAnalysis
float blendColorSaturation(vec3 color) { return max(max(color.x, color.y), color.z) - min(min(color.x, color.y), color.z); } vec4 blendHSLColor(vec2 flipSat, vec4 src, vec4 dst) { const float EPSILON = 0.0; const float MIN_NORMAL_HALF = 6.10351562e-05; float alpha = dst.a * src.a; vec3 sda = src.rgb * dst.a; vec3 dsa = dst.rgb * src.a; vec3 l = bool(flipSat.x) ? dsa : sda; vec3 r = bool(flipSat.x) ? sda : dsa; if (bool(flipSat.y)) { float mn = min(min(l.x, l.y), l.z); float mx = max(max(l.x, l.y), l.z); l = mx > mn ? ((l - mn) * blendColorSaturation(r)) / (mx - mn) : vec3(0.0); r = dsa; } float lum = dot(vec3(0.3, 0.59, 0.11), r); vec3 result = (lum - dot(vec3(0.3, 0.59, 0.11), l)) + l; float minComp = min(min(result.x, result.y), result.z); float maxComp = max(max(result.x, result.y), result.z); if (minComp < 0.0 && lum != minComp) { result = lum + (result - lum) * (lum / ((lum - minComp + MIN_NORMAL_HALF) + EPSILON)); } if (maxComp > alpha && maxComp != lum) { result = lum + ((result - lum) * (alpha - lum)) / ((maxComp - lum + MIN_NORMAL_HALF) + EPSILON); } return vec4( ((result + dst.rgb) - dsa + src.rgb) - sda, src.a + dst.a - alpha ); } vec4 blendLuminance(vec4 src, vec4 dst) { return blendHSLColor(vec2(1.0, 0.0), src, dst); }