渲染顺序 先画不透明物体,再画透明物体,渲染顺序非常重要,不能随意改动,如果没有一个渲染顺序则会导致部分物体无法被画到屏幕上。先渲染不透明物体,再渲染透明物体,之后再对不透明物体从后往前排序渲染
透明度测试: 概念 只要一个片元的透明度不满足条件(通常小于某个阈值),那么就舍弃对应的片元。被舍弃的片元不会在进行任何的处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体来处理,即进行深度测试,深度写入等等。虽然简单,但是很极端,要么完全透明,要么完全不透明。
例如下图
假如说我们代码中缩写为当α小于0.5是便会舍去上半部分,指挥渲染下班α大于0.5的部分,但之后不会再去干其α的值为多少,统一将其按照不透明物体渲染。最后渲染出来便为一座山。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 Shader "Unlit/014" { Properties { _MainTex("MainTex" , 2 D) = "white" {} _Diffuse("Diffuse" , Color) = (1 ,1 ,1 ,1 ) _Cutoff("Alpha Cutoff" , Range(0 ,1 )) = 0.5 } SubShader { Tags { "Queue" ="AlphaTest" "IgnoreProjector" ="True" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _Diffuse; sampler _MainTex; float4 _MainTex_ST; float _Cutoff; struct v2f { float4 vertex : SV_POSITION; fixed3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); o.worldNormal = worldNormal; o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed4 testColor = tex2D(_MainTex, i.uv); if ((testColor.a - _Cutoff) < 0 ) { discard; } fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * testColor.rgb * _Diffuse.rgb * (max(0 , dot(worldLightDir, normalize(i.worldNormal)))* 0.5 + 0.5 ); fixed3 color = ambient + diffuse; return float4(color, 1 ); } ENDCG } } }
其中最重要的时要把渲染顺序改成AlphaTest,虽然此处不影响,但是以后写代码时需要改。
透明度混合: 概念 可以得到真正的半透明效果,它会使当前片元的透明度作为混合因子,与已经储存在颜色缓冲中的颜色值进行混合么,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。注意:透明度混合只关闭了深度写入,但没有关闭深度测试。这表示当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果深度值距离摄像机更远,那么就不会在进行混合操作。比如一个不透明物体在透明物体前面,我们先渲染不透明物体,可以正常的挡住不透明物体。
为什么要关闭深度写入?如下图:
不透明物体小物块已经画好了,在话不透明物体时,深度测试正常进行,其透明度测试小于不透明度深度,会关掉深度写入,此时好处便为,若想要在两个物理之间再画一个透明物体,此时该物体的深度要大于蓝色的深度,若刚才的深度写入开启列,那么这个物体便不会画在屏幕上。若关闭之后便可正常画出。
两个不透明物体的渲染
若此时先渲染A再徐然B,则会导致B再A的前面,那么就会导致错误,所以再渲染时会先渲染不透明物体,再渲染透明物体,之后再对不透明物体从后往前排序渲染。
混合 只要有关键字Blend,就代表开启混合(除Blend off以外)
计算公式
最后混合只要使用Blend SrcFator DstFactor就行了。
混合因子
混合操作
常见的混合操作类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Blend SrcAlpha OneMinusSrcAlpha Blend OneMinusDstColor One Blend DstColor Zero Blend DstColor SrcColor BlendOp min Blend One One Blend OneMinusDstColor One Blend One OneMinusSrcColor Blend One One
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 Shader "Unlit/015" { Properties { _MainTex("MainTex" , 2 D) = "white" {} _Diffuse("Diffuse" , Color) = (1 ,1 ,1 ,1 ) _AlphaScale("Alpha Scale" , Range(0 ,1 )) = 1 } SubShader { Tags { "Queue" ="transparent" "IgnoreProjector" ="True" "RenderType" ="Transparent" } LOD 100 ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Pass { tags{"LightMode" ="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" fixed4 _Diffuse; sampler _MainTex; float4 _MainTex_ST; float _AlphaScale; struct v2f { float4 vertex : SV_POSITION; fixed3 worldNormal : TEXCOORD0; float3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; v2f vert (appdata_base v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); fixed3 worldNormal = UnityObjectToWorldNormal(v.normal); o.worldNormal = worldNormal; o.worldPos = mul(unity_ObjectToWorld, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed4 testColor = tex2D(_MainTex, i.uv); fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos); fixed3 diffuse = _LightColor0.rgb * testColor.rgb * _Diffuse.rgb * (dot(worldLightDir, normalize(i.worldNormal))* 0.5 + 0.5 ); fixed3 color = ambient + diffuse; return float4(color, testColor.a * _AlphaScale); } ENDCG } } Fallback "Transparent/Vertexlit" }
特殊问题处理 当以上代码用于下图所示图形时,便会出现如下图所示的渲染错误的情况出现。
所以此时需要另写一个通道,去做深度写入。
1 2 3 4 5 6 7 8 9 Pass { ZWrite On ColorMask 0 } ZWrite Off Blend SrcAlpha OneMinusSrcAlpha
做完以上改动后就可以正常渲染了