最近做 WebGL 时遇到一个问题,显示的文字比 Canvas 中要模糊,问题情况如下:
Canvas 里的字体如图:
从 Canvas 画到帧缓冲中:
再从帧缓冲画到屏幕:
最后定位到问题在混合模式那里。有问题的混合模式设置为:
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
正确的混合模式应该是(也就是默认的混合模式):
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
具体原因如下:
如果使用 gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
如果我有一个透明的颜色,比如 (0,0,0,0.5),要叠加上去应该就是:
src 颜色为 (0,0,0,0.5),如果什么都不做,底层的颜色为 (0,0,0,0),也就是 dst 颜色。
那么采用上面的叠加公式:
r = srcR * srcA + dstR * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
g = srcG * srcA+ dstG * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
b = srcB * srcB + dstB * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
a = srcA * srcA + dstA * (1 - srcA) = 0.5 * 0.5 + 0 * (1 - 0.5) = 0.25
那么得到的颜色就为 (0,0,0,0.25) 比原来的颜色 (0,0,0,0.5) 要淡了,如果再作一次叠加就成了 (0,0,0,0.125),颜色更淡了,字体的边缘是有半透明色的,所以那部分会越来越淡。
但是如果使用 blendSpeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
那么 src 为 (0,0,0,0.5),同样 dst 也为 (0,0,0,0)
采用上面的叠加公式:
r = srcR * srcA + dstR * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
g = srcG * srcA+ dstG * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
b = srcB * srcB + dstB * (1 - srcA) = 0 * 0.5 + 0 * (1 - 0.5) = 0
a = srcA * 1 + dstA * (1 - srcA) = 0.5 * 1 + 0 * (1 - 0.5) = 0.5
所以得到的颜色还是 (0,0,0,0.5),不管画几次颜色都保持原样。