最近听到了一个想法,是将Unity内的一些特效进行截图,导出带透明度的png用于视频制作,于是回顾了一下以前研究过的混融相关的问题,研究了一下如何实现,也学到了一些新的技巧和知识。

混融公式

(图1:OpenGL渲染管线)

在OpenGL渲染管线中,对于透明相关的处理是在管线后期进行的。画面中的物体,三角形是按照一定顺序渲染的,当物体或三角形在位置发生重叠时可能会出现一个颜色覆盖另一个颜色的情况 ,对透明的处理过程,混融blending,实际上就是shader输出颜色与此颜色所在像素位置已有颜色的合并过程。

假如将当前Shader片段着色器要输出的颜色称为源颜色Source Color,简称Cs,将Cs(r,g,b,a)的alpha值称为Source alpha,简称srcA,此像素已有颜色设为目标颜色Color Destination简称为Cd,混融后得出的颜色Color(r,g,b,a)),简称C,那么求C的公式为:

C=Cs * srcA + Cd * (1-srcA)       (公式1)

以上既是Unity中默认的混融模式“Blend SrcAlpha OneMinusSrcAlpha”背后的计算公式。此公式实际为OpenGL多个混融方程中的其中一个版本,在OpenGL中所有方程都是从公式2演变出:

C=Cs*srcfactor 操作符 Cd*dstfactor (公式2)

OpenGL对srcfactor,dstfactor提供了19个可选参数,例如公式1中的源颜色透明度srcA,也可以换为Color Destination的alpha值,或者是常数0或1,甚至还可以是一个固定rgb颜色值(管线预设好的)。

Unity对srcfactor,distractor提供了10种选择:

   
One The value of one - use this to let either the source or the destination color come through fully.
Zero The value zero - use this to remove either the source or the destination values.
SrcColor The value of this stage is multiplied by the source color value.
SrcAlpha The value of this stage is multiplied by the source alpha value.
DstColor The value of this stage is multiplied by frame buffer source color value.
DstAlpha The value of this stage is multiplied by frame buffer source alpha value.
OneMinusSrcColor The value of this stage is multiplied by (1 - source color).
OneMinusSrcAlpha The value of this stage is multiplied by (1 - source alpha).
OneMinusDstColor The value of this stage is multiplied by (1 - destination color).
OneMinusDstAlpha

The value of this stage is multiplied by (1 - destination alpha) .

对于操作符,OpenGL可以进行5种操作,对于乘以参数后的Cs与Cd进行Cs+Cd,Cs-Cd,Cd-Cs,取最小,取最大。

在Unity中可以用 Blendop命令对操作符进行设置:

例如如果在Shader中写明Blendop Sub,那么公式1会变为:C=Cs * srcA - Cd * (1-srcA)

Unity提供的5个可选操作符:

   
Add Add source and destination together.
Sub Subtract destination from source.
RevSub Subtract source from destination.
Min Use the smaller of source and destination.
Max Use the larger of source and destination.

另外还有16种DX专用操作符。

虽然混融阶段不是可编程的,但是通过选择不同的参数与操作符,混融也有很大的可操作空间,例如在Unity中,不考虑DX专用操作符,所有可能的混融模式有10*10*5=500种。

(图2:同样的两张png图片在Unity中不同混融模式下的一些渲染差异对比)

背景颜色剔除

Unity Camera的Clear Flags控制着相机在渲染当前帧之前如何处理上一帧的帧缓存,或者也可以理解为对当前帧缓存的初始化。例如Skybox意味着清除所有内容并渲染skybox,然后才进行接下来的渲染。Clear flags的设置可以看做是场景背景的设置。

剔除背景图最简单的思路是将clear flags选为solid color,然后选项下方background中的alpha改为0,再用代码将render texture写入到png中。或者利用Unity提供的custom render texture将画面export出来。但是这种方法有些问题,一是在Editor中Clear flags的background alpha为0不能正常显示为透明背景,第二是如果渲染物体Shader有需要DstAlpha(目标颜色的alpha值)时,由于它是0,而正常背景alpha都是1,会出现错误的渲染效果。

PS抠图也算是一种思路,但是它无法处理透明物体后面的背景颜色。

这里介绍的方法和上文中的混融公式有关。我们先设一个半透明物体的源颜色Cs,与背景混融后的颜色C,既然已知了公式1混融的参数与操作数:

C=Cs * srcA + Cd * (1-srcA)

进行一些变换后我们可以反过来求Cs:

Cs=(C-Cd*(1-srcA))/srcA (公式3)

观察等式发现首先要求出srcA。设两个相机,一个纯黑色背景0001,一个纯白色背景1111,对同一个画面同时进行渲染,设渲染后的颜色分别为Cblack,Cwhite,由于srcA是一个常数,我们只使用Cblack和Cwhite的rgb中的一个值进行计算,将两个r值导入公式1中设一个方程组:

Cblack.r=Cs.r*srcA+0*(1-srcA)= Cs.r*srcA

Cwhite.r=Cs.r*srcA+1*(1-srcA)

将Cblack.r导入到Cwhite.r等式中:

Cwhite.r=Cblack.r+1*(1-srcA)=Cblack.r + 1 - srcA

可变为:

srcA=Cblack.r-Cwhite.r+1

这样利用两个黑白渲染帧的r值,成功的求得了srcA,再利用srcA与Cblack还原Cs.rgb。已知:

Cblack=Cs*srcA+0001*(1-srcA)= Cs*srcA+0001-000srcA = Cs*srcA + 000B

Cblack除以srcA就得出了:

Cblack/srcA=(Sc*srcA+000B)/srcA=Cs+000C

000C是一个未知数导致Cs.a是不正确的,但是这里不用管它,因为已经求得了正确的SrcA,我们只要再将srcA赋值给Cs就最后得出了正确的剔除背景后的颜色值:

Cs.a=srcA;

以上,对于一般场景(没有多个透明物体重叠)的背景剔除还原后的rgba值是精准的,但是对于有两个以上半透明物体重叠的场景,重叠处的像素的颜色计算会有误差,还需要进一步改进。以后有时间会在回来Update一下。

老外利用以上思路写的一个工具: http://wiki.unity3d.com/index.php/AnimationToPNG

半透明物体之间和同一物体的各个面之间的遮挡问题

在Unity内对不同的Shader可以设置渲染顺序优先级,例如不透明的物体优先级大于透明物体。相同优先级物体的会根据Z值进行先后渲染。不透明物体根据相机视角从近到远渲染,透明物体从远到近渲染。这个设计在大部分情况下是没有问题的,并且也比较优化,但是有一个问题是如果前方的半透明物体的一部分被后方的半透明物体遮挡会出现什么效果?用以下Shader测试:

Shader "Unlit/trans1"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "Queue" = "Transparent" }LOD 100Blend SrcAlpha OneMinusSrcAlphaZWrite On ZTest LEqualCull BackPass{CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;UNITY_FOG_COORDS(1)float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);UNITY_TRANSFER_FOG(o,o.vertex);return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fogUNITY_APPLY_FOG(i.fogCoord, col);col.a=0.5f;return col;}ENDCG}}
}

(图3:测试结果)

可以看出后方的cube渲染后,所有像素的z值写入了深度缓存,渲染前方的cube时,每个像素的z值是精确的,但是渲染管线丢弃了Cs的颜色,对于ZTest来讲是正确的操作,但是对于混融来讲是错误的,但由于ZTest是在混融之前所以会发生这种冲突。将ZTest LEqual改为ZTest Always可以解决这一问题,但是会繁衍出另一问题,它们无法被渲染顺序在transparent之前的非透明物体遮挡。

(图4:ZTest LEqual改为ZTest Always后的测试结果)

另一个问题是同一个半透明3D物体的各个面能不能正确处理互相遮挡的关系呢?还是用上面的Shader测试:

(图5:ZTest LEqual,从一个怪物型模型头顶处观察)

(图6:ZTest Always)

测试结果是同一个物体的各个面之间无法依赖于z值去正确的互相遮挡。

再把Cull Back改为Cull Off,观察cube和sphere:

(图7:Cull Off,ZTest Lqual。cube后方反面部分三角形没有正确渲染)

(图8:Cull Off,ZTest Lqual。sphere后方反面部分三角形没有正确渲染)

(图9:Cull Off,ZTest Always。全部三角形正确渲染)

可以看出同一个物体的各个面的渲染顺序遮挡关系是以三角形为基础单位的,不管是正反面都无法期望依赖z值按正确的顺序渲染,但可以用ZTest Always强行混融渲染出正确的效果。这个问题的引发原因还要深入研究一下,以后有机会再Update此文。

————————————————————————————————————————————————————

参考:

AnimationToPNG-- Brad Nelson

OpenGL 编程指南 Chapter4--Khronos group

Unity Manual: ShaderLab Blending -- Unity Technology

维护日志:

2018-5-2:增,改

2018-5-12:改标题。增加Custom Render Texture部分。

2018-9-26:增加“当前混融机制的局限性”

2020-2-4:增,改

在Unity内利用混融公式剔除背景颜色导出透明PNG以及半透明遮挡相关问题的研究相关推荐

  1. 在Unity中使用FFmpeg将视频绿色背景处理为透明背景

    1. 安装FFmpeg 官方下载地址:Download FFmpeg  这里演示windows版的下载 建议下载shared压缩包 找到bin目录,复制路径 设置环境变量: 状态栏左下角,右键开始菜单 ...

  2. Excel的公式:公式基本使用、单元格地址引用、错误值利用、追踪公式利用与追踪错误

    文章目录 Exceld的公式 1.概念 2.作用 3.基本使用 (1)查看公式 (2)输入包含内置函数的公式 (3)公式深度介绍 (4)公式运算符 (5)公式特点 4.单元格地址引用 5.单列变多列- ...

  3. 基于28335实现的旋变软解码 利用三角函数积化和差公式将旋变输出信号分解为高频和低频两部分 自动补偿辅助低通滤波器带来的滞后

    基于28335实现的旋变软解码 1.在0-360°的范围内,与TI方案的偏差非常小,平均偏差最大为0.0009弧度左右,最大偏差0.0016弧度左右. 2.与1205最大偏差在±3个弧分以内,考虑到A ...

  4. Java利用注解实现配置动态公式并结合POI导出Excel

    利用注解实现动态配置公式并结合POI导出Excel 实现思想 创建导出对象 创建使用注解类 创建封装导出对象属性类 创建测试类 创建ExcelUtil类 实现效果 实现思想 实施顾问提出导出Excel ...

  5. Unity TextMeshPro图文混排

    笔者最近项目使用TextMeshPro完成图文混排的需求,以及图文超链接,总的来说功能很强大,没遇到什么坑. 这里记录一下图文混排实现的基本流程流程. 制作需要混排图片.asset文件 方式一 使用图 ...

  6. 基于28335实现的旋变软解码 利用三角函数积化和差公式将旋变输出信号分解为高频和低频两部分… 锁相环

    基于28335实现的旋变软解码 1.在0-360°的范围内,与TI方案的偏差非常小,平均偏差最大为0.0009弧度左右,最大偏差0.0016弧度左右. 2.与1205最大偏差在±3个弧分以内,考虑到A ...

  7. qt中json构造一个数组_告别撸单元格!我来分享Excel中如何利用一条公式得到一个数组...

    来分享一个Excel中小众的大招."小众的大招"--这么说不矛盾.在Excel表格中利用一条公式来得到一个数组是一个高深且晦涩的话题.多数人不懂什么是数组,所以遇到此类文章或应用实 ...

  8. Unity内置的三套消息发送机制的应用实例

    转自http://blog.sina.com.cn/s/blog_1491e52310102wuf6.html 代码简介 : [1] 实例中包含2个类文件, SendMessage.cs 和 Rece ...

  9. 基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统

    基于Unity引擎利用OpenCV和MediaPipe的面部表情和人体运动捕捉系统 前言 项目概述 项目实现效果 2D面部表情实时捕捉 3D人体动作实时捕捉 补充 引用 前言 之前做的一个项目--使用 ...

最新文章

  1. Permission denied: make_sock: could not bind to address [::]:81 Apache 虚拟主机
  2. 程序员饭碗不保了?GPT-3 最强应用发布,动动手指就自动写代码的神器来了!...
  3. .NET Core Community 第二个千星项目诞生:Util
  4. 深入理解多线程(二)—— Java的对象模型
  5. C++面试八股文快问快答のSTL篇
  6. python链表和树实验报告_关于Python实现树结构和链表结构的一点想法
  7. 软件系统中的颗粒度_意式浓缩咖啡丨甘醇香浓余韵长,研磨的度与质千万别忽视...
  8. 大一下学期的自我目标
  9. 分享 | 绝对值得一看的深度学习三巨头之一的Yoshua Bengio清华大学讲座视频
  10. R语言模拟:Bias Variance Decomposition
  11. Reduce归约 证明原理
  12. nginx 做代理转发 对文件上传下载有影响速度吗_少年: Nginx了解下
  13. 面向对象七大设计原则
  14. python随机抽取样本1500个_python 随机抽取数据
  15. 微博开放平台注册应用
  16. 数字化住宅小区对计算机网络有需求,浅谈智能小区宽带接入及其技术发展趋势...
  17. 林书豪055-阿联德比
  18. 别人家的思维导图,原来这么画!
  19. Kindle3之中文乱码问题
  20. Java基础篇之三----Java简介

热门文章

  1. 多图技术贴:深入浅出解析大数据平台架构
  2. jQuery学习之八---文档处理
  3. mysql 吧库下的表名都加_MySQL 数据库名、表名、字段名大小写敏感记录
  4. 拓步机器人联网说明书_科沃斯IPO聚焦三大战略 谋划转型互联网生态企业
  5. html5 crosshair,嘿,纯正ROG血统 CROSSHAIR VIII IMPACT (开箱篇)最终版
  6. python查看数据类型type_python——获取数据类型:type()、isinstance()的使用方法:...
  7. java后端做教育视频网站源码_【Java并发面试点】看这一篇应该是够了
  8. python自加1_python中有自增
  9. 导致网站服务器负担过重,利用httpd.ini实现图片和文件的防盗链
  10. vb.net 弹出对话框 修改按钮名_Microsoft Excel怎么批量快速修改批注?