前言 在许多游戏中都会遇到人物被场景或者其他物品遮挡的问题,遮挡半透是游戏中非常常用的处理遮挡的手段。 这里介绍一种使用摄像机脚本和URP Shader实现遮挡半透明效果的方法。 效果预览 实现步骤 1. 创建Shader 在Unity中创建一个Shader,并将其命名为“TransparentColorURP”,然后在Shader中添加以下代码: Shader "Custom/TransparentColorURP" { // 属性块定义了可以在材质编辑器中调整的参数 Properties { _Color("Color Tint", Color) = (1,1,1,1) _MainTex("Main Tex", 2D) = "white" {} _AlphaScale("Alpha Scale", Range(0, 1)) = 0.5 // 透明度缩放,控制材质的透明度程度 } SubShader { Tags { "RenderPipeline"="UniversalPipeline" "Queue"="Transparent" "IngoreProjector"="True" "RenderType"="Transparent" } Pass { ZWrite On // 开启深度写入 ColorMask 0 // 关闭颜色通道写入 } Pass { Tags {"LightMode"="UniversalForward"} ZWrite Off Blend SrcAlpha OneMinusSrcAlpha // 设置混合模式,基于源Alpha值的透明度混合 HLSLPROGRAM #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" #pragma vertex vert #pragma fragment frag CBUFFER_START(UnityPerMaterial) half4 _Color; float4 _MainTex_ST; float _AlphaScale; CBUFFER_END TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex); struct a2v { float4 vertex : POSITION; half3 normal : NORMAL; float4 texcoord : TEXCOORD0; }; struct v2f { float4 pos : SV_POSITION; half3 worldNormal : TEXCOORD0; half3 worldPos : TEXCOORD1; float2 uv : TEXCOORD2; }; v2f vert(a2v i) { v2f o; o.pos = TransformObjectToHClip(i.vertex.xyz); o.worldPos = mul(UNITY_MATRIX_M, i.vertex).xyz; o.worldNormal = TransformObjectToWorldNormal(i.normal); o.uv = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; return o; } half4 frag(v2f i) : SV_Target { half3 worldNormal = normalize(i.worldNormal); half3 worldLightDir = normalize(_MainLightPosition.xyz); half4 texColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv); half3 albedo = texColor.rgb * _Color.rgb; half3 ambient = half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w) * albedo; half3 diffuse = _MainLightColor.rgb * albedo * saturate(dot(worldLightDir, worldNormal)); return half4(ambient + diffuse, texColor.a * _AlphaScale); } ENDHLSL } } FallBack "Univeral Render Pipeline/Simple Lit" } 这个 shader 通过透明度叠加的方式实现了半透明效果,并且透明度可调。 2. 创建摄像机脚本 在Unity中创建一个脚本,并将其命名为“TransparentControl”,然后在脚本中添加以下代码: using System.Collections; using System.Collections.Generic; using UnityEngine; public class TransparentControl : MonoBehaviour { public class TransparentParam { public Material[] materials; public Material[] sharedMats; public float currentFadeTime = 0; public bool isTransparent = true; } public Transform targetObject; //目标对象 public float height = 3.0f; //目标对象Y方向偏移 public float destTransparent = 0.2f; //遮挡半透的最终半透强度, public float fadeInTime = 1.0f; //开始遮挡半透时渐变时间 public LayerMask occlusionLayers; // 定义哪些层会被视为遮挡层 private Dictionary transparentDic = new(); private readonly List clearList = new(); private Vector3 lastTargetPos; void Update() { if (targetObject == null) return; // 仅当目标位置改变或超过更新间隔时更新射线投射 if (targetObject.position != lastTargetPos) { lastTargetPos = targetObject.position; UpdateRayCastHit(); } UpdateTransparentObject(); } void UpdateTransparentObject() { foreach (var kvp in transparentDic) { TransparentParam param = kvp.Value; param.isTransparent =false; // 实现渐入半透明效果,避免太过突兀 foreach (var mat in param.materials) { float destAlphaScale = destTransparent; float currentAlphaScale = mat.GetFloat("_AlphaScale"); float newAlphaScale = Mathf.MoveTowards(currentAlphaScale, destAlphaScale, Time.deltaTime / fadeInTime); mat.SetFloat("_AlphaScale", newAlphaScale); } } } void RemoveUnuseTransparent() { clearList.Clear(); foreach (var kvp in transparentDic) { if (!kvp.Value.isTransparent) { kvp.Key.materials = kvp.Value.sharedMats; clearList.Add(kvp.Key); } } foreach (var renderer in clearList) transparentDic.Remove(renderer); } void UpdateRayCastHit() { RaycastHit[] rayHits; //视线方向为从自身(相机)指向目标位置 Vector3 targetPos = targetObject.position + new Vector3(0, height, 0); Vector3 viewDir = (targetPos - transform.position).normalized; RaycastHit HitInfo; Physics.Raycast(transform.position, viewDir, out HitInfo); // 假如角色没有被遮挡,则删除半透明效果 if (HitInfo.transform.tag == "Player") { RemoveUnuseTransparent(); } // 角色被遮挡时,获取所有遮挡物,并设置为透明 else { Vector3 oriPos = transform.position; float distance = Vector3.Distance(oriPos, targetPos); Ray ray = new Ray(oriPos, viewDir); rayHits = Physics.RaycastAll(ray, distance, occlusionLayers); // 直接在Scene画一条线,方便观察射线 Debug.DrawLine(oriPos, targetPos, Color.red); foreach (var hit in rayHits) { Renderer[] renderers = hit.collider.GetComponentsInChildren(); foreach (Renderer r in renderers) { AddTransparent(r); } } } } void AddTransparent(Renderer renderer) { TransparentParam param; if (!transparentDic.TryGetValue(renderer, out param)) { param = new TransparentParam(); transparentDic.Add(renderer, param); // 此处顺序不能反,调用material会产生材质实例。 param.sharedMats = renderer.sharedMaterials; param.materials = renderer.materials; foreach (var v in param.materials) { v.shader = Shader.Find("Custom/TransparentColorURP"); } } param.isTransparent = true; } } 这个脚本从摄像机向目标对象发射一条射线,并检测是否与遮挡物发生碰撞,如果碰撞则将该物体设置为透明。 如果射线直达目标位置,则说明角色没有被遮挡,删除半透明效果。 3. 绑定摄像机脚本 将摄像机脚本绑定到摄像机上,并将目标对象设置为脚本的属性。 并按需设置高度和遮挡层。 原理解析 Shader 原理 Shader 的主要功能是将材质的颜色、透明度、光照等参数映射到物体的表面上,使得物体看起来更逼真、更有质感。 在这个 shader 中,我们使用了混合模式 Blend SrcAlpha OneMinusSrcAlpha,即基于源Alpha值的透明度混合。 在计算机图形学中,混合模式(Blend Mode)是指在渲染过程中如何将源颜色(Source Color)和目标颜色(Destination Color)结合起来。SrcAlpha和OneMinusSrcAlpha是混合方程中使用的两个因子,它们分别代表了源颜色的透明度(Alpha)和源颜色不透明度。 在Unity等图形引擎中,混合模式通常用于控制透明度渲染。当使用SrcAlpha和OneMinusSrcAlpha作为混合因子时,混合方程如下: 最终颜色 = 源颜色 * SrcAlpha + 目标颜色 * OneMinusSrcAlpha 这里的SrcAlpha是源颜色的Alpha值,而OneMinusSrcAlpha是1减去源颜色的Alpha值。Alpha值是一个介于0和1之间的数,其中0表示完全透明,1表示完全不透明。 具体来说: SrcAlpha:使用源颜色的Alpha值作为混合因子,这意味着源颜色的不透明度越高,其对最终颜色的影响就越大。 OneMinusSrcAlpha:使用1减去源颜色的Alpha值作为混合因子,这意味着目标颜色的影响随着源颜色的不透明度增加而减少。 这种混合模式通常用于实现半透明效果,如玻璃、水、烟雾等。在这种情况下,源颜色(例如烟雾的颜色)会根据其Alpha值与背景(目标颜色)混合,从而产生正确的半透明视觉效果。 脚本原理 这个Unity脚本TransparentControl用于控制3D场景中物体的透明度,以实现遮挡时物体逐渐变为半透明效果。脚本通过射线投射来判断目标对象是否被其他物体遮挡,并据此调整透明度。以下是该脚本的原理解析: 定义与变量: TransparentParam类:一个自定义类,用于存储与透明度相关的参数,包括材质数组、当前淡入时间和透明状态。 targetObject:需要关注的对象,通常是一个角色或重要的场景元素。 height:目标对象在Y轴的偏移量,用于调整射线投射的起点。 destTransparent:目标对象被遮挡时应达到的透明度。 fadeInTime:目标对象变为半透明状态的时间。 occlusionLayers:定义哪些层级的物体可以被视为遮挡物。 transparentDic:一个字典,用于存储每个渲染器的透明度参数。 clearList:一个列表,用于存储不再透明的渲染器,以便从字典中移除。 Update方法: 检查targetObject是否为空,若为空则不进行任何操作。 如果目标对象的位置发生了变化,则更新射线投射。 UpdateTransparentObject方法: 遍历transparentDic中的每个渲染器,将其材质的透明度参数逐渐调整至目标值,实现淡入效果。 RemoveUnuseTransparent方法: 清除clearList。 遍历transparentDic,如果某个渲染器不再透明,则将其从字典中移除,并恢复其原始材质。 UpdateRayCastHit方法: 从摄像机位置向目标对象发射射线。 如果射线击中的是标签为“Player”的对象,说明目标没有被遮挡,调用RemoveUnuseTransparent移除半透明效果。 如果目标被遮挡,获取所有遮挡物,并调用AddTransparent使它们变为半透明。 AddTransparent方法: 如果渲染器不在transparentDic中,将其添加到字典中,并设置其材质为指定的透明度着色器。 标记该渲染器为透明状态。 整体而言,这个脚本通过射线检测来识别目标对象是否被遮挡,并动态调整遮挡物的透明度,从而创造出一种视觉上的半透明效果。这在游戏和3D可视化中是一种常用的技术,可以增强场景的深度感和真实感。
前言
在许多游戏中都会遇到人物被场景或者其他物品遮挡的问题,遮挡半透是游戏中非常常用的处理遮挡的手段。 这里介绍一种使用摄像机脚本和URP Shader实现遮挡半透明效果的方法。
效果预览
实现步骤
1. 创建Shader
在Unity中创建一个Shader,并将其命名为“TransparentColorURP”,然后在Shader中添加以下代码:
这个 shader 通过透明度叠加的方式实现了半透明效果,并且透明度可调。
2. 创建摄像机脚本
在Unity中创建一个脚本,并将其命名为“TransparentControl”,然后在脚本中添加以下代码:
这个脚本从摄像机向目标对象发射一条射线,并检测是否与遮挡物发生碰撞,如果碰撞则将该物体设置为透明。 如果射线直达目标位置,则说明角色没有被遮挡,删除半透明效果。
3. 绑定摄像机脚本
将摄像机脚本绑定到摄像机上,并将目标对象设置为脚本的属性。 并按需设置高度和遮挡层。
原理解析
Shader 原理
Shader 的主要功能是将材质的颜色、透明度、光照等参数映射到物体的表面上,使得物体看起来更逼真、更有质感。
在这个 shader 中,我们使用了混合模式 Blend SrcAlpha OneMinusSrcAlpha,即基于源Alpha值的透明度混合。
在计算机图形学中,混合模式(Blend Mode)是指在渲染过程中如何将源颜色(Source Color)和目标颜色(Destination Color)结合起来。
SrcAlpha
和OneMinusSrcAlpha
是混合方程中使用的两个因子,它们分别代表了源颜色的透明度(Alpha)和源颜色不透明度。 在Unity等图形引擎中,混合模式通常用于控制透明度渲染。当使用SrcAlpha
和OneMinusSrcAlpha
作为混合因子时,混合方程如下:这里的
SrcAlpha
是源颜色的Alpha值,而OneMinusSrcAlpha
是1减去源颜色的Alpha值。Alpha值是一个介于0和1之间的数,其中0表示完全透明,1表示完全不透明。 具体来说:SrcAlpha
:使用源颜色的Alpha值作为混合因子,这意味着源颜色的不透明度越高,其对最终颜色的影响就越大。OneMinusSrcAlpha
:使用1减去源颜色的Alpha值作为混合因子,这意味着目标颜色的影响随着源颜色的不透明度增加而减少。 这种混合模式通常用于实现半透明效果,如玻璃、水、烟雾等。在这种情况下,源颜色(例如烟雾的颜色)会根据其Alpha值与背景(目标颜色)混合,从而产生正确的半透明视觉效果。脚本原理
这个Unity脚本
TransparentControl
用于控制3D场景中物体的透明度,以实现遮挡时物体逐渐变为半透明效果。脚本通过射线投射来判断目标对象是否被其他物体遮挡,并据此调整透明度。以下是该脚本的原理解析:TransparentParam
类:一个自定义类,用于存储与透明度相关的参数,包括材质数组、当前淡入时间和透明状态。targetObject
:需要关注的对象,通常是一个角色或重要的场景元素。height
:目标对象在Y轴的偏移量,用于调整射线投射的起点。destTransparent
:目标对象被遮挡时应达到的透明度。fadeInTime
:目标对象变为半透明状态的时间。occlusionLayers
:定义哪些层级的物体可以被视为遮挡物。transparentDic
:一个字典,用于存储每个渲染器的透明度参数。clearList
:一个列表,用于存储不再透明的渲染器,以便从字典中移除。targetObject
是否为空,若为空则不进行任何操作。transparentDic
中的每个渲染器,将其材质的透明度参数逐渐调整至目标值,实现淡入效果。clearList
。transparentDic
,如果某个渲染器不再透明,则将其从字典中移除,并恢复其原始材质。RemoveUnuseTransparent
移除半透明效果。AddTransparent
使它们变为半透明。transparentDic
中,将其添加到字典中,并设置其材质为指定的透明度着色器。