在渲染项目中时常需要处理颜色混合、色彩空间、预乘和非预成空间等基本问题。处理不好这些问题会导致渲染时 出现黑边、白边、过爆、过暗等瑕疵。另外有很多优秀的文章也讲述了这些问题, 如:
In rendering projects, it is often to run into problems cased by basic issues such as color blending, color spaces, premultiplied and non-premultiplied spaces. Poor handling of such issues can lead to artifacts, such as black edges, white edges, overexposure, and underexposure. There are also many excellent articles that discuss these problems, such as:
Alpha compositing, OpenGL blending and premultiplied alpha
在这篇文章中我讲从最基本的物理/数学公式开始, 逐步介绍如何在 shader 中正确处理此类问题。
In this article, I will start with the fundamental physics/mathematics formulas and gradually introduce how to correctly handle such issues in shaders.
RGB 和 Alpha
RGB and Alpha
在计算机图形学中, 我们通常使用RGB色彩空间来表示颜色。RGB色彩空间是一个三维空间, 其中每个轴代表一个颜色通道。 RGB之所以被广泛使用, 是因为RGB直接对应于人类视觉系统的三种颜色感受器, 也同时直接对应LED显示器的三种发光颜色。
In computer graphics, we usually use the RGB color space to represent colors. The RGB color space is a three-dimensional space, where each axis represents a color channel. RGB is widely used because it directly corresponds to the three color receptors of the human visual system and also directly corresponds to the three emitting colors of LED displays.
当然, 由于人眼对颜色的感知是非线性的, 显示硬件也有自己的特性, 所以在送给显示器之前, 我们需要对RGB进行一些处理。 一种标准的处理方式是将线性RGB转换为 sRGB (standard-RGB), 这样可以使颜色在显示器上更加接近人眼的感知。
Of course, since the human eye's perception of color is nonlinear and the display hardware has its own characteristics, we need to process RGB before sending it to the display. A standard way to process RGB is to convert linear RGB to sRGB (standard-RGB), which makes colors on the display closer to human perception.
需要格外注意的是, sRGB 不是一个线性空间, 这意味着在 sRGB 空间中的颜色混合和计算是不同于线性空间的。我们接下 来的所有讨论都会建立在线性RGB空间上, 在实际引用中, 需要根据具体情况进行转换。
It is important to note that sRGB is not a linear space, which means that color blending and calculations in the sRGB space are different from those in the linear space. All our subsequent discussions will be based on the linear RGB space. In practical applications, conversion should be made according to specific situations.
唯一的真理
The only truth
我们首先需要把alpha混合的数学公式和其中各个参数的物理意义搞明白, 这是后续讨论的基础, 也是我们正确处理问题 时所依赖的唯一真理。
First of all, we need to understand the mathematical formula of alpha blending and the physical meaning of each parameter. This is the basis for subsequent discussions and the only truth we rely on to correctly handle the problem.
其中
- 计算发生在非预乘空间
- 代表色值, 也就是rgb三个灯光的强度
- 代表不透明度, 可以理解为rgb灯光整体的强度
Where
- The calculation takes place in the non-premultiplied space
- represents the color value, that is, the intensity of the three RGB lights
- represents the opacity, which can be understood as the overall intensity of the RGB lights
预乘空间
Premultiplied space
将上面的公式2重新整理下, 可以得到如下形式:
Rearranging the above formula 2, we can get the following form:
不难发现, 上面的公式中, 无论是, 还是 , 都分别与自己的 相乘了。我们将 乘以 的操作称为预乘, 重新整理公式, 可以得到如下形式:
It is not difficult to find that in the above formula, whether it is , or , they are multiplied by their own . We call the operation of multiplying by premultiplication. Rearranging the formula, we can get the following form:
上式被称为预乘空间的混合公式。在预乘空间中, 混合公式变得更加简单, 且在混合过程中能够节省3次乘法运算, 提升性能。
The above formula is called the blending formula in the premultiplied space. In the premultiplied space, the blending formula becomes simpler, and 3 multiplication operations can be saved in the blending process, which improves performance.
Alpha混合GLSL片段
Alpha blending in GLSL
vec4 alpha_blending(vec4 src, vec4 dst) { float a = src.a + dst.a * (1.0 - src.a); vec3 rgb = (src.rgb * src.a + dst.rgb * dst.a * (1.0 - src.a)) / a; return vec4( rgb, a ); } vec4 alpha_blending_premultiplied(vec4 src, vec4 dst) { return vec4( src.rgb + dst.rgb * (1.0 - src.a), src.a + dst.a * (1.0 - src.a) ); }
在shader中混合时, 我们只需要注意3个要点:
When blending in the shader, we only need to pay attention to 3 points:
- 确保混合时
src
和dst
都处在同一个色彩空间, 即都是线性空间或都是sRGB空间 - 确保混合时
src
和dst
都处在同一个预乘空间, 即都是预乘空间或都是非预乘空间 - 混合上下游, 即
openGl
中的glBlendFunc
和glBlendEquation
需要正确设置来对应所选择的预乘空间
When blending in the shader, we only need to pay attention to 3 points:
- Ensure that
src
anddst
are in the same color space when blending, that is, both are in the linear space or both are in the sRGB space - Ensure that
src
anddst
are in the same premultiplied space when blending, tiplied space - Blend upstream and downstream, that is,
glBlendFunc
andglBlendEquation
inopenGl
need to be set correctly to correspond to the selected premultiplied space
现象
What If You Do Wrong

正确混合时, 两个图层的边缘是平滑的。
When blending correctly, the edges of the two layers are smooth.