02_描边追加

添加描边Pass

参数增加

新增外描边宽度颜色和Z偏移参数,以及Toggle开关控制描边Pass是否开启 pass内声明

Pass输入输出结构

这里获取了模型第二套UV,里面是平滑法线数据

顶点着色器

最简单的法线外扩,这样直接外扩会导致断线问题,所以才需要平滑法线数据

1
2
3
4
5
//描边宽度
                float outlineWidth = _OutlineWidth * 0.001;
                //法线外扩
                float3 positionWS = positionInputs.positionWS.xyz;
                positionWS += normalInputs.normalWS * outlineWidth;

分段解析

先进行判断是否需要描边Pass,不需要的话直接返回0 设置描边宽度 这里缩放函数使用了Colin大佬的代码 https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample/blob/master/NiloOutlineUtil.hlsl 法线外扩需要读取第二套UV的平滑法线信息 八面体映射读取函数 使用平滑法线后描边就不会断线了,但嘴角的线依旧有缺陷,所以需要将面部的描边向后偏移 这里需要裁剪空间下描边顶点Z轴方向偏移,代码来自于Colin大佬

顶点代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 // 顶点着色器函数
            Varyings vert(Attributes input)
            {
                #if !_OUTLINE_PASS
                return  (Varyings)0;
                #endif
                //获取世界空间下法线和位置等信息
                VertexPositionInputs positionInputs = GetVertexPositionInputs(input.positionOS.xyz);
                VertexNormalInputs normalInputs = GetVertexNormalInputs(input.normalOS, input.tangentOS);
                //描边宽度
                float outlineWidth = _OutlineWidth;
                //描边宽度随相机深度进行缩放
                outlineWidth *= GetOutlineCameraFovAndDistanceFixMultiplier(positionInputs.positionVS.z);
                
                //法线外扩
                float3 positionWS = positionInputs.positionWS.xyz;
                //获取平滑法线
                float3 smoothNormal = OctTounitVector(input.texcoord1);
                //构建TBN矩阵
                float3x3 TBN = float3x3(
                    normalInputs.tangentWS,
                    normalInputs.bitangentWS,
                    normalInputs.normalWS
                );
                smoothNormal = mul(smoothNormal, TBN);
                positionWS += smoothNormal * outlineWidth;

                Varyings output = (Varyings)0;
                output.positionCS = NiloGetNewClipPosWithZOffset(TransformWorldToHClip(positionWS), _OutlineZOffset);
                output.FogFactor = ComputeFogFactor(positionInputs.positionCS.z);
                output.uv = input.texcoord0;

                return output;

            }

片元着色器

这里进行描边的染色,需要针对不同区域进行不同染色

获取材质ID

这里需要读取R通道,里面是存的材质ID

添加ID选择通用的函数宏

这里定义了4个函数宏,两个“##”表示拼接 TYPE TYPE##2 TYPE##3 TYPE##4 分别对应 1维到4维数据类型,同时在下面进行函数声明

1
2
3
4
5
6
7
8
9
10
11
      #define DFINE_SELECT(TYPE)\
        TYPE select(int id, TYPE e0, TYPE e1, TYPE e2, TYPE e3, TYPE e4)    {return TYPE(id > 0 ? (id > 1 ? (id > 2 ? (id > 3 ? e4 : e3) : e2) : e1) : e0);}\
        TYPE##2 select(int id, TYPE##2 e0, TYPE##2 e1, TYPE##2 e2, TYPE##2 e3, TYPE##2 e4)  {return TYPE##2(id > 0 ? (id > 1 ? (id > 2 ? (id > 3 ? e4 : e3) : e2) : e1) : e0);}\
        TYPE##3 select(int id, TYPE##3 e0, TYPE##3 e1, TYPE##3 e2, TYPE##3 e3, TYPE##3 e4)  {return TYPE##3(id > 0 ? (id > 1 ? (id > 2 ? (id > 3 ? e4 : e3) : e2) : e1) : e0);}\
        TYPE##4 select(int id, TYPE##4 e0, TYPE##4 e1, TYPE##4 e2, TYPE##4 e3, TYPE##4 e4)  {return TYPE##4(id > 0 ? (id > 1 ? (id > 2 ? (id > 3 ? e4 : e3) : e2) : e1) : e0);}

        DFINE_SELECT(bool)
        DFINE_SELECT(uint)
        DFINE_SELECT(int)
        DFINE_SELECT(float)
        DFINE_SELECT(half)

最后进行输出 描边颜色x0.2压暗,除了面部为FF8181,其余先调成555555

片元代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 片元着色器函数
            float4 frag(Varyings input):SV_Target
            {
                #if !_OUTLINE_PASS
                clip(-1);
                #endif
                float3 outlineColor = 0;

                #if _DOMAIN_FACE
                {
                    outlineColor = _OutlineColor1.rgb;
                }
                #elif _DOMAIN_BODY
                {
                    outlineColor = _OutlineColor2.rgb;
                    float4 var_OtherDataTex1 = SAMPLE_TEXTURE2D(_OtherDataTex1, sampler_OtherDataTex1, input.uv);
                    int materialId = max(0, 4 - floor(var_OtherDataTex1.r * 5));
                    outlineColor = select(materialId, _OutlineColor1, _OutlineColor2, _OutlineColor3, _OutlineColor4, _OutlineColor5);
                }
                #endif

                outlineColor *= 0.2;
               
                float4 color = float4(outlineColor, 1);
                color.rgb = MixFog(color.rgb, input.FogFactor);
                return color;

            }

Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • URP - RendererFeature :ScreenSpaceOutline
  • 平滑法线处理 - 八面体映射
  • Lv.3 Unity主线:一个简单的PBRShader
  • 理论支线:直接光漫反射与GGX高光的混合问题
  • 理论支线:PBR - 基于图像的照明( image based lighting-IBL)
  • # #