Unity3D运行时如何更改Cut-Out Shader的Alpha遮罩
解决Unity运行时Cut-Out Shader漫反射Alpha修改问题
核心思路
Unity默认Cut-Out Shader通过_MainTex的Alpha通道结合_Cutoff阈值实现像素裁剪。要实现衣物遮挡区域的皮肤隐藏,核心是在运行时为身体材质的漫反射纹理替换对应遮罩的Alpha通道,或使用自定义Shader单独传入Alpha遮罩。
方案一:修改原漫反射纹理的Alpha通道(适配内置Standard Cutout Shader)
步骤1:准备遮罩资源
- 为每件衣物制作对应身体部位的Alpha遮罩贴图:尺寸与身体原漫反射纹理一致,需要隐藏的区域Alpha设为0,显示区域设为1。
- 贴图导入设置:
Texture Type设为Default,Alpha Source选择From Texture(若遮罩是灰度图则选From Gray Scale),确保Alpha通道生效。
步骤2:运行时代码实现
using UnityEngine; public class BodyMaskController : MonoBehaviour { private SkinnedMeshRenderer _bodyRenderer; void Awake() { _bodyRenderer = GetComponent<SkinnedMeshRenderer>(); } // 切换衣物时调用此方法,传入对应遮罩贴图路径 public void ApplyBodyMask(string maskPath) { // 销毁旧材质实例,避免内存泄漏 if (_bodyRenderer.material != null) { Destroy(_bodyRenderer.material); } // 实例化材质副本(不修改共享材质,避免影响其他对象) Material bodyMat = new Material(_bodyRenderer.material); _bodyRenderer.material = bodyMat; // 加载遮罩贴图 Texture2D alphaMask = Resources.Load<Texture2D>(maskPath); if (alphaMask == null) { Debug.LogError("遮罩贴图加载失败:" + maskPath); return; } // 获取原漫反射纹理 Texture2D originalMainTex = bodyMat.GetTexture("_MainTex") as Texture2D; if (originalMainTex == null) { Debug.LogError("未找到漫反射纹理_MainTex"); return; } // 合并原纹理RGB与遮罩Alpha Texture2D newMainTex = new Texture2D(originalMainTex.width, originalMainTex.height); Color[] originalColors = originalMainTex.GetPixels(); Color[] maskColors = alphaMask.GetPixels(); for (int i = 0; i < originalColors.Length; i++) { // 若遮罩是灰度图,用R通道作为Alpha;若带Alpha则直接用maskColors[i].a originalColors[i].a = maskColors[i].r; } newMainTex.SetPixels(originalColors); newMainTex.Apply(); // 赋值给材质并设置裁剪阈值 bodyMat.SetTexture("_MainTex", newMainTex); bodyMat.SetFloat("_Cutoff", 0.5f); // 低于此值的Alpha像素会被裁剪 } }
方案二:自定义Cutout Shader(更高效,无需修改原纹理)
如果不想每次合并纹理,可自定义支持单独Alpha遮罩的Shader,直接传入遮罩贴图控制裁剪:
自定义Shader示例(内置渲染管线)
Shader "Custom/BodyCutoutWithMask" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _AlphaMask ("Alpha Mask", 2D) = "white" {} _Cutoff ("Alpha Cutoff", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 _Glossiness ("Smoothness", Range(0,1)) = 0.5 } SubShader { Tags { "RenderType"="TransparentCutout" "Queue"="AlphaTest" } LOD 200 CGPROGRAM #pragma surface surf Standard alphatest:_Cutoff sampler2D _MainTex; sampler2D _AlphaMask; half _Metallic; half _Glossiness; struct Input { float2 uv_MainTex; float2 uv_AlphaMask; }; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); fixed maskAlpha = tex2D(_AlphaMask, IN.uv_AlphaMask).r; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = maskAlpha; // 用遮罩控制Alpha裁剪 } ENDCG } FallBack "Standard" }
运行时代码简化版
public void ApplyBodyMask(string maskPath) { if (_bodyRenderer.material != null) { Destroy(_bodyRenderer.material); } Material bodyMat = new Material(_bodyRenderer.material); _bodyRenderer.material = bodyMat; Texture2D alphaMask = Resources.Load<Texture2D>(maskPath); if (alphaMask == null) { Debug.LogError("遮罩贴图加载失败:" + maskPath); return; } bodyMat.SetTexture("_AlphaMask", alphaMask); bodyMat.SetFloat("_Cutoff", 0.5f); }
注意事项
- 材质实例化:必须创建材质副本,直接修改共享材质会影响所有使用该材质的对象。
- 内存管理:切换遮罩时销毁旧材质实例,避免内存泄漏。
- UV匹配:遮罩贴图的UV需与原漫反射纹理完全一致,否则遮罩会错位。
- URP/HDRP适配:若使用SRP,只需替换对应的基础Shader模板,参数逻辑保持一致。
内容的提问来源于stack exchange,提问作者Owen Alexander
相关产品推荐
相关产品推荐

