You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 2D中实现多重叠Image合并后统一设置透明度的方法

解决Unity 2D多Image重叠时透明度叠加问题

问题描述

多个挂载Image组件的GameObject在场景中重叠时,若单独给每个Image设置Color(1.0, 1.0, 1.0, 0.5)(透明度0.5),重叠区域会出现颜色叠加的异常效果(比如蓝红重叠处呈现紫色)。需要实现先将所有重叠Image合并为整体,再统一设置0.5透明度的效果,且支持任意数量的Image。

实现方案

方案一:RenderTexture批量渲染合并(通用动态场景)

通过RenderTexture将所有目标Image渲染为一张纹理,再用单个Image承载这张纹理并设置透明度,从根本上避免叠加问题。

步骤说明

  • 计算所有目标Image的整体包围盒:遍历每个Image的RectTransform,获取其世界空间的四角坐标,计算出覆盖所有Image的最小矩形区域。
  • 创建匹配包围盒尺寸的RenderTexture,将UI相机的渲染目标临时切换为该RenderTexture。
  • 临时隐藏目标Image,让相机将这些Image渲染到RenderTexture中。
  • 从RenderTexture读取像素生成Texture2D,再创建Sprite赋值给新的Image组件。
  • 给新Image设置0.5透明度,按需销毁或隐藏原Image。

代码实现

using UnityEngine;
using UnityEngine.UI;

public class ImageMerger : MonoBehaviour
{
    public Image[] targetImages; // 需合并的所有Image
    public Image mergedImagePrefab; // 合并后Image的预制体

    public void MergeImages()
    {
        if (targetImages == null || targetImages.Length == 0) return;

        // 计算所有Image的总包围盒
        Vector3[] corners = new Vector3[4];
        targetImages[0].rectTransform.GetWorldCorners(corners);
        Vector2 min = corners[0];
        Vector2 max = corners[2];

        foreach (var img in targetImages)
        {
            img.rectTransform.GetWorldCorners(corners);
            min = Vector2.Min(min, corners[0]);
            max = Vector2.Max(max, corners[2]);
        }
        Rect totalRect = new Rect(min, max - min);

        // 创建RenderTexture
        RenderTexture rt = new RenderTexture((int)totalRect.width, (int)totalRect.height, 0);
        RenderTexture.active = rt;

        // 获取UI相机
        Camera uiCamera = FindObjectOfType<Canvas>().worldCamera ?? Camera.main;

        // 临时隐藏目标Image
        foreach (var img in targetImages) img.gameObject.SetActive(false);

        // 渲染到RenderTexture
        uiCamera.targetTexture = rt;
        uiCamera.Render();

        // 生成最终纹理
        Texture2D mergedTex = new Texture2D(rt.width, rt.height);
        mergedTex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        mergedTex.Apply();

        // 恢复相机与原Image状态
        uiCamera.targetTexture = null;
        RenderTexture.active = null;
        foreach (var img in targetImages) img.gameObject.SetActive(true);

        // 创建合并后的Image
        GameObject mergedObj = Instantiate(mergedImagePrefab.gameObject, transform);
        Image mergedImg = mergedObj.GetComponent<Image>();
        mergedImg.sprite = Sprite.Create(mergedTex, new Rect(0, 0, mergedTex.width, mergedTex.height), new Vector2(0.5f, 0.5f));
        mergedImg.rectTransform.anchoredPosition = totalRect.position;
        mergedImg.rectTransform.sizeDelta = totalRect.size;
        mergedImg.color = new Color(1f, 1f, 1f, 0.5f);

        // 销毁原Image(可根据需求改为隐藏)
        foreach (var img in targetImages) Destroy(img.gameObject);

        // 清理临时资源
        Destroy(rt);
    }
}

方案二:Mask+自定义Shader(适合静态场景)

通过Mask将所有子Image约束在同一区域,再用自定义Shader改变混合模式,实现先绘制所有不透明内容、再统一叠加透明度的效果。

步骤说明

  • 创建父GameObject,将所有需合并的Image设为其子物体。
  • 给父物体添加Mask组件,用一个覆盖所有子Image区域的Image作为遮罩。
  • 编写自定义Shader,修改混合模式为Blend One OneMinusSrcAlpha(先叠加所有RGB颜色,再统一应用透明度)。
  • 将所有子Image的材质替换为该自定义材质,最后给遮罩Image设置0.5透明度。

简化Shader示例

Shader "UI/CustomOpaqueBlend"
{
    Properties
    {
        [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)
    }

    SubShader
    {
        Tags
        { 
            "Queue"="Transparent" 
            "IgnoreProjector"="True" 
            "RenderType"="Transparent" 
            "PreviewType"="Plane"
            "CanUseSpriteAtlas"="True"
        }

        Cull Off
        Lighting Off
        ZWrite Off
        Blend One OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "UnityUI.cginc"

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
            };

            fixed4 _Color;
            fixed4 _TextureSampleAdd;
            float4 _ClipRect;

            v2f vert(appdata_t IN)
            {
                v2f OUT;
                OUT.worldPosition = IN.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = IN.texcoord;

                #ifdef UNITY_HALF_TEXEL_OFFSET
                OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
                #endif

                OUT.color = IN.color * _Color;
                return OUT;
            }

            sampler2D _MainTex;

            fixed4 frag(v2f IN) : SV_Target
            {
                half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
                color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                return color;
            }
            ENDCG
        }
    }
}

内容的提问来源于stack exchange,提问作者Robin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 22:16:18