WebGL で高速 BlendMode
Posted :
WebGL を使えば、色の加算、減算、乗算といったブレンドモードが高速で処理できるというお話。
Canvas2D でもピクセルマニピュレーションができるので、様々なブレンドモードを自前で実装することができるのだが、ピクセル数 * 4色の処理負荷が CPU にかかるためパフォーマンスがよくない。しかし WebGL のフラグメントシェーダーを利用すれば色を高速で処理できる。
WebGL は簡単なことをするにも、初期化の処理が冗長なためコードは長くなってしまうが、実際は大したことはやっていない。
- 2つのトライアングルを用意する
- 2枚のテクスチャー画像を適用する
- フラグメントシェーダーでテクスチャー画像の色をブレンドする
GLSL では、4成分 (Red, Green, Blue, Alpha) をそのまま足したり掛けたりすることができるので、ブレンドの公式に当てはめるだけで処理できる。
考え方は Shader Effects: Blend Modes がとても参考になる。
ブレンドモードの計算式
src
は vec3 型の前景 (source) テクスチャー色dst
は vec3 型の背景 (dist) テクスチャー色
ADD
src + dst;
SUBTRACT
src - dst;
MULTIPLY
src * dst;
DARKEN
min( src, dst );
COLOUR BURN
vec3(
( src.r == 0.0 ) ? 0.0 : ( 1.0 - ( ( 1.0 - dst.r ) / src.r ) ),
( src.g == 0.0 ) ? 0.0 : ( 1.0 - ( ( 1.0 - dst.g ) / src.g ) ),
( src.b == 0.0 ) ? 0.0 : ( 1.0 - ( ( 1.0 - dst.b ) / src.b ) )
);
LINEAR BURN
( src + dst ) - 1.0;
LIGHTEN
max( src, dst );
SCREEN
( src + dst ) - ( src * dst );
COLOUR DODGE
vec3(
( src.r == 1.0 ) ? 1.0 : min( 1.0, dst.r / ( 1.0 - src.r ) ),
( src.g == 1.0 ) ? 1.0 : min( 1.0, dst.g / ( 1.0 - src.g ) ),
( src.b == 1.0 ) ? 1.0 : min( 1.0, dst.b / ( 1.0 - src.b ) )
);
LINEAR DODGE
src + dst;
OVERLAY
vec3(
( dst.r <= 0.5 ) ? ( 2.0 * src.r * dst.r ) : ( 1.0 - 2.0 * ( 1.0 - dst.r ) * ( 1.0 - src.r ) ),
( dst.g <= 0.5 ) ? ( 2.0 * src.g * dst.g ) : ( 1.0 - 2.0 * ( 1.0 - dst.g ) * ( 1.0 - src.g ) ),
( dst.b <= 0.5 ) ? ( 2.0 * src.b * dst.b ) : ( 1.0 - 2.0 * ( 1.0 - dst.b ) * ( 1.0 - src.b ) )
);
SOFT LIGHT
vec3(
( src.r <= 0.5 ) ? ( dst.r - ( 1.0 - 2.0 * src.r ) * dst.r * ( 1.0 - dst.r ) ) : ( ( ( src.r > 0.5 ) && ( dst.r <= 0.25 ) ) ? ( dst.r + ( 2.0 * src.r - 1.0 ) * ( 4.0 * dst.r * ( 4.0 * dst.r + 1.0 ) * ( dst.r - 1.0 ) + 7.0 * dst.r ) ) : ( dst.r + ( 2.0 * src.r - 1.0 ) * ( sqrt( dst.r ) - dst.r ) ) ),
( src.g <= 0.5 ) ? ( dst.g - ( 1.0 - 2.0 * src.g ) * dst.g * ( 1.0 - dst.g ) ) : ( ( ( src.g > 0.5 ) && ( dst.g <= 0.25 ) ) ? ( dst.g + ( 2.0 * src.g - 1.0 ) * ( 4.0 * dst.g * ( 4.0 * dst.g + 1.0 ) * ( dst.g - 1.0 ) + 7.0 * dst.g ) ) : ( dst.g + ( 2.0 * src.g - 1.0 ) * ( sqrt( dst.g ) - dst.g ) ) ),
( src.b <= 0.5 ) ? ( dst.b - ( 1.0 - 2.0 * src.b ) * dst.b * ( 1.0 - dst.b ) ) : ( ( ( src.b > 0.5 ) && ( dst.b <= 0.25 ) ) ? ( dst.b + ( 2.0 * src.b - 1.0 ) * ( 4.0 * dst.b * ( 4.0 * dst.b + 1.0 ) * ( dst.b - 1.0 ) + 7.0 * dst.b ) ) : ( dst.b + ( 2.0 * src.b - 1.0 ) * ( sqrt( dst.b ) - dst.b ) ) )
);
HARD LIGHT
vec3(
( src.r <= 0.5 ) ? ( 2.0 * src.r * dst.r ) : ( 1.0 - 2.0 * ( 1.0 - src.r ) * ( 1.0 - dst.r ) ),
( src.g <= 0.5 ) ? ( 2.0 * src.g * dst.g ) : ( 1.0 - 2.0 * ( 1.0 - src.g ) * ( 1.0 - dst.g ) ),
( src.b <= 0.5 ) ? ( 2.0 * src.b * dst.b ) : ( 1.0 - 2.0 * ( 1.0 - src.b ) * ( 1.0 - dst.b ) )
);
VIVID LIGHT
vec3(
( src.r <= 0.5 ) ? ( 1.0 - ( 1.0 - dst.r ) / ( 2.0 * src.r ) ) : ( dst.r / ( 2.0 * ( 1.0 - src.r ) ) ),
( src.g <= 0.5 ) ? ( 1.0 - ( 1.0 - dst.g ) / ( 2.0 * src.g ) ) : ( dst.g / ( 2.0 * ( 1.0 - src.g ) ) ),
( src.b <= 0.5 ) ? ( 1.0 - ( 1.0 - dst.b ) / ( 2.0 * src.b ) ) : ( dst.b / ( 2.0 * ( 1.0 - src.b ) ) )
);
LINEAR LIGHT
2.0 * src + dst - 1.0;
PIN LIGHT
vec3(
( src.r > 0.5 ) ? max( dst.x, 2.0 * ( src.r - 0.5 ) ) : min( dst.x, 2.0 * src.r ),
( src.r > 0.5 ) ? max( dst.y, 2.0 * ( src.g - 0.5 ) ) : min( dst.y, 2.0 * src.g ),
( src.b > 0.5 ) ? max( dst.z, 2.0 * ( src.b - 0.5 ) ) : min( dst.z, 2.0 * src.b )
);
DIFFERENCE
abs(dst - src);
EXCLUSION
src + dst - 2.0 * src * dst;