混合模式公式
Blending Equations
No. | Name | Color Component | Alpha Component | Usage |
---|
1 | Normal | Cs+Cd⋅(1−as) | as+ad⋅(1−as) | |
2 | Darken | Cs+Cd−max(Cs⋅ad, Cd⋅as) | as+ad⋅(1−as) | |
3 | Multiply | Cs⋅Cd+Cs⋅(1−ad)+Cd⋅(1−as) | as+ad⋅(1−as) | |
4 | Plus Darker | max(0,a−(ad−Cd)−(as−Cs)) | as+ad⋅(1−as) | |
5 | Color Burn | Complex component-wise calculation | as+ad⋅(1−as) | |
6 | Lighten | Cs+Cd−min(Cs⋅ad,Cd⋅as) | as+ad⋅(1−as) | |
7 | Screen | 1−(1−Cs)⋅(1−Cd) | as+ad⋅(1−as) | |
8 | Plus Lighter | min(1,Cs+Cd) | as+ad⋅(1−as) | |
9 | Color Dodge | Complex component-wise calculation | as+ad⋅(1−as) | |
10 | Overlay | Component-wise conditional calculation | as+ad⋅(1−as) | |
11 | Soft Light | Complex component-wise calculation | as+ad⋅(1−as) | |
12 | Hard Light | Same as Overlay but with swapped inputs | as+ad⋅(1−as) | |
13 | Difference | Cs+Cd−2⋅min(Cs⋅ad,Cd⋅as) | as+ad⋅(1−as) | |
14 | Exclusion | Cs+Cd−2⋅Cs⋅Cd | as+ad⋅(1−as) | |
15 | Hue | HSL-based calculation | as+ad−as⋅ad | |
16 | Saturation | HSL-based calculation | as+ad−as⋅ad | |
17 | Color | HSL-based calculation | as+ad−as⋅ad | |
18 | Luminosity | HSL-based calculation | as+ad−as⋅ad | |
Shader及验证
Shader and Verification
1. Normal
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
vec4 blendMultiply(vec4 src, vec4 dst) {
return src * (1.0 - dst.a) + dst * (1.0 - src.a) + src * dst;
}
4. Plus Darker
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
vec4 blendHardLight(vec4 src, vec4 dst) {
return blendOverlay(dst, src);
}
13. Difference
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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
Render | Analysis |
---|
 |  |
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);
}