[Unity Shader] (9) Cases and analysis of transparency testing and transparency mixing

Unity Shader Transparency Test and Transparency Mixing


For the theoretical part of transparency testing and transparency mixing, you can refer to the previous blog [Unity Shader] (8) The theoretical basis of transparency testing and transparency mixing . This section mainly organizes the code according to "Introduction to Unity Shader".



1. Transparency test

Concept: As long as the transparency of a fragment does not meet the conditions (usually less than the set threshold), its corresponding fragment will be discarded. (Abandoned means - no processing will be performed, including color buffering), if it is greater than the set threshold, the fragment will be processed in the same way as ordinary opacity objects.

Method: Usually in the fragment shader, use the clip function for transparency testing.

Function: clip(), equivalent to:

void clip(float4 v)
{
    
    
	if(any(x<0))
		discard;
}

code start

① After creating a new Shader, clear the code in it.
② Add our attribute code, namely Properties:

Properties{
    
    
	_Color ("Base Color",Color) = (1,1,1,1)
	_MainTex("Main Tex",2D) = "white"{
    
    }
	_Cutoff("Alpha Cutoff",Range(0,1)) = 0.5
}

Among them, Cutoff is the transparency threshold we set. The range is 0~1, because the transparency of the texel is in this range.

③ Write our SubShader semantic block

SubShader{
    
    
	Tags{
    
    "Queue"="AlphaTest" "IgnoreProjector"+"Ture" "RenderType"="TransparentCutout"}
	Pass{
    
    
		Tags{
    
    "LightMode"="ForwardBase"}


	}
}

The difference here is that we set three tags outside of Pass.

Label effect
Queue Render queue set to "transparency test"
RenderType Let Unity classify the Shader into the pre-defined TransparentCutout group (indicating that the Shader is a Shader that uses a transparency test)
IgnoreProjectors Set to True, which means that the Shader will not be affected by the projector (Projector)

Usually, the Shader that uses transparency to test AlphaTest needs to set the above three tags in SubShader.

④Supplement CGPROGRAM
⑤Complete code

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader"LeonShader/Shader_8_3_AlphaTest"
{
    
    
	Properties{
    
    
		_Color ("Base Color",Color) = (1,1,1,1)
		_MainTex("Main Tex",2D) = "white"{
    
    }
		_Cutoff("Alpha Cutoff",Range(0,1)) = 0.5
	}
	SubShader{
    
    
		Tags{
    
    "Queue" = "AlphaTest" "IgnoreProjector" = "Ture" "RenderType" = "TransparentCutout"}
		Pass{
    
    
			Tags{
    
    "LightMode" = "ForwardBase"}

			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);
				
				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);
				//等同于:
				//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"
}

Effect display:
1
It is worth mentioning that we can find that the black wireframe in the middle is opaque, which is supposed to be perfect black, but after the transparency test, some jagged effects will be found. This is because of the changing precision of textures at borders. So if you want to get a softer transparent effect, you can use the following transparency blending.



2. Transparency blending

In unity, you can use Blendthe open blend command. To achieve a translucent effect, it is necessary to mix the current own color with the color already in the color buffer. The following table shows the semantics of the Blend command:

Semantics describe
Blend Off close mix
Blend SrcFactor DstFactor Turn on blending, and set the blending factor. The source color (the color of the fragment itself) will be multiplied by SrcFator, and the target color (the color already in the color cache) will be multiplied by DstFator, and then the two are added and stored in the color buffer
Blend SrcFator DstFator,ScrFatorA DstFatorA Almost the same as above, just using a different factor to blend the alpha channel
BlendOp blendOperation Instead of simply adding the source color and the target color and mixing them, use blendOperation to perform other operations on them

Ps: When the mixing factor is set, the mixing mode will also be turned on. This is because the transparency channel only makes sense when blending is turned on. (In unity, using blend will automatically open.)

In summary, the following formula is obtained:
2



code part

The code for transparency blending is similar to transparency testing. In the process of writing, I found that there was a place that kept reporting errors, but I did not find the problem after repeated inspections. Copying the case code is indeed allowed, but this is not acceptable. I have to type it myself, and I can type it again and again. I am very confused @-@
directly on the code:

Note that this code is 关闭了深度写入.

Shader"LeonShader/Shader_8_4_AlphaBlend"
{
    
    
	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" }

			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"
}


3
In addition, since we turned off 深入写入(Zwrite Off), it will cause deep visual errors, as shown in the figure below:
4

So we need to improve the Shader code for transparency mixing.



3. Turn on the transparency blending of deep writing

The idea here is to open a new Pass channel to separately process the results caused by the depth buffer. But no color is output. Just store the depth value in the color buffer.
Of course, using one more Pass will have a certain impact on performance.

The code is based on the above "transparency mixing", add the following code:

Pass ( 
	ZWrite On 
	ColorMask 0
}

Pass {
    
    
	//与透明度混合的代码相同
}
FallBack "Diffuse"

In the above code, ColorMask can be used to set the write mask (Wirte Mask) of the color channel. The semantics are as follows:
ColorMask + RGB | A | 0 | 其他任何R、G、B、A的组合
After ColorMask is set to 0, it means that the Pass is not written into the color channel, which is exactly what we need, and the Pass value is written into the depth buffer.

The final result is as follows:
5



4. Double-sided transparent

In real life, if an object is transparent, it means that we can not only see the appearance of other objects through it, but
also see its internal structure. However, in the previous transparency effects, whether it is transparency testing or transparency mixing, we cannot observe the shape of the inside of the cube and its back, resulting in the object looking as if it is only half. This is because,
by default, the rendering engine culls the rendering primitives on the back of the object (relative to the direction of the camera), and only renders the
front of the object.
If we want to get a double-sided rendering effect, we can use the Cull command to control which side of the rendering primitive needs to be proposed. In Unity, the syntax of the Cull instruction is as follows:

Cull Back | Front | Off
instruction describe
Cull Back The rendering primitives facing the camera will not be rendered, which is also the culling state by default
Cull Front Then those rendering primitives facing the camera will not be rendered
Cull Off With culling turned off, all rendering primitives will be rendered. However, since the number of primitives that need to be rendered is multiplied at this time, unless there are special effects, such as the transparency effect of double-sided rendering here, the culling function is usually not turned off.

Instructions:

We add the following code to AlphaTest, which is the Shader code for the transparency test above:

Pass{
    
    
	Tags{
    
    "LightMode" = "ForwardBase"}
	Cull Off



The code addition method and effect are as follows:
insert image description here


5. Double-sided transparency with mixed transparency

Guess you like

Origin blog.csdn.net/weixin_46840974/article/details/124509394