1. 程式人生 > >【UnityShader】設定Image元件圖片透明四個方向透明漸變(Sprite原理相同)

【UnityShader】設定Image元件圖片透明四個方向透明漸變(Sprite原理相同)

由於對Shader比較感興趣,雖然這不是公司的需求,但還是自己利用工作時間之餘完成了這個效果,這個功能對於2D遊戲來說以後可能會有需求

先展示一下效果

先上Shader程式碼

Shader "Unlit/ImageAlpha"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_AlphaLX("RangeAlphaLX",Float) = 0
		_AlphaRX("RangeAlphaRX",Float) = 1
		_AlphaTY("RangeAlphaTY",Float) = 1
		_AlphaBY("RangeAlphaBY",Float) = 0
		_AlphaPower("Power",Float) = 0 //透明度變化範圍
	}
	SubShader
	{
		Tags { "RenderType"="Transparent" }
		Blend SrcAlpha OneMinusSrcAlpha
		Cull Back
		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

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

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			float _AlphaPower;
			sampler2D _AlphaTex;
			float _AlphaLX;
			float _AlphaRX;
			float _AlphaTY;
			float _AlphaBY;

			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}
			//此方法取自Unity預設Sprite的Shader
			fixed4 SampleSpriteTexture (float2 uv)
			{
				fixed4 color = tex2D (_MainTex, uv);

#if ETC1_EXTERNAL_ALPHA
				// get the color from an external texture (usecase: Alpha support for ETC1 on android)
				color.a = tex2D (_AlphaTex, uv).r;
#endif //ETC1_EXTERNAL_ALPHA

				return color;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				// sample the texture
				fixed4 col = SampleSpriteTexture(i.uv);
				//利用透明度閾值和uv座標的差來計算透明的程度和是否控制其半透
				//四個方向只是對座標的取值和正反方向不同,原理一致
				fixed alphalx = col.a * lerp(1,_AlphaPower,(_AlphaLX-i.uv.x));
				col.a = saturate(lerp(alphalx,col.a,step(_AlphaLX,i.uv.x)));

				fixed alpharx = col.a * lerp(1,_AlphaPower,(i.uv.x-_AlphaRX));
				col.a = saturate(lerp(col.a,alpharx,step(_AlphaRX,i.uv.x)));

				fixed alphaby = col.a * lerp(1,_AlphaPower,(_AlphaBY-i.uv.y));
				col.a = saturate(lerp(alphaby,col.a,step(_AlphaBY,i.uv.y)));

				fixed alphaty = col.a * lerp(1,_AlphaPower,(i.uv.y-_AlphaTY));
				col.a = saturate(lerp(col.a,alphaty,step(_AlphaTY,i.uv.y)));

				return col;
			}
			ENDCG
		}
	}
}
Shader 的原理就是用閾值和uv來計算透明程度,再使用lerp控制改變透明度的範圍,避免進行條件判斷。為了不用每個圖片建立一個材質放著,可以使用C#指令碼動態建立Material給Shader賦值,也更好用更人性化,更方便製作動畫。

首先上個基類程式碼,這個程式碼是我從《Unity入門精要》螢幕特效那裡改一點點拿來用的,因為真的非常好用,感謝馮樂樂女神!

using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
public class PostEffectsBase : MonoBehaviour {

	// Called when start
	protected void CheckResources() {
		bool isSupported = CheckSupport();
		
		if (isSupported == false) {
			NotSupported();
		}//
	}

	// Called in CheckResources to check support on this platform
	protected bool CheckSupport() {
		if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false) {
			Debug.LogWarning("This platform does not support image effects or render textures.");
			return false;
		}
		
		return true;
	}

	// Called when the platform doesn't support this effect
	protected void NotSupported() {
		enabled = false;
	}
	
	protected void Start() {
		CheckResources();
	}

	// Called when need to create the material used by this effect
	protected Material CheckShaderAndCreateMaterial(Shader shader, Material material) {
		if (shader == null) {
			return null;
		}
		
		if (shader.isSupported && material && material.shader == shader)
			return material;
		
		if (!shader.isSupported) {
			return null;
		}
		else {
			material = new Material(shader);
			material.hideFlags = HideFlags.DontSave;
			if (material)
				return material;
			else 
				return null;
		}
	}
}
接下來繼承這個基類寫上自己的變數,動態進行賦值,一個簡單的編輯器就完成了
using UnityEngine;
using System.Collections;
using UnityEngine.UI;

public class SetImageAlpha : PostEffectsBase
{
    [Range(0, 1)]
    public float leftX = 0;
    [Range(0, 1)]
    public float rightX = 0;
    [Range(0, 1)]
    public float topY = 0;
    [Range(0, 1)]
    public float bottomY = 0;
    [Range(-2, 0)]
    public float alphaSmooth = 0;
    // Use this for initialization
    public Shader alphaShader;
    private Material _materal;
    public Material _Material
    {
        get
        {
            _materal = CheckShaderAndCreateMaterial(alphaShader, _materal);
            return _materal;
        }
    }
    private void Awake()
    {
        alphaShader = Shader.Find("Unlit/ImageAlpha");
    }
    // Update is called once per frame
    void Update()
    {  
        _Material.SetFloat("_AlphaLX", leftX*2);
        _Material.SetFloat("_AlphaRX", ((1 - rightX) - 0.5f)*2);
        _Material.SetFloat("_AlphaTY", ((1 - topY) - 0.5f)*2);
        _Material.SetFloat("_AlphaBY", bottomY*2);
        _Material.SetFloat("_AlphaPower", alphaSmooth);
//變數的計算只是為了對映範圍
        GetComponent<Image>().material = _Material;
    }
}
這裡對變數進行的一些運算都是為了變數從把(0,1)的範圍對映到Shader的有效值範圍,只是一些簡單的數學運算,參考了一點半蘭伯特的演算法。大家想一下就可以理解

最後再把SetImageAlpha指令碼掛到帶有Image元件的遊戲物體上就可以了,大家也可以自己加一些漸變強度之類的引數獲得更靈活的效果。

還有一種就是圖片到螢幕某個區域的部分進行半透,這也很簡單,在頂點著色器中把頂點座標轉換到齊次座標,然後用座標和閾值做運算判斷即可。之前我也已經實現了這個效果,只是Shader檔案被我刪了。原理都大致相同,參照做出來即可。