unity实现透明效果
效果图理论Alpha TestAlpha Blend改进开启深度写入的半透明效果双面渲染的透明效果 代码部分效果图
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、透明度混合:
开启深度测试,关闭深度写入(深度缓冲是只读的)
得到真正的半透明效果,使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。
为什么要关闭深度写入?
如果不关闭深度写入,半透物体后面的表面将会被剔除,就无法透过半透明表面看到后面的物体了
为什么要非常小心物体的渲染顺序?
为了渲染的正确性
如何排序?
尽可能让模型是凸面体,并且考虑将复杂的模型拆分成可以独立排序的多个子模型等
unity Shader的渲染顺序
透明度测试
透明度混合
Alpha Test
【效果】
默认剔除背面,在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 Blend
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; // albedofixed3 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 进行正常的透明度混合,但是对性能有一定影响
// 新加一个PassPass{Zwrite OnColorMask 0 // }
ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色
效果:
双面渲染的透明效果
默认情况下渲染引擎剔除了物体背面(相对于摄像机的方向)的渲染图元,而只渲染了物体的正面
Cull Back I Front I Off
如果设置为 Off,就会关闭剔除功能,那么所有的渲染图元都会被渲染,计算量成倍增加,因此除非是用于特殊效果,例如这里的双面渲染的透明效果,通常是不会关闭剔除功能的
透明度测试的双面渲染
Cull Off透明度混合的双面渲染
分成两个 Pass——第一个 Pass 渲染背面(Cull Front),第二个 Pass只渲染正面(Cull Back),由于 Unity 会顺序执行 SubShader 中的各个 Pass, 从而可以保证先渲染背面,后渲染正面,渲染关系正确
对比:
代码部分
【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的内置光照变量,例如_LigbtColorOCull 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 testclip(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个标签,透明度测试用的是TransparentCutoutTags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}// 新加一个Pass,按照像素级别的深度排序结果进行透明渲染,但是对性能有一定的影响Pass{Zwrite OnColorMask 0 // ColorMask 用于设置颜色通道的写掩码 write mask,0代表 Pass 不写入任何颜色通道,即不会输出任何颜色}Pass {Tags {"LightMode" = "ForwardBase" } // 正确提供各个光照变量// Cull OffZWrite Off // 深度只读Blend SrcAlpha OneMinusSrcAlpha // 开启并设置了该Pass的混合模式[重要],将源颜色(该片元着色器产生的颜色)的混合因子设为SrcAlpha, 把目标颜色(已经存在于颜色缓冲中的颜色)的混合因子设为OneMinusSrcAlpha// BlendOp MinCGPROGRAM#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; // albedofixed3 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 FrontZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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 BackZWrite OffBlend SrcAlpha OneMinusSrcAlphaCGPROGRAM#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"}