读一读

混合操作指的是源颜色*因子后和目标颜色*因子后的值的操作方式,一般默认是+起来的,但是我们可以使用混合操作命令修改操作。

BlendOp BlendOperation


BlendOperation有:

dc4390371e9eb44deb8373f5e0960171.png

1d8a5601d9c26aefb5a2b93d35e0e5af.png


对于混合,就会有一个等式来计算。结果颜色就是O,S就是源颜色(片元的颜色),D为目标颜色(颜色缓冲读取出来的)。这时候就需要一些因子来计算出结果O了,RGB和A是使用不同的因子的,所以将会有4个因子的存在。

O(rgb) = 因子1 * S(rgb) + 因子2 * D(rgb)

O(a) = 因子3 * S(a) + 因子4 * D(a)


我们能控制的,就是对这些因子的配置来控制混合的计算。

181c1f62f0de6257e048f58fd2eb7011.png

第一种设置表示 因子3 = 因子1,因子4 = 因子2。第二种就是一一对应了。


因子的值可设置为:

0.png


Shader "Unlit/pass2"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Color("Color",Color) = (1,1,1,1)
		_AlphaScale("Alpha Scale",Range(0,5)) = 0.5
	}
	SubShader
	{
		Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }

		//第一个通道用来写入深度值,但是不写入颜色值
		Pass
		{
			ZWrite on
			ColorMask 0
		}

		Pass
		{
			Tags{ "LightMode" = "ForwardBase" }
			ZWrite Off  //关闭深度测试
			Blend SrcAlpha OneMinusSrcAlpha  //开启混合,设置混合因子

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 normal:NORMAL;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos : TEXCOORD2;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float3 _Color;
			float _AlphaScale;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				fixed3 wn = normalize(i.worldNormal);
				fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb;
				return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用
			}
			ENDCG
		}
	}
}


这里和混合实例是差不多的,就是多了一个Pass通道来写入深度值,达到的效果就是在一些互相交叉的物体上有了深度值,剔除一些元素,使自身透明不能看到自身。

看图:左边双通道,右边单纯的混合

blob.png


Shader "Unlit/blend"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_Color("Color",Color) = (1,1,1,1)
		_AlphaScale("Alpha Scale",Range(0,5)) = 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 "UnityCG.cginc"
			#include "Lighting.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float3 normal:NORMAL;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos:TEXCOORD2;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float3 _Color;
			float _AlphaScale;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				fixed3 wn = normalize(i.worldNormal);
				fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0.rgb * _Color.rgb * col.rgb;
				return fixed4(diffuse,col.a * _AlphaScale);//只有开启了Blend,返回透明度才有用
			}
			ENDCG
		}
	}
}


TIM图片20180927171622.png


Shader "Unlit/transparent"
{
    Properties
    {
        _Color("Color",Color)=(1,1,1,1)
        _MainTex ("Texture", 2D) = "white" {}
        _Cutoff("CutOff",Range(0,1))=0.5
    }
    SubShader
    {
        Tags{ "Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout" }

        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 worldNormal:TEXCOORD1;
                float3 worldPos:TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            float3 _Color;
            float _Cutoff;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                clip(col.a - _Cutoff);//透明度测试
                /*if ((col.a - _Cutoff) < 0.0) {
                     discard;//不通过直接舍弃
                }*/

                fixed3 wn = normalize(i.worldNormal);
                fixed3 wl = normalize(UnityWorldSpaceLightDir(i.worldPos));

                fixed3 diffuse = (dot(wn, wl) * 0.5 + 0.5) * _LightColor0 * _Color * col;

                return fixed4(diffuse,1);
            }
            ENDCG
        }
    }
}



123.gif


123.png


Tags{ "Queue"="AlphaTest" }


在Unity中有两种方式可以实现透明效果:一是使用透明度测试(Alpha Test)、一个是透明度混合(Alpha Blending)。要实现透明的效果就需要注意渲染的顺序了,在没有透明物体的时候,渲染顺序是不重要的,因为有一个深度测试,就是离摄像机的远近程度,深度值小的颜色值就会覆盖深度大的buff,反之直接舍弃了,所以无论怎么渲染都是对的。但是如果有透明物体后,透明物体就应该在不透明的物体后面渲染,在渲染透明物体时要关闭深度写入(更新深度值buff)。对于透明物体需要注意重叠的情况,尽量细分模型。

常用的办法是:

  1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。

  2. 把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的循序渲染这些半透明物体,并开启他们的深度测试,但关闭深度写入。


遮罩纹理是为了具体的控制某些光照的影响,也就是可以逐像素的控制表面的属性。比如说,一张纹理有4个通道RGBA,我们可以利用A通道存储高光反射的强度,G通道存储边缘光照的强度,B通道存储高光反射的指数,A通道存储自发光的强度。这样,使用遮罩纹理就能很好的控制每一个顶点受到的光照影响。


Shader "Unlit/jianbian"
{
	Properties
	{
		_Color ("Color",Color) = (1,1,1,1)
		_RampTex ("Texture", 2D) = "white" {}
	}
	SubShader
	{
		Tags { "LightMode"="ForwardBase" }

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc" 

			struct appdata
			{
				float4 vertex : POSITION;
				float3 normal:NORMAL;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 worldNormal:TEXCOORD1;
				float3 worldPos:TEXCOORD2;
			};

			fixed4 _Color;
			sampler2D _RampTex;
			float4 _RampTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed z = 0.5 * dot(worldNormal, worldLightDir) + 0.5;

				//利用光照影响因子构建uv来取样渐变纹理,从而影响漫反射
				fixed3 diffuse = tex2D(_RampTex, fixed2(z, z)).rgb * _Color.rgb * _LightColor0.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				return fixed4( diffuse.rgb + ambient.rgb,1);
			}
			ENDCG
		}
	}
}


TIM图片20180926175152.pngTIM图片20180926175221.pngTIM图片20180926175429.png


在法线贴图导入到Unity后,可以设置为Normal map类型,这一步操作Unity会根据平台来看看是否对法线贴图进行压缩,随后中需要使用UnpackNormal函数来映射为法线。

inline fixed3 UnpackNormalDXT5nm (fixed4 packednormal)
{    
    fixed3 normal;    
    normal.xy = packednormal.wy * 2 - 1;    
    normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy)));//根号(1-(x^2 + y^2))
    return normal;
}

inline fixed3 UnpackNormal(fixed4 packednormal)
{
    #if defined(UNITY_NO_DXT5nm)    
        return packednormal.xyz * 2 - 1;
    #else    
        return UnpackNormalDXT5nm(packednormal);
    #endif
}


法线可以压缩,只用两个通道就可以的原因在于:

一、法线是一个单位向量,也就是x^2+y^2+z^2=1

二、法线是一个只有正方向的向量