UnityShader⼊门教程(九):开启深度写⼊的半透明效果在上篇⽂章有讲到实现透明度混合时需要关闭深度写⼊。⽽为了不造成错误的排序效果,渲染引擎⼀般都会先对物体进⾏排序,再渲染。常⽤的⽅法如下:(对于上篇⽂章shader代码中使⽤的渲染队列Transparent)
1. 先渲染所有不透明物体,并开启他们的深度测试和深度写⼊;
2. 把半透明物体按他们距离摄像机的远近进⾏排序,然后按照从后往前的顺序渲染这些半透明物体,并开启他们的深度测试,但关闭深
度写⼊;
其实这样做并没有解决所有情况下的错误的排序问题。在⼀些情况下,根据“距离摄像机远近”并不能解决问题。这是因为深度缓冲中的值其实是像素级别的,即每个像素有⼀个深度值,但是现在我们对单个物体级别进⾏排序,这意味着排序结果是,要么物体A全部在B前⾯渲染,要么A全部在B后⾯渲染。但如果存在循环重叠的情况,那么使⽤这种⽅法就永远⽆法得到正确的结果。如下展⽰了⽆法使⽤上述⽅法解决的情况:
图8.3中由于3个物体互相重叠,我们不可能得到⼀个正确的排序顺序。这种时候,我们可以选择把物体拆分成2个部分,然后再进⾏正确的排序。但是如果遇到图8.4的情况,如何判断他们距离摄像机远近呢?我们知道,⼀个物体的⽹格结构往往占据了空间中的莫⼀块区域,也就是说⽹格上的每⼀个点的深度值都是不⼀样的,那我们选择图中哪个点来判断距离摄像机远近呢?答案是不管选择哪个点都不合理。
所以,为了解决这个问题,⼀种⽅法是使⽤2个Pass来渲染模型:第⼀个Pass开启深度写⼊,但不输出颜⾊,它的⽬的仅仅是为了把该模型的深度值写⼊深度缓冲中;第⼆个Pass进⾏正常的透明度混合,由于上⼀个pass已经得到了逐像素的正确的深度信息,该Pass就可以按照像素级别的深度排序结果进⾏透明渲染。但这种⽅法的缺点是,多使⽤⼀个Pass会对性能造成⼀定影响,并且对于模型内部不会有半透明效果。
开始深度写⼊的半透明混合的shader代码如下:
Shader "Custom/AlphaBlendZWrite"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Main Tex", 2D) = "white" {}
_AlphaScale("Alpha Scale", Range(0, 1)) = 1
}
SubShader
{
/
/"Queue" = "Transparent"表⽰开启了透明度混合的都要使⽤此模式,"IgnoreProjector" = "True表⽰该shader不会受投影器(Projector)影响
//"RenderType" = "TransparentCutout"表⽰让unity把这个shader归⼊到提前定义的组(这⾥指TransparentCutout)
//⼀般开启透明度测试需要设置这三个标签
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
Pass
{
//开启深度写⼊
ZWrite On
//ColorMask语义有以下⼏种:ColorMask RGB|A|0|其他任何RGB组合
//为0时代表该Pass不写⼊任何颜⾊通道,即不会输出任何颜⾊
ColorMask 0
}
Pass
{
Tags{"LightMode" = "ForwardBase"}
//关闭深度写⼊
ZWrite Off
//该博客中有讲
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "inc"
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 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 = al);
//模型空间转世界空间下的坐标
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//计算纹理坐标
o.uv = TRANSFORM_rd, _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 = b * _b;
//环境光计算
fixed3 ambient = UNITY_ * albedo;
//漫反射光计算
fixed3 diffuse = _b * albedo * max(0, dot(worldNormal, worldLightDir));
//texColor.a * _AlphaScale,表⽰纹理的透明通道与_AlphaScale变量混合
return fixed4(ambient + diffuse, texColor.a* _AlphaScale);
}
ENDCG
ENDCG
}
}
FallBack "Diffuse"
}
该Shader代码的结果的如下图:
由此可见该透明效果并不能看到正⽅体的内部。⽽现实⽣活中,如果⼀个物体是透明的,意味着我们不仅可以透过它看到其他物体的样⼦,也可以看到它的内部结构。⽽造成这种结果的原因是,默认情况下渲染引擎剔除了物体背⾯的渲染图元,⽽只渲染了物体的正⾯。因此我们需要得到双⾯的渲染效果。Unity中可以⽤Cull指令来控制需要剔除哪个⾯的渲染图元。指令如下:
Cull Back | Front | Off
如果设置为Back,那么那些背对着相机的图元就不会得到渲染,也是默认情况下的剔除状态;如果设置为Front,那么那些朝向摄像机的渲染图元就不会得到渲染;如果设置为Off,就会关闭剔除功能,所有图元都将得到渲染,但此时需要渲染的图元成倍增加,影响性能。
因此,我们还需要实现透明度混合的双⾯渲染效果,使效果更接近现实情况。⽽实现此功能,只需要将正⾯和反⾯都渲染⼀遍,并保证反⾯的渲染在正⾯的渲染之前,以此来保证正确的深度渲染关系(原因(⼋)⽂章中有讲)。
unity shader中SubShader下的Pass会按照从上往下的顺序依次渲染,因此,我们第⼀个Pass只渲染背⾯,第⼆个Pass只渲染正⾯,从⽽保证背⾯总在正⾯之前被渲染。具体shader代码如下(两个Pass代码⼀致,唯⼀的区别只是⼀个关闭了正⾯的渲染,⼀个只关闭了背⾯的渲染):
Shader "Custom/AlphaBlendBothSided"
{
Properties
{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Main Tex", 2D) = "white" {}
_AlphaScale("Alpha Scale", Range(0, 1)) = 1
}
SubShader
{
//"Queue" = "Transparent"表⽰开启了透明度混合的都要使⽤此模式,"IgnoreProjector" = "True表⽰该shader不会受投影器(Projector)影响
//"RenderType" = "TransparentCutout"表⽰让unity把这个shader归⼊到提前定义的组(这⾥指TransparentCutout)
//⼀般开启透明度测试需要设置这三个标签
Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
//关闭正⾯的渲染
Cull Front
//关闭深度写⼊
ZWrite Off
//该博客中有讲
Blend SrcAlpha OneMinusSrcAlpha
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "inc"
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 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 = al);
//模型空间转世界空间下的坐标
unity 教程
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//计算纹理坐标
o.uv = TRANSFORM_rd, _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 = b * _b;
//环境光计算
fixed3 ambient = UNITY_ * albedo;
//漫反射光计算
fixed3 diffuse = _b * albedo * max(0, dot(worldNormal, worldLightDir));
//texColor.a * _AlphaScale,表⽰纹理的透明通道与_AlphaScale变量混合
return fixed4(ambient + diffuse, texColor.a* _AlphaScale);
}
ENDCG
}
Pass
{
Tags{"LightMode" = "ForwardBase"}
//关闭背⾯渲染
Cull Back
Cull Back
//关闭深度写⼊
ZWrite Off
//该博客中有讲
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "inc"
float4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
float _AlphaScale;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 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 = al);
//模型空间转世界空间下的坐标
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//计算纹理坐标
o.uv = TRANSFORM_rd, _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 = b * _b;
//环境光计算
fixed3 ambient = UNITY_ * albedo;
/
/漫反射光计算
fixed3 diffuse = _b * albedo * max(0, dot(worldNormal, worldLightDir));
//texColor.a * _AlphaScale,表⽰纹理的透明通道与_AlphaScale变量混合
return fixed4(ambient + diffuse, texColor.a* _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论