混合(Blending)
可编程图形管线中提到fragment shader为每一个片元(除非被discard
)计算RGBA颜色(以fragment输出参数COLOR
的形式)。
然后片元会按照在cg 逐片元操作中提到的过程被进一步处理。
这其中,提到了Blending
阶段,该阶段会把该片元输出的颜色(称之为source color
)同帧缓存中的颜色(称之为destination color
)进行处理。
Blending
是一个固定管线功能阶段,你只能指定其支持的操作,而不能对其进行编程处理。
Unity3D中通过Blend {code for SrcFactor} {code for DstFactor}
来指定,该表达式对应的数学运算法则为:
float4 result=
SrcFactor*fragment_output+DstFactor*pixel_color;
fragment_output为fragment shader阶段的输出参数,pixel_color是当前帧缓存中已经存在的颜色。最终的result就是blending的结果。
下表展示了SrcFactor和DstFactor常用的code(更多Unity ShaderLab支持的code参见ShaderLab: Blending):
code | expressiong |
---|---|
One | float4(1.0,1.0,1.0,1.0) |
Zero | float4(0.0,0.0,0.0,0.0) |
SrcColor | fragment_output |
SrcAlpha | fragment_output.aaaa |
DstColor | pixel_color |
DstAlpha | pixel_color.aaaa |
OneMinusSrcColor | float4(1.0,1.0,1.0,1.0)-fragment_output |
OneMinusSrcAlpha | float4(1.0,1.0,1.0,1.0)-fragment_output.aaaa |
OneMinusDstColor | float4(1.0,1.0,1.0,1.0)-pixel_color |
OneMinusDstAlpha | float4(1.0,1.0,1.0,1.0)-pixel_color.aaaa |
注意这里提到的所有颜色相关的值,最终都会被限制在[0,1]这个区间内。
Alpha混合
在Unity中Alpha混合等式为Blend SrcAlpha OneMinusSrcAlpha
。对应的数学表达式为:
float4 rsult=
fragment_output.aaaa*fragment_output+
(float4(1.0,1.0,1.0,1.0)-fragment_output.aaaa)*pixel_color;
该混合等式,有时候也被称之为覆盖
操作,即fragment_output覆盖pixel_color。
预乘Alpha混合
对于fragment shader输出color已经处理了alpha值的,不再需要对其再次处理,可以使用混合等式Blend One OneMinusSrcAlpha
。对应的数学表达式为:
float result=
flaot4(1.0,1.0,1.0,1.0)*fragment_output+
(float4(1.0,1.0,1.0,1.0)-fragment_output.aaaa)*pixel_color;
叠加混合(Additive Blending)
等式为:Blend One One
对应的数学表达式为:
float4 result=
float4(1.0,1.0,1.0,1.0)*fragment_output+
float4(1.0,1.0,1.0,1.0)*pixel_color;
该混合方式会把fragment shader的输出颜色值直接加在帧缓冲的颜色上。在粒子系统中会经常出现该混合方式。更多的讨论见Order-Independent Transparency
Shader代码
对于Alpha混合透明物体shader的实现,需要注意几个点:
1,SubShader的第一句需要增加Queue为Transparent的Tags。
Tags{"Queue"="Transparent"}
2,Pass中第一句要关闭深度缓冲ZWrite Off
(注意ZWrite Off 指令也可以写在SubShader层级下,那么将会影响所有的Pass)
3,Pass中紧接着在CGPROGRAM前面,需要声明混合模式,比如:Blend SrcAlpha OneMinusSrcAlpha。
Shader "My/Transparency/SrcAlpha_OneMinusSrcAlpha"
{
SubShader
{
Tags{"Queue"="Transparent"}
Pass
{
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertexInput{
float4 vertex:POSITION;
};
struct vertexOutput{
float4 pos:SV_POSITION;
float4 posInObjSpace:TEXCOORD0;
};
vertexOutput vert (vertexInput input)
{
vertexOutput output;
output.pos=UnityObjectToClipPos(input.vertex);
output.posInObjSpace=input.vertex;
return output;
}
fixed4 frag (vertexOutput input) : COLOR
{
return float4(0.0,1.0,0.0,0.3);
}
ENDCG
}
}
}
- 深度缓冲会记录片元的距离,根据算法决定哪些片元会自动被discard。因为透明物体的存在,被透明物体遮挡的物体需要可见,因此需要关闭深度缓冲。
Tags{"Queue"="Transparent"}
指定了使用该subshader的mesh在所有不透明(opaque)的mesh渲染完毕后再渲染。之所以要这样指定,部分原因是因为我们关闭了深度缓冲,这会导致一个透明物体被它后面的非透明物体遮挡。因此我们通过增加"Queue"="Transparent"的Tags来指定透明物体在非透明物体渲染完毕后再渲染(需要注意这种方式并不是经常奏效,更多参考:Order-Independent Transparency)
Unity中预定义的Queue包含以下几个:
Background
,index为1000
Geometry
,默认值,index为2000
AlphaTest
,index为2450
Transparent
,index为3000
Overlay
,index为4000,最后渲染的物体可以使用该Queue,比如lens flares.
更多关于Unity ShaderLab的Tags内容参考:ShaderLab: SubShader Tags