【游戏开发】摄像机遮挡半透

大多数游戏都加入了遮挡半透这个效果,即角色跟摄像机中间有物体遮挡时,会将这个物体半透,还会有渐隐和渐现的过程。

如下图:
gif

实现过程如下图:
代码逻辑
shader逻辑
把脚本挂到摄像机上,并把需要半透的物体设置可半透的材质和半透层即可
遮挡半透脚本

具体代码如下,shader是最普通的diffuse加了半透所需代码段:

using System.Collections.Generic;
using UnityEngine;

public class OcclusionTransparent : MonoBehaviour
{
    public GameObject targetObj;
    public LayerMask OHLayer;
    public float maxDistance = 10f;
    [Range(0.1f, 5f)]
    public float fadeTime = 2f;
    [Range(0f, 1f)]
    public float fadeDstAlpha = 0.3f;

    private Camera mainCamera;
    private Vector3 cameraPos;
    private Vector3 targetPos;

    private class TransObjAttr
    {
        public Material[] materials;
        public float curfadeTime = 0f;
        public bool transFlag = false;
    }
    Dictionary<Renderer, TransObjAttr> transObjDic = new Dictionary<Renderer, TransObjAttr>();
    List<Renderer> transObjClearList = new List<Renderer>();

    private void Start()
    {
        mainCamera = GetComponent<Camera>();
        if (mainCamera == null || targetObj == null)
        {
            enabled = false;
            return;
        }
        else
        {
            cameraPos = mainCamera.transform.position;
            targetPos = targetObj.transform.position;
        }
    }

    private void Update()
    {
        cameraPos = mainCamera.transform.position;
        targetPos = targetObj.transform.position;

        Debug.DrawLine(cameraPos, targetPos, Color.red);

        FadeOutTransObj();
        if (Physics.Raycast(targetPos, (cameraPos - targetPos).normalized, out RaycastHit hitInfo, maxDistance, OHLayer))
            UpdataTransObjDic(hitInfo);
        FadeInTransObj();
    }

    /// <summary>
    /// 把射线检测到的hitInfo转化为键值对加入字典里
    /// </summary>
    /// <param name="hitInfo">射线检测的hitInfo</param>
    private void UpdataTransObjDic(RaycastHit hitInfo)
    {
        Renderer[] renderers = hitInfo.collider.GetComponentsInChildren<Renderer>();
        foreach (var renderer in renderers)
        {
            transObjDic.TryGetValue(renderer, out TransObjAttr transObjAttr);

            if (transObjAttr == null)
            {
                if (renderer.sharedMaterials != null && renderer.sharedMaterials.Length > 0)
                {
                    string matName = renderer.sharedMaterials[0].name;
                    Material[] materials = matName.Contains("(Instance)") ? renderer.sharedMaterials : renderer.materials;
                    foreach (var material in materials)
                        SetMatTransparentMode(material);

                    transObjAttr = new TransObjAttr
                    {
                        materials = materials
                    };
                    transObjDic.Add(renderer, transObjAttr);
                }
            }

            transObjAttr.transFlag = true;
        }
    }

    /// <summary>
    /// 遍历字典做渐隐动画
    /// </summary>
    private void FadeOutTransObj()
    {
        Dictionary<Renderer, TransObjAttr>.Enumerator objEnumerator = transObjDic.GetEnumerator();
        while (objEnumerator.MoveNext())
        {
            KeyValuePair<Renderer, TransObjAttr> keyValuePair = objEnumerator.Current;
            if (keyValuePair.Key == null) continue;

            TransObjAttr transObjAttr = keyValuePair.Value;
            if (transObjAttr.transFlag)
            {
                if (transObjAttr.curfadeTime <= fadeTime)
                {
                    transObjAttr.curfadeTime += Time.deltaTime;

                    if (Mathf.Abs(transObjAttr.curfadeTime - fadeTime) > Time.deltaTime)
                    {
                        float t = transObjAttr.curfadeTime / fadeTime;
                        float alpha = Mathf.Lerp(1, fadeDstAlpha, t);

                        Material[] materials = transObjAttr.materials;
                        foreach (var material in materials)
                        {
                            if (material.HasProperty("_Alpha"))
                                material.SetFloat("_Alpha", alpha);
                        }
                    }
                }
                transObjAttr.transFlag = false;
            }
        }
    }

    /// <summary>
    /// 遍历字典做渐现动画
    /// </summary>
    private void FadeInTransObj()
    {
        transObjClearList.Clear();

        Dictionary<Renderer, TransObjAttr>.Enumerator objEnumerator = transObjDic.GetEnumerator();
        while (objEnumerator.MoveNext())
        {
            KeyValuePair<Renderer, TransObjAttr> keyValuePair = objEnumerator.Current;
            if (keyValuePair.Key == null) continue;

            TransObjAttr transObjAttr = keyValuePair.Value;
            if (!transObjAttr.transFlag)
            {
                Material[] materials = transObjAttr.materials;
                foreach (var material in materials)
                {
                    float alpha = 0;
                    if (material.HasProperty("_Alpha"))
                        alpha = material.GetFloat("_Alpha");
                    else
                        continue;

                    if (Mathf.Abs(alpha - 1) < 0.01)
                    {
                        SetMatOpaqueMode(material);
                        transObjClearList.Add(keyValuePair.Key);
                    }
                    else
                    {
                        if (transObjAttr.curfadeTime >= 0)
                            transObjAttr.curfadeTime -= Time.deltaTime;

                        float t = transObjAttr.curfadeTime / fadeTime;
                        alpha = Mathf.Lerp(1, fadeDstAlpha, t);
                        material.SetFloat("_Alpha", alpha);
                    }
                }
            }
        }

        for (int i = 0; i < transObjClearList.Count; i++)
            transObjDic.Remove(transObjClearList[i]);
    }

    private void SetMatTransparentMode(Material material)
    {
        if (material.HasProperty("_SrcBlend"))
            material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
        if (material.HasProperty("_DstBlend"))
            material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
        material.renderQueue = 3000;
    }

    private void SetMatOpaqueMode(Material material)
    {
        if (material.HasProperty("_SrcBlend"))
            material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
        if (material.HasProperty("_DstBlend"))
            material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
        material.renderQueue = 2000;
    }
}

Shader "Scene/Diffuse"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
		_Color ("Base Color", Color) = (1,1,1,1)

		[HideInInspector] _Alpha ("", float) = 1
		[HideInInspector] _SrcBlend ("", int) = 1
		[HideInInspector] _DstBlend ("", int) = 0
    }
    SubShader
    {
        Tags { "Queue"="Geometry" "RenderType"="Opaque" }

        LOD 200

        Pass
        {
			Blend [_SrcBlend][_DstBlend]

            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
            {
                float4 vertex		: SV_POSITION;
				float2 uv			: TEXCOORD0;
				float3 worldPos		: TEXCOORD1;
				float3 worldNormal	: TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
			half4 _Color;		
			half _Alpha;	

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex.xyz);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);			
				
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
				half4 texColor = tex2D(_MainTex, i.uv);

				half3 normal = normalize(i.worldNormal);
				half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				half diff = dot(normal, lightDir) * 0.5 + 0.5;
				
				half3 diffColor = _LightColor0.rgb * _Color.rgb * texColor.rgb * diff;				

                return half4(diffColor, texColor.a * _Alpha);
            }
            ENDCG
        }
    }
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值