Unity_Shader学习(六)基础透明Shader
今天学习了简单的透明Shader的实现,真的是比想象中简单很多,但是有很多需要注意的点,特别是渲染顺序!
1. 渲染顺序
首先我们要知道,对于透明物体,他只开启了深度测试而没有开启深度写入
我们来考虑这样一种情况,物体A是透明的,物体B是不透明的。物体A在物体B前面,刚好遮住了B。
(1)如果我们先渲染物体B,B将会写入颜色缓冲与深度缓冲中。然后渲染物体A,我们发现物体A是透明物体,它将会进行深度测试,发现A比B离摄像机更近,因此我们将A的信息与颜色缓冲中的信息(即B的信息)进行混合,得到了正确的结果
(2)如果我们先渲染物体A,A发现颜色缓冲与深度缓冲是空的,于是它直接写入颜色缓冲。然后渲染B,B发现深度缓冲是空的,于是它不去考虑颜色缓冲有没有信息,直接用自己的信息覆盖了原来的颜色缓冲中的信息。这样视觉显示上B就在A前面了,得到错误的结果
据此,我们确定不透明物体必须先于透明物体渲染
我们来考虑另一种情况,物体A和B都是透明的,物体A在物体B前面,遮住了物体B。
(1)如果先渲染物体B,物体B的信息会写入颜色缓冲。然后渲染物体A,A会与颜色缓冲中的信息进行混合,得到相应的效果
(2)如果先渲染物体A,然后渲染物体B,我们就得到了刚好相反的结果...
据此,我们确定透明物体必须按由远至近的顺序渲染
2.透明度测试
在UnityShader中有以下几个渲染队列:
Background:这是最先渲染的队列,通常用于渲染背景信息
Geometry:一般不透明物体都在这个队列中
AlphaTest:需要透明度测试的物体使用这个队列
Transparent:进行透明度混合的物体使用这个队列
Overlay:该队列用于实现一些叠加效果
接下来我们来实现透明度测试,透明度测试是指,只要一个片元的透明度不满足条件,就舍弃这个片元。也就是说一个片元要么全部保留,要么全部舍弃,我们这样就得到完全透明与不透明两种极端的片元类型。
[code]Shader "Custom/MyAlphaTest" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _CutOff("CutOff",Range(0,1.0)) = 0.5 } SubShader { Tags{"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"} Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float _CutOff; struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; float4 texcoord:TEXCOORD0; }; struct v2f{ float4 pos:SV_POSITION; float2 uv:TEXCOORD0; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = normalize(mul(v.normal, unity_WorldToObject).xyz); o.uv = _MainTex_ST.xy * v.texcoord.xy + _MainTex_ST.zw; return o; } fixed4 frag(v2f i):SV_TARGET{ fixed3 worldNormal = i.worldNormal; fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed4 texColor = tex2D(_MainTex,i.uv); clip(texColor.a - _CutOff); fixed3 albedo = _Color.rgb * texColor.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldLightDir,worldNormal)); return fixed4(ambient + diffuse,1.0); } ENDCG } } FallBack "Diffuse" }
在这个Shader中,我们用_CutOff来表示透明度条件的阈值,在SubShader的标签中,我们标明队列为AlphaTest,设置IngnoreProjector为true来忽略投影,用RenderType将Shader归入组TransparentCutout中。在frag函数中,我们使用了clip函数,这个函数为:
[code]void clip(float4 x){ if(any(x<0)) discard; }
效果如下:
2.透明度混合
透明度混合比透明度测试复杂,为了进行混合,我们需要使用Blend命令:
Blend Off:关闭混合
Blend SrcFactor Dstfactor:开启混合并设置混合因子
Blend SrcFactor Dstfactor A B:和上面基本相同,只是使用了不同的因子A与B
Blend BlendOperation:使用特定函数
最终颜色=src因子 * 该物体颜色 + dst因子 * 颜色缓冲区颜色
[code]Shader "Custom/AlphaBlend" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("MainTex", 2D) = "white" {} _AlphaScale("Alpha Scale",Range(0, 1))=1 } SubShader { Tags{"Queue"="Transparent" "IgnoreProjector"="true" "RenderType"="Transparent"} Pass{ Tags{"LightMode"="ForwardBase"} ZWrite off Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "Lighting.cginc" fixed4 _Color; sampler2D _MainTex; float4 _MainTex_ST; float _AlphaScale; struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; float4 texcoord:TEXCOORD; }; struct v2f{ float4 pos:SV_POSITION; float3 worldPos:TEXCOORD0; float3 worldNormal:TEXCOORD1; float2 uv:TEXCOORD2; }; v2f vert(a2v v){ v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = normalize(mul(v.normal, unity_WorldToObject).xyz); o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return o; } fixed4 frag(v2f i):SV_TARGET{ float3 worldNormal = i.worldNormal; float3 LightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed4 texColor = tex2D(_MainTex, i.uv); fixed3 albedo = texColor.rgb * _Color; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; fixed3 diffuse = albedo * _LightColor0.rgb * saturate(dot(worldNormal, LightDir)); return fixed4(ambient + diffuse, texColor.a * _AlphaScale); } ENDCG } } FallBack "Diffuse" }
_AlphaScale用于设置透明度,Queue设置为Transparent,RenderType设置为Transparent。同时关闭深度写入ZWrite=Off,然后使用Blend命令设置混合为最终颜色为SrcAlpha * 该物体颜色 + (1-SrcAlpha) * 缓冲区颜色。效果如下:
3.开启深度写入的透明度混合
对于某些形状诡异扭来扭曲的物体....如果我们直接采用上面的透明度混合方法,就会出现这种情况:
这是由于我们关闭了深度写入造成的,那么怎么在实现透明的同时避免这个问题呢?
我们可以采用两个Pass,第一个Pass将模型深度值写入深度缓冲中,第二个Pass进行透明度混合。
第一个Pass实现如下:
[code] Pass{ ZWrite On ColorMask 0 }
第二个Pass采用透明度混合Pass即可。这样得到正确的结果:
但是这种处理方式同样有缺点,即它将无法实现下面的透明物体自身的效果。
4.透明物体自身的问题
如果我们仔细观察上述Shader效果,我们会发现虽然可以透过透明物体看到其他物体,但是却不能透过透明物体的一个面看到自身的另一个面,这是由于默认的剔除设置:Cull Back。这个设置会自动剔除背面的信息,从而导致上述问题。我们现在关闭剔除,即在Pass块中加上命令Cull Off。得到如下效果:
可以看出透明度测试得到了正确的结果,但透明度混合由于关闭了深度写入,不能确保图元从后往前渲染,得到了不能清楚辨识的视觉效果。所以对于透明度混合,我们采用两个Pass进行处理,第一个Pass只渲染背面Cull Front,第二个Pass只渲染正面Cull Back,这样确保了渲染顺序得到了可以清楚辨识正面背面的视觉效果:
- shader学习之基础纹理透明效果
- Unity学习笔记-shader基础学习(一)
- 基础差不得不学啊(unity shader中的矩阵学习一)
- UnityShader入门精要学习笔记(十一):透明效果-下部分
- UnityShader入门精要学习笔记(九):基础纹理之渐变纹理与遮罩纹理
- UnityShader入门精要学习笔记(十):透明效果-上部分
- 第4章 学习Shader所需的数学基础(终)(Unity Shader内置变量与练习题答案)
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
- Unity Shader学习笔记:基础数学
- Unity学习(三)Unity Shader入门(基础知识篇)+线性代数复习(未完待续)
- Unity Shader入门精要学习笔记 - 第4章 学习 Shader 所需的数学基础
- Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照
- Unity shader 官网文档全方位学习(一)————Surface Shaders基础及Examples详尽解析
- Unity Shader入门精要学习笔记 - 第8章 透明效果
- Shader学习的基础知识(六)透明效果
- Unity Shader学习5 —— Cg语言的基础
- UnityShader 学习笔记 (八) 数学基础 坐标系
- [unity 基础教程]给着色器shader添加透明属性
- Unity Shader入门精要学习笔记 - 第8章 透明效果
- shader学习基础之十三,根据透明阴影图扭曲图片(褶皱)