这一篇是为了继续深入之前透明原理而写,为了更好的理解图形学理论知识,如果不明白透明原理的同学务必返回图形学理论分类去找到透明原理这一篇博客看明白。
以上是前提,那么接下来入正题,我们知道透明其实也就是颜色缓冲区中已存在的片段颜色值ColorFrame和需要覆盖的片段颜色值ColorGenerated进行“混合”计算得到新的片段颜色值ColorFinal,然后ColorFinal更新到颜色缓冲区渲染。那么这个“混合”计算的过程就很重要,unity cg runtime一直以来就为我们提供了很齐全的“混合”计算,同时也提供了详细的文档,如下:
首先是blend操作所处的渲染流程具体位置。
Blend SrcFactor DstFactor
: Configure and enable blending. The generated color is multiplied by the SrcFactor. The color already on screen is multiplied by DstFactor and the two are added together.
配置和启用blend,首先对ColorGenerated乘SrcFactor,然后对颜色缓冲区中已存在的片段ColorFrame乘DstFactor,然后两者相加。这里要解释一下,ColorGenerated也就是“透明片段颜色”,也就是要去主动混合颜色缓冲区中片段ColorFrame,通俗简单一点来说就是你的shader/material中采样的(纹理)颜色(带有Alpha通道)。
Blend factors
All following properties are valid for both SrcFactor & DstFactor in the Blend command. Source refers to the calculated color, Destination is the color already on the screen. The blend factors are ignored if BlendOp is using logical operations.
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). |
上面是一些混合参数,这些混合参数方便我们进行混合渲染,这很重要,下面我来写几个例子让大家好理解一下,如下:
1. Blend SrcAlpha OneMinusSrcAlpha
首先使用blend命令去实现混合
Shader "Custom/BlendShader"
{
Properties
{
_MainColor ("Main Color", Color) = (1,1,1,1)
_SrcAlpha("Src Alpha",Range(0,1)) = 0
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
LOD 100
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
float4 _MainColor;
float _SrcAlpha;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _MainColor;
col.a = _SrcAlpha;
return col;
}
ENDCG
}
}
}
效果如下:
可以很明显看得出来,首先红色背景为颜色缓冲区中已存在的颜色值ColorFrame,然后蓝色为所谓的“generated color”ColorGenerated,SrcAlpha从0->1那么OneMinusSrcAlpha英文意思就是1-SrcAlpha从1->0,所以最终的“混合”颜色ColorFinal:
ColorFinal= ColorGenerated* SrcAlpha + ColorFrame* (1-SrcAlpha)
脑海中仔细想一下就能得到效果:蓝色慢慢消失,红色慢慢显现。
得到上面的效果后,为了验证,我特意使用了一个Opaque渲染类型的shader去手动计算“透明混合”,如下:
Shader "Custom/SimulateBlendShader"
{
Properties
{
_BackColor("Back Color",Color) = (1,1,1,1)
_MainColor("Main Color", Color) = (1,1,1,1)
_SrcAlpha("Src Alpha",Range(0,1)) = 0
}
SubShader
{
Tags { "RenderType" = "Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
float4 _BackColor;
float4 _MainColor;
float _SrcAlpha;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = _SrcAlpha * _MainColor + _BackColor * (1 - _SrcAlpha);
return col;
}
ENDCG
}
}
}
效果如下:
可以很明显看的出来验证正确。
2.Blend SrcAlpha One
One | The value of one - use this to let either the source or the destination color come through fully. |
官方意思就是DstFactor = 1,所以得到的“混合”效果如下:
ColorFinal= ColorGenerated* SrcAlpha + ColorFrame* 1
Blend SrcAlpha One
脑海中想象一下:当SrcAlpha从0到1插值,那么背景色红色(1,0,0,1)和前景色蓝色(0,0,1,1)混合色最终混合成粉色(1,0,1,1)
为了节省篇幅,我不在贴全shader代码,只贴关键部分。
接下来使用Opaque去手动计算混合,如下:
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = _SrcAlpha * _MainColor + _BackColor * 1;
return col;
}
也得到了验证。
3.Blend DstColor SrcAlpha
DstColor | The value of this stage is multiplied by frame buffer source color value. |
请注意,这里开始就和上面不一样了,因为使用了Color这种float4值作为系数去计算的,那么这条blend命令实际的意义就是:
ColorFinal = ColorGenerated * DstColor + ColorFrame * SrcAlpha
我来解释一下,DstColor也就是帧颜色缓冲区中原始颜色(等价于ColorFrame),其作为系数去乘generated color,再加上颜色缓冲区ColorFrame乘SrcAlpha,得到最终颜色ColorFinal,效果如下:
Blend DstColor SrcAlpha
顺便来解释一下,这种混合现象:
①.当SrcAlpha==0时(ColorFrame * SrcAlpha = (0,0,0,1)),将背景色(DstColor)从白色(1,1,1,1)插值到红色(1,0,0,1)或黑色(0,0,0,1),其前景色(ColorGenerated)蓝色(0,0,1,1)做乘法就会趋向于黑色(0,0,0,1)。
②.当背景色(DstColor)为红色(1,0,0,1)和前景色(ColorGenerated)为蓝色(0,0,1,1)时(ColorGenerated * DstColor = (0,0,0,1)),SrcAlpha从0插值到1,那么ColorFrame * SrcAlpha则从黑色(0,0,0,1)趋向于背景色(ColorA)红色(1,0,0,1)。
接下来手动计算这种混合算法,如下:
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = _MainColor * _BackColor + _BackColor * _SrcAlpha;
return col;
}
注意:同学们脑海中仔细推算一下,务必要清楚明白。
4.Blend SrcAlpha OneMinusDstAlpha
OneMinusDstAlpha | The value of this stage is multiplied by (1 - destination alpha). |
首先,我们可以很容易的将公式写出来:
ColorFinal = ColorGenerated * SrcAlpha + ColorFrame * (1-DstAlpa)
请注意,这也是一个典型的特例,DstAlpha意思就是帧颜色缓冲区中的Alpha值,那么也就意味着背景色(ColorFrame)是带有Transparent Alpha通道的,那么我首先要给背景板的shader改成支持透明通道,同时Blend Off关闭混合,为了测试我们的ColorGenerated混合效果,如下:
Shader "Custom/ColorAlphaShader"
{
Properties
{
_MainColor ("Main Color", Color) = (1,1,1,1)
_DstAlpha("Dst Alpha",Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType"="Transparent" "Queue"="Transparent"}
LOD 100
Pass
{
Blend off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
float4 _MainColor;
float _DstAlpha;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = _MainColor;
col.a = _DstAlpha;
return col;
}
ENDCG
}
}
}
接下来观察这种混合命令的效果,如下:
Blend SrcAlpha OneMinusDstAlpha
我来解释一下这种现象的原因,如下:
①.当背景色(ColorFrame)的DstAlpha==1,前景色(ColorGenerated)的SrcAlpha==1,那么混合中ColorFrame * (1-DstAlpa)则为(0,0,0,0),则ColorFinal为前景色(ColorGenerated)蓝色(0,0,1,1)。
②.当背景色(ColorFrame)的DstAlpha从1插值到0,那么混合中ColorFrame * (1-DstAlpa)则为(0,0,0,0)到红色(1,0,0,1),则ColorFinal为慢慢变成粉红色(1,0,1,1)。
还是要同学们仔细在脑海中演算琢磨。
接下来就来模拟混合计算了,如下:
fixed4 frag(v2f i) : SV_Target
{
fixed4 col = _MainColor * _SrcAlpha + _BackColor * (1 - _DstAlpha);
return col;
}
结果一致,很正常。
好了,其他的我就不模拟了,上面只挑四个突出的典型混合讲解,看完理解完,我觉得同学们对于透明混合基本就摸透了。
以上属于图形理论知识,我说过的,数学算法和原理理论是最重要的东西,请务必牢记。