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

Unity Shader能否将8个偏移绘制Pass合并为单通道实现2D角色描边?

实现Spine 2D角色描边:合并8个偏移Pass为单个Pass

问题描述

我是Shader新手,正在为tk2d Spine 2D角色制作描边效果。我知道有其他描边方法,但对当前思路感兴趣(请勿推荐其他描边方式)。
当前思路是通过获取对象顶点,设置8个方向(上下左右及4个对角线)的偏移,绘制同纹理来实现描边。效果已实现,但Shader代码需9个Pass:8个仅偏移方向不同的重复Pass,最后1个绘制角色本体。重复代码冗余且可能影响性能,因此想询问:能否将这8个Pass压缩为1个?即保存各方向顶点位置信息传递给片元着色器。
我曾尝试放大顶点,但因角色边缘不光滑导致描边失真。


解决方案

可以通过在片元着色器中采样8个偏移方向的纹理像素来合并这8个Pass,不需要重复绘制8次顶点。核心逻辑是:在顶点着色器中计算纹理空间的8个方向偏移量,传递给片元着色器后,检查当前像素周围8个方向是否存在不透明的本体像素,若存在则输出描边颜色,最后叠加原角色纹理。

以下是修改后的完整Shader代码:

Shader "Custom/Shader_Spine_Battle_Red" 
{
    Properties 
    {
        _Color ("描边颜色", color) = (1, 0, 0, 1)
        _MainTex ("基础纹理 (RGB) 透明通道 (A)", 2D) = "white" {}
        _OutlineWidth ("描边宽度", float) = 1
    }
    
    SubShader
    {
        Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
        ZWrite Off Lighting Off Cull Off Fog { Mode Off } Blend SrcAlpha OneMinusSrcAlpha
        LOD 110
        
        Stencil{
            Comp Equal
        }

        // 合并描边与本体的单个Pass
        Pass 
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma fragmentoption ARB_precision_hint_fastest
            #include "UnityCG.cginc"

            sampler2D _MainTex;
            fixed4 _Color;
            float _OutlineWidth;
            // 纹理像素大小,由Unity自动传入
            float4 _MainTex_TexelSize;

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

            struct v2f_vct
            {
                float4 vertex : SV_POSITION;
                fixed4 color : COLOR;
                float2 texcoord : TEXCOORD0;
                // 传递8个方向的纹理偏移坐标
                float2 offset[8] : TEXCOORD1;
            };

            v2f_vct vert(vin_vct v)
            {
                v2f_vct o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.color = v.color;
                o.texcoord = v.texcoord;

                // 计算纹理空间的8个方向偏移量
                float2 baseOffset = _OutlineWidth * _MainTex_TexelSize.xy;
                o.offset[0] = float2(baseOffset.x, baseOffset.y);     // 右上
                o.offset[1] = float2(baseOffset.x, -baseOffset.y);    // 右下
                o.offset[2] = float2(-baseOffset.x, baseOffset.y);    // 左上
                o.offset[3] = float2(-baseOffset.x, -baseOffset.y);   // 左下
                o.offset[4] = float2(baseOffset.x, 0);                // 右
                o.offset[5] = float2(-baseOffset.x, 0);               // 左
                o.offset[6] = float2(0, baseOffset.y);                // 上
                o.offset[7] = float2(0, -baseOffset.y);               // 下

                return o;
            }

            fixed4 frag(v2f_vct i) : SV_Target
            {
                // 采样原纹理像素
                fixed4 originalCol = tex2D(_MainTex, i.texcoord);
                // 如果原像素不透明,直接返回本体颜色
                if (originalCol.a > 0.01) {
                    return originalCol * i.color;
                }

                // 检查8个方向的采样结果
                fixed outlineAlpha = 0;
                for (int j = 0; j < 8; j++) {
                    fixed4 offsetCol = tex2D(_MainTex, i.texcoord + i.offset[j]);
                    outlineAlpha = max(outlineAlpha, offsetCol.a);
                }

                // 如果有方向采样到不透明像素,输出描边颜色
                if (outlineAlpha > 0.01) {
                    fixed4 outlineCol = _Color;
                    outlineCol.a *= outlineAlpha;
                    return outlineCol;
                }

                // 否则返回透明
                return fixed4(0,0,0,0);
            }
            
            ENDCG
        }
    }
}

关键改动说明

  • 利用_MainTex_TexelSize获取纹理像素尺寸,将描边宽度转换为纹理空间偏移量,适配不同分辨率的纹理
  • 顶点着色器预计算8个方向的纹理偏移坐标,一次性传递给片元着色器
  • 片元着色器优先判断当前像素是否为本体像素,避免重复计算;仅在透明区域检查周围8个方向的像素,生成描边
  • 整个逻辑合并在单个Pass中,消除了重复Pass的冗余代码,同时降低了绘制调用开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 04:15:32