透明度:透明度为1,物体完全不透明;为0,完全透明不显示。实现透明两种方式,透明度测试,透明度混合
深度缓冲(z-buffer):根据深度缓冲里的值来判断该片元距离摄像机的距离,判断某片元的深度值与深度缓存中的值进行比较,该值距离摄像机更远,则不进行渲染;否则,该片元的值覆盖掉颜色缓冲中的像素值,并把深度值进行更新。
深度测试:进行深度值的比较。
Z缓冲器 --保存各像素处物体深度值
深度写入:深度值的更新。
帧缓冲器 – 保存各像素颜色值。当物体渲染时,通过帧缓冲器,写入的是颜色缓冲
1.当ZWrite为On时,ZTest通过时,该像素的深度才能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值也会写入颜色缓存。
2.当ZWrite为On时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
3.当ZWrite为Off时,ZTest通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值会写入颜色缓存。
4.当ZWrite为Off时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。
透明度测试:先将Z缓冲器中每个单元的初始值置为最小值。当要改变某个像素的颜色值时,首先检查当前多边形的深度值是否大于该像素原来的深度值(保存在该像素所对应的Z缓冲器的单元中),如果大于,说明当前多边形更靠近观察点,用它的颜色替换像素原来的颜色;否则说明在当前像素处,当前多边形被前面所绘制的多边形遮挡了,是不可见的,像素的颜色值不改变。
透明度测试:通过片元的透明度与阈值进行比较,确定某片元是否渲染
透明度混合:达到半透明效果(关闭深度写入,不关闭深度测试)使用当前片元的透明度作为混合因子,与存储在颜色缓存中的颜色值进行混合,得到新颜色。
渲染顺序很重要(在关闭深度写入之后)
一般的渲染顺序是:
(1)先渲染所有不透明物体,并且开起深度测试和深度写入
(2)把半透明物体按照摄像机距离进行排序,从后往前进行渲染。开启深度测试,关闭深度写入。
一.透明度测试:
// Upgrade NOTE: replaced ‘mul(UNITY_MATRIX_MVP,)’ with 'UnityObjectToClipPos()’
Shader "Custom/Shadser8.1" {
Properties {
_Color ("Main Tint", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Cutoff("Alpha Cutoff",Range(0,1))=0.5
}
SubShader {
/*
"Queue"="AlphaTest":Unity中透明度测试使用的渲染队列是名为AlphaTest的队列
"IgnoreProjectors"="True":意味着这个Shader不会受到投影器的影响
RenderType标签可以让Unity把这个Shader归入到提前定义的组(这里就是TransparentCutout组)
通常使用了透明度测试的shader都应该在SubShader中设置这3个标签
*/
Tags{"Queue"="AlphaTest""IgnoreProjectors"="True""RenderType"="TransparentCutout"}
Pass
{
Tags{"LightMode"="ForwardBase"}
//Cull Off//控制双面渲染开关
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
fixed _Cutoff;
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);//等价于o.uv=v.texcoord.xy* _MainTex_ST.xy(Tilling)+_MainTex_ST.zw(OffSet);
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);
clip(texColor.a-_Cutoff);//当texColor.a小于材质参数_Cutoff时u,该片元就会产生完全透明的效果,相当于一个判定,随着_Cutoff增大,texColor.a-_Cutoff会逐渐减小,当小于0时,则全透明
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"
}
效果较为极端,要么渲染,要么完全透明。
二.透明度混合
//透明度混合原理:用_AlphaScale影响当前图形的a值,实现透明度的改变。
Shader "Custom/shader8.2" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_AlphaScale("Alpha Scale",Range(0,1))=1
}
//任何使用了透明度混合(关闭了深度写入的Shader)的物体都应该使用Transparent队列
//blend命令两个作用:1.打开混合模式 2.设置混合因子
SubShader {
Tags{"Queue"="Transparent" "IgnoreProjectors"="True""RenderType"="Transparent"}
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
//Cull Off//控制双面渲染开关
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);//等价于o.uv=v.texcoord.xy* _MainTex_ST.xy(Tilling)+_MainTex_ST.zw(OffSet);
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));
//用_AlphaScale影响当前图形的a值,实现透明度混合
return fixed4(ambient+diffuse,texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
如上所示,关闭了深度写入,但是在自身发生遮挡的时候,无法确定渲染顺序,往往造成错误的效果。因此需要想办法,重新开启深度写入。
三.可以同时开启深度写入和透明度混合,但需要放在两个pass中
第一个pass,得到了逐像素的的正确的深度信息(开深度写入,不写入颜色 ColorMask 0)
第二个pass,正常的透明度混合
就可以有效的避免,比如自身遮挡带来的透明现象错误
Shader "Custom/shader8.3" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_AlphaScale("Alpha Scale",Range(0,1))=1
}
SubShader {
Tags{"Queue"="Transparent" "IgnoreProjectors"="True""RenderType"="Transparent"}
pass{
ZWrite On
ColorMask 0
}
Pass
{
Tags{"LightMode"="ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
//Cull Off//控制双面渲染开关
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);//等价于o.uv=v.texcoord.xy* _MainTex_ST.xy(Tilling)+_MainTex_ST.zw(OffSet);
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));
//用_AlphaScale影响当前图形的a值,实现透明度混合
return fixed4(ambient+diffuse,texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
效果如图所示:
四.ShaderLab的混合命令
混合如何实现?
片元着色器产生一个颜色时,可以与颜色缓存中的颜色进行混合。
源颜色:片元着色器产生的颜色(s)
目标颜色:从颜色缓存器中读取的颜色(D)
混合后输出的颜色:(O)
上述三者都包含了RGBA四个值。
要使用混合时,除了设置混合状态外,还要开启混合。
混合等式:有S,有D,算O,需要一个等式来进行计算,该等式称为混合等式。
混合时,我们需要两个混合等式:一个混合RGB通道,一个混合A通道。
在设置混合状态时,设置的就是混合等式的操作和因子,此时操作默认是加操作,因子有四个,两个等式,每个等式,每个等式两个因子
五.双面渲染
1.深度测试的双面渲染:
在之前的渲染中,只渲染了面对摄像机的部分,背面没有渲染,使用 Cull off命令,关闭剔除背面的功能。实现深度测试的双面渲染。此外,还可以Cull Back/ Front /Off 剔除 背面/正面/关闭
2.透明度混合的双面渲染
在开启深度写入的情况下,需要保证正确的渲染顺序,两个pass,第一个只渲染背面,cull front,然后进行正常的透明度混合操作。第二个,只渲染正面,cull back,然后进行正常的透明度混合操作。
Shader "Unity Shaders 8.4" {
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"
}