1.前言
曾经做过一个项目,要求一个实体模型能够半透明显示,并高亮其中的某一部分。采用unity标准(standard)材质,通过将RenderMode改为transparent,然后通过MeshRender组件更改颜色透明通道进行实现。但是结果不尽人意,这是由于模型之间交互覆盖。很难周全,再次对Unity混合问题进行简析。
2.渲染队列
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
渲染队列,表示那部分先渲染,并非完全按照深度值来进行。在同一个渲染队列的对象会按深度进行渲染,关闭深度写入的,则按游戏对象位置来处理。
Background:顾名思义,表示背景队列,此渲染队列的物体最先被渲染。
Geometry:表示正常的结合对象,在Backgroud队列后被渲染。
AlphaTest:透明度测试,通过clip可以对color的alpha值进行处理,当大于某一值时就discard掉(可以关闭深度写入ZWrite Off)。
Transparent:表示半透明队列,此队列的对象一般都是需要进行半透明处理,并关闭深度写入。并且为了保证叠加效果,其在Geometry和AlphaTest之后渲染。半透明处理时要开启混合,即Blend
SrcAlpha OneMinusSrcAlpha,关闭混合则为Blend Off。
Overlay:跟Backgound一样,顾名思义,是最后覆盖之意,表示最后被渲染的一层。
3.透明度混合
上文中在Transparent中简单说明透明度混合的关闭开启,再次详细说明一下。混合的意思为上一次buffer中颜色与新计算出来的颜色是否叠加或者覆盖的意思。
3.1 Blend命令格式
基本格式为Blend+参数:
Blend参数形式 | 含义 |
---|---|
Off | 关闭混合 |
factorA factorB | 表示Blend后面加两个参数,以空格分开,将源颜色成以factorA与目标颜色成以factorB相加结果存入颜色缓冲区 |
factorA factorB alphaA alphaB | 表示Blend后面加四个参数,以空格分开将源颜色成以factorA与目标颜色成以factorB相加结果存入颜色缓冲区,但是混合通道用后两个参数来叠加 |
如Blend SrcAlpha OneMinusSrcAlpha。其中SrcAlpha为factorA,OneMinusSrcAlpha为factorB。
factorA,factorB以及alphaA值如下所示。
3.2 Blend参数释义
Blend参数 | 意义 |
---|---|
One/Zero | 1/0 |
SrcColor/DstColor | 源颜色值/目标颜色值 |
SrcAlpha/DstAlpha | 源颜色透明通道值/目标颜色值透明通道值 |
OneMinusSrcAlpha/OneMinusDstAlpha | 1-源颜色透明通道值/1-目标颜色值透明通道值 |
OneMinusSrcColor/OneMinusDstColor | 1-源颜色值/1-源颜色值 |
如Blend SrcAlpha OneMinusSrcAlpha表示用源颜色的透明通道值乘以源颜色+目标颜色值乘以(1-源颜色透明通道值)。
3.3 Blend混合方式
上述所说的混合方式均为相加方式,是默认方式,也可以通过BlendOp命令更改为相减或者其他。如下所示:
BlendOp Sub//相减
Blend One One
还有RevSub,Min,Max等。
4.实例分析
4.1 透明度测试
透明度测试用于根据透明程度决定是否剔除此像素(也可以在不透明渲染时采用某一颜色值去处理)。此时要开启透明度测试,否则用像素alpha进行判断无效。如下所示,下述代码关闭了剔除功能,如果不需要看到物体内部,可以开启剔除,只渲染一面。
Shader "LL/AlphaTest"
{
Properties
{
_MainTex("Main Texture",2D)="white"{}
//_DissolveTex("Dissolve Cutoff",2D)="white"{}
_Cutoff("Dissolve Cutoff",Range(0,1))=1
}
SubShader
{
Tags {"Queue"="AlphaTest" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
pass
{
Tags {"LightMode"="ForwardBase"}
Cull off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//sampler2D _DissolveTex;
float _Cutoff;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
clip(mainTexColor.a-_Cutoff);
//return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
return mainTexColor;
}
ENDCG
}
}
}
4.2 透明度混合
如果想通过更改alpha值更改模型透明度,则需要开启透明度混合。此时要关闭深度写入,如果需要深度值,可以在其他pass中开启深度写入,但不输出任何颜色值。
Shader "LL/AlphaBlend"
{
Properties
{
_MainTex("Main Texture",2D)="white"{}
//_DissolveTex("Dissolve Cutoff",2D)="white"{}
_Cutoff("Dissolve Cutoff",Range(0,1))=1
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
// pass
// {
// Tags {"LightMode" = "ForwardBase"}
// Cull Front
// ZWrite On
// ColorMask 0
// }
pass
{
Tags {"LightMode"="ForwardBase"}
//Cull Back
//Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//sampler2D _DissolveTex;
float _Cutoff;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
}
ENDCG
}
}
}
4.3 透明度混合+双面渲染
双面渲染可以在上述shader中直接Cull Off来实现,但是会看着不是很自然(如果不对比下述代码的效果时看不出来的),所以一般是先剔除正面,结束后再剔除背面。
Shader "LL/AlphaBlendTwoSides"
{
Properties
{
_MainTex("Main Texture",2D)="white"{}
//_DissolveTex("Dissolve Cutoff",2D)="white"{}
_Cutoff("Dissolve Cutoff",Range(0,1))=1
}
SubShader
{
Tags {"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="TransparentCutout"}
// pass
// {
// Tags {"LightMode" = "ForwardBase"}
// Cull Front
// ZWrite On
// ColorMask 0
// }
pass
{
Tags {"LightMode"="ForwardBase"}
Cull Front
//Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//sampler2D _DissolveTex;
float _Cutoff;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
}
ENDCG
}
pass
{
Tags {"LightMode"="ForwardBase"}
Cull Back
//Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
//sampler2D _DissolveTex;
float _Cutoff;
struct a2v
{
float4 vertex:POSITION;
float2 uv:TEXCOORD0;
};
struct v2f
{
float2 uv:TEXCOORD0;
float4 vertex:SV_POSITION;
};
v2f vert(a2v v)
{
v2f f;
f.uv=v.uv;
f.vertex=UnityObjectToClipPos(v.vertex);
return f;
}
float4 frag(v2f f):SV_TARGET
{
float4 mainTexColor=tex2D(_MainTex,f.uv);
return float4(mainTexColor.rgb,mainTexColor.a * _Cutoff);
}
ENDCG
}
}
}
5.结语
半透明问题比较复杂,但实际操作起来却没有那么繁杂。