Alpha Test
Alpha Test With Both Side:Cull Off关闭剔除
Alpha Blend Scale = 0.5
AlphaBlendZWrite:新加一个Pass,Zwrite On, 按照像素级别的深度排序结果进行透明渲染
Alpha Blend With Both Side:两个Pass,Cull Front Cull Back
景中既有不透明物体,又有半透明物体时,渲染引擎一般都会先对物体进行排序,再渲染
(1) 先渲染所有不透明物体,开启它们的深度测试和深度写入,渲染顺序为从前往后(Front-to-Back)
(2) 再渲染半透明物体,开启它们的深度测试,但关闭深度写入,按它们距离摄像机的远近进行排序,渲染顺序为从后往前(Back-to-Front)
【渲染不透明物体】
不考虑渲染顺序也能得到正确的排序效果,利用深度缓冲(z-buffer)解决可见性问题。
当渲染每个片元时, 需要把它的深度值和已经存在于深度缓冲中的值进行比较(如果开启了深度测试),如果它的值距离摄像机更远,那么说明这个片元不应该被渲染到屏幕上(有物体挡住了它);否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把它的深度值更新到深度缓冲中(如果开启了深度写入)
为什么从前往后渲染?
减少深度颜色缓冲器的写入操作,提升性能,避免overdraw
【渲染透明物体】
通过控制透明通道 (Alpha Channel) 实现透明效果
1、透明度测试:
不需要关闭深度测试和深度写入
产生的效果很极端,要么完全透明,即看不到,要么完全不透明,就像不透明物体那样。只要片元的透明度不满足条件(通常是小千某个阙值),那么就会被舍弃。否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。
2、透明度混合:
开启深度测试,关闭深度写入(深度缓冲是只读的)
得到真正的半透明效果,使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。
为什么要关闭深度写入?
如果不关闭深度写入,半透物体后面的表面将会被剔除,就无法透过半透明表面看到后面的物体了
如何排序?
尽可能让模型是凸面体,并且考虑将复杂的模型拆分成可以独立排序的多个子模型等
【效果】
默认剔除背面,在Pass里设置Cull Off关闭剔除
得到的透明效果在边缘处往往参差不齐有锯齿,这是因为在边界处纹理的透明度的变化精度问题
【代码解析】
跟渲染普通的不透明物体几乎是一样的
FS:增加了对透明度判断并裁剪片元
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1) // Diffuse
_MainTex("Main Tex", 2D) = "white" {}
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5 // cutoff
}
SubShader{
Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}
Pass {
Tags { "LightMode" = "ForwardBase" } // 定义了正确的 LigbtMode,才能正确得到一些 Unity的内置光照变量,例如_LigbtColorO
透明度测试的subshader的固定三个标签
“Queue” = “AlphaTest” : 透明度测试使用的渲染队列
“IgnoreProjector” = “True”:这个 Shader 不会受到投影器(Projectors)的影响
“RenderType” = “TransparentCutout”: 该 Shader是个使用了透明度测试的Shader
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff; // 范围在[O,1], 使用 fixed 来存储
和Properties 语义块中声明的属性建立联系
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
VS略,同纹理映射
FS
fixed4 texColor = tex2D(_MainTex, i.uv);
clip(texColor.a - _Cutoff);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
clip CG 中的一个函数
if ((texColor.a - _Cutoff) < 0.0) {
discard;
}
FallBack "Transparent/Cutout/VertexLit"
和之前使用的 Diffuse Specular 不同,能够保证SubShader 无法在当前显卡上工作时有合适的代替 Shader, 还可以保证使用透明度测试的物体可以正确地向其他物体投射阴影
alpha scale = 0.3
alpha scale = 0.8
【代码解析】
FS:把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 2D) = "white" {}
_AlphaScale("Alpha Scale", Range(0, 1)) = 1 // 控制透明度
}
SubShader{
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
使用了透明度混合的 Shader都应该在 SubShader 中设置这3个标签,透明度测试用的是TransparentCutout,透明度混合用的是Transparent
Pass {
Tags { "LightMode" = "ForwardBase" } // 正确提供各个光照变量
ZWrite Off // 深度只读
Blend SrcAlpha OneMinusSrcAlpha //
开启并设置了该Pass的混合模式(重要!!)
将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha, 把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha
当设置混合状态时,我们实际上设置的就是混合等式中的操作和因子
混合操作:
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
VS略
FS:
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb; // albedo
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
先采样纹理贴图,然后设置该FS返回值中的透明通道 (= 纹理像素的透明通道* AlphaScale )
由于关闭深度写入,无法对模型进行像素级别的深度,当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果
一种解决方法是分割网格
一种解决方法是使用两个Pass来渲染模型,第一个 Pass 开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个Pass 进行正常的透明度混合,但是对性能有一定影响
// 新加一个Pass
Pass{
Zwrite On
ColorMask 0 //
}
ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色
默认情况下渲染引擎剔除了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面
Cull Back I Front I Off
如果设置为 Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,计算量成倍增加,因此除非是用于特殊效果,例如这里的双面渲染的透明效果,通常是不会关闭剔除功能的
【alpha test】
Shader "Custom/AlpbaTest"
{
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1) // Diffuse
_MainTex("Main Tex", 2D) = "white" {}
_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5 // cutoff
}
SubShader{
// 透明度测试的subshader的固定三个标签
// AlphaTest: 透明度测试使用的渲染队列
// TransparentCutout:该 Shader是个使用了透明度测试的Shader
// "IgnoreProjector" = "True":这个 Shader 不会受到投影器(Projectors)的影响
Tags {"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}
Pass {
Tags { "LightMode" = "ForwardBase" } // 定义了正确的 LigbtMode,才能正确得到一些 Unity的内置光照变量,例如_LigbtColorO
Cull Off // 关闭剔除,看到背面
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff; // 范围在[O I], 使用 fixed 来存储
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
// Alpha test
clip(texColor.a - _Cutoff);
// Equal to
// if ((texColor.a - _Cutoff) < 0.0) {
// discard;
// }
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, 1.0);
}
ENDCG
}
}
FallBack "Transparent/Cutout/VertexLit"
}
【Alpha Blend】
// Q1
// 当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果。
// 新加一个 Pass,开启深度写入,但不输出颜色; 第二个Pass 进行正常的透明度混合
// 就可以按照像素级别的深度排序结果进行透明渲染,但是对性能有一定的影响
// Q2 双面渲染
// 第一个 Pass 渲染背面,第二个 Pass只渲染正面
// 保证正确的深度渲染关系
Shader "Custom/AlphaBlend"
{
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1)
_MainTex("Main Tex", 2D) = "white" {}
_AlphaScale("Alpha Scale", Range(0, 1)) = 1 // 控制透明度
}
SubShader{
// 通常,使用了透明度混合的 Shader都应该在 SubShader 中设置这3个标签,透明度测试用的是TransparentCutout
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
// 新加一个Pass,按照像素级别的深度排序结果进行透明渲染,但是对性能有一定的影响
Pass{
Zwrite On
ColorMask 0 // ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色
}
Pass {
Tags { "LightMode" = "ForwardBase" } // 正确提供各个光照变量
// Cull Off
ZWrite Off // 深度只读
Blend SrcAlpha OneMinusSrcAlpha // 开启并设置了该Pass的混合模式[重要],将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha, 把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha
// BlendOp Min
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb; // albedo
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale); // 纹理像素的透明通道 * _AlphaScale
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
【Alpha Blend With Both Side】
Shader "Custom/Alpha Blend With Both Side" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
Pass {
Tags { "LightMode"="ForwardBase" }
// First pass renders only back faces
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
Pass {
Tags { "LightMode"="ForwardBase" }
// Second pass renders only front faces
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed3 albedo = texColor.rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}