欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【UnityShader】设置Image组件图片透明四个方向透明渐变(Sprite原理相同)

程序员文章站 2022-04-03 22:19:00
...

由于对Shader比较感兴趣,虽然这不是公司的需求,但还是自己利用工作时间之余完成了这个效果,这个功能对于2D游戏来说以后可能会有需求

先展示一下效果

【UnityShader】设置Image组件图片透明四个方向透明渐变(Sprite原理相同)

先上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文件被我删了。原理都大致相同,参照做出来即可。