大多数游戏都加入了遮挡半透这个效果,即角色跟摄像机中间有物体遮挡时,会将这个物体半透,还会有渐隐和渐现的过程。
实现过程如下图:
把脚本挂到摄像机上,并把需要半透的物体设置可半透的材质和半透层即可
具体代码如下,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();
}
///
/// 把射线检测到的hitInfo转化为键值对加入字典里
///
/// 射线检测的hitInfo
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;
}
}
///
/// 遍历字典做渐隐动画
///
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;
}
}
}
///
/// 遍历字典做渐现动画
///
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
}
}
}