乐趣区

关于opengl:Opengl混合算法探究

在咱们的理论利用中应用 OpenGL 进行混合常见的问题有以下三种:

  • 应用 Opengl 自带的混合算法
  • 自定义混合算法
  • 半透明混合
    针对以上三种状况咱们具体分析有何不同及如何解决问题。

一、Opengl 自带混合算法

OpenGL 渲染管线的最初阶段会将源色和底色进行混合,咱们大部分状况下只需思考实现此次 drawcall 的渲染实现即可,无心过分操心如何与底色进行混合。那么如何应用自带的混合呢?

首先咱们须要开启混合模式,开启混合模式之后即可应用 OpenGL 自带的混合算法。
glEnable(GL_BLEND);

开启混合之后如何混合源色彩与底色呢?混合须要设置两个函数,一是设置混合因子即源色彩与底色各按何种比例进行混合,另一个就是混合函数,决定源色彩与底色应用哪种运算符失去后果。同时 RGB 色彩也能与 Alpha 值别离设置不同得混合因子与混合函数。

// 设置源颜色混合因子与底色混合因子
glBlendFunc(GLenum sfactor,GLenum defector);
// 示例
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

// 别离设置 RGB 色彩与 Alpha 值得混合因子
glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLdstAlpha)
// 示例
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);

// 设置混合函数
glBlendEquation(GLenum mode);
// 示例
glBlendEquation(GL_FUNC_ADD);

// 别离设置 RGB 色彩与 Alpha 值得混合函数
glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);
// 示例
glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);

下面得代码示例示意:源色彩(RGB 或 RGBA)须要乘源色彩得 alpha,底色(RGB 或 RGBA)须要乘以一减源色彩得 alpha,而混合函数为 GL_FUNC_ADD,所以最终混合算法为:

// 未别离设置
Color = vec4(Srgb * Sa + Drgb * (1 - Sa), Sa * Sa + Da * (1 - Sa));
// 别离设置 RGB 与 Alpha 得混合
Color = vec4(Srgb * Sa + Drgb * (1 - Sa), Sa + Da * (1 - Sa));

具体得混合因子与混合函数参考 OpenGL 混合示例,不再赘述。惟一须要留神得色彩是否预乘 alpha 得问题,因为默认也是最罕用得混合模式后果是预乘的,因而如果想要失去未预乘的后果,自带的混合算法并不实用。
总的来说,自带混合算法比拟不便速度也比拟快能够满足大部分的应用场景,但性能反对无限有些需要无奈满足。

二、自定义混合算法

下面讲到如果要失去未预乘的后果 Opengl 自带混合算法无奈为咱们失去正确的后果,此时咱们就须要敞开 OpenGL 自带的混合模式在 shader 中本人实现混合算法。PhotoShop 与 AE 中都有混合模式这个性能,有很多的混合模式可选,但因为 OpenGL 自带的混合算法反对的性能无限,这些混合算法都无奈间接通过 OpenGL 自带的混合算法实现。手动实现混合算法比拟自在,咱们能够自定义一些混合办法,实现一些 OpenGL 自带混合模式无奈实现的简单混合算法,毛病是在大部分 GPU 上同一个 texture 无奈既作 FBO 输入,又作纹理采样输出,如果底图作为输出传入 Fragment Shader,则以后 FBO 须要绑定另一个 texture 作为输入。如果混合区域笼罩全图,能够用 FBO 绑定一个空的 texture 作为输入,同时原始底图传入 Fragment Shader 作为输出;如果混合区域只占全图的一部分,那么就须要首先复制一份底图纹理并绑定到 FBO 作为输入,同时原始底图纹理传入 Fragment Shader 做混合,这两种不同的混合场景下,不论混合区域是全图还是局部区域,都须要申请一块额定的底图大小的纹理存储(空白或复制底图),另外局部区域混合时还须要一次额定的渲染(复制底图),混合所须要的空间和工夫都有额定开销。尽管实现了咱们的需要,但毛病也是不言而喻的,工夫与空间的开销都比拟大。

一些罕用的自定义混合算法能够从这里找到

三、半透明混合

在 3D 渲染中,深度缓冲去十分重要,深度值决定了以后片元是否须要抛弃,如果通明物体与半透明物体放在一起渲染就可能会呈现半透明物体没有与背地的物体进行混合。例如

产生这一景象的起因是,深度测试和混合一起应用的话会产生一些麻烦。当写入深度缓冲时,深度缓冲不会查看片段是否是通明的,所以通明的局部会和其它值一样写入到深度缓冲中。后果就是不思考透明度都会进行深度测试。即便通明的局部应该显示背地的物体,但深度测试依然抛弃了它们。这也是混合变得有些麻烦的局部。要想保障窗户中可能显示它们背地的窗户,咱们须要首先绘制背地的这部分窗户。这也就是说在绘制的时候,咱们必须先手动将窗户依照最远到最近来排序,再依照程序渲染。

但这依然不能解决所有问题,因为物体在 3d 空间中可能会穿插导出咱们无奈确定程序,甚至咱们都无奈确定物体是否为半透明的。这样看来这也不是一个好的解决办法。那么如果咱们能逐像素对所有的渲染指标进行排序,这样咱们就能分清远近关系。这就须要我之前一篇文章提到的 OIT 渲染。

半透明混合是十分辣手的,如果想要失去好的成果必然带来性能或空间的巨大损失,OIT 渲染的将来仍须要提出更好的计划及硬件反对。

退出移动版