【Unity Shader 描边效果_案例分享】

简介: 【Unity Shader 描边效果_案例分享】

1.实现逻辑


描边效果Shader有多种实现方式,可以通过后处理和MatCap实现。

这次主要想展示的是通过两个Pass实现。

当Shader中有多个Pass时,渲染流程会安装顺序依次执行,于是后面的Pass效果会覆盖前面Pass所绘制的图像。

实现的逻辑是:


1.1


第一层,将模型的顶点沿着法线方向膨胀一段距离,膨胀的距离就是描边的宽度,膨胀的距离可以作为属性开放到材质面板上,方便调用。然后再为膨胀之后的模型指定一个纯色进行着色,着色不需要参与任何光照交互,膨胀之后的纯色就是描边的颜色。将颜色也作为属性开放到材质面板,方便后期更换颜色。


1.2


第二层,模型以正常效果进行显示。也就是Surface Shader,并使用Standard Specular光照模型。


1.3


在正常情况下,第一层膨胀后的模型会将第二层的模型完全笼罩,第二层的模型是无法通过深度测试的,最终只会显示第一层膨胀后的效果,解决这个就需要让第二次的模型通过深度测试。


1.4


办法就是关闭第一层的深度写入,这样就没有第一层模型的深度值,第二层模型自然就可以通过深度测试了。


1.5


这样会出现一种情况,当物体关闭深度写入之后,在膨胀着一层后面的物体就不知道自己是被遮挡的,本该被遮挡,因为没有深度写入,现在反而通过了速度测试,因此就会覆盖掉膨胀的这一层模型。


- 避免这一状况出现

需要使该物体在所有不透明物体绘制完成之后再进行绘制,因此更改Shader的渲染队列为Transparent,如此一来绘制的图像就不会被后面的物体覆盖了。


2.编写Shader


理解了实现逻辑,那么接下来按照逻辑思路开始编写Shader。


2.1.开放属性


正常效果相关属性:

    [Header(Texture Group)]
    [Space(10)]
    _Albedo ("Albedo" , 2D) = "white" {}
    [NoScaleOffset]_Specular ("Specular (RGB - A)" , 2D) = "black" {}
    [NoScaleOffset]_Normal("Normal" , 2D) = "bump" {}
    [NoScaleOffset]_AO ("Ambient Occlusion" , 2D) = "white" {}


讲解:

Properties代码块开放了常规Specular工作流所需要的贴图有Albedo、Specular、Normal 和 AO,至于Smoothness属性,本案例参照Unity内置的Standard Specular Shader,将其合并到Specular贴图的alpha通道中。

通过Albedo纹理贴图的Tiling和Offset控制所有的纹理贴图,也就是使用Albedo的纹理坐标对所有纹理贴图进行采样,因此除了Albedo,其他所有纹理贴图的Tiling和Offset都会失效,如是在这些属性前添加[NoScaleOffset]指令以隐藏Tiling和Offset。

为了对属性进行类别区分,在这些属性之前添加[Header(Texture Group)]指令,从而在材质面板上显示“Texture Group”文字以起到分组提醒的作用。


描边相关属性:
        [Header(Outline Properties)]
        [Space(10)]
        _OutlineColor (“OutlineColor” , Color) = (1,0,1,1)
        _OutlineWidth (“OutlineWidth” , Range(0,0.1)) = 0.01


讲解:

除了开放正常效果所需的贴图属性,还开放了两个用于控制描边效果的属性,分别为:描边颜色 _Outlinecolor 描边宽度_OutlineWidth。

为了对属性进行类别区分,同样在这些属性之前添加[Header(Outline Properties)]指令,从而在材质面板上显示“Outline Properties”文字以起到分组提醒的作用。


2.2.SubShader标签


代码:
Tags { "RenderType"="Opaque" "Queue"="Transparent" }
讲解:

属于不透明效果,因此渲染类型设置为“Opaque”。为了使物体在所有不透明物体之后再进行绘制,还需要将渲染队列设置为“Transparent”。


2.3.描边Pass


代码:
        Pass
        {
            ZWrite Off
            
            CGPROGRAM
    
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            fixed4 _OutlineColor;
            fixed _OutlineWidth;

            v2f vert (appdata_base v)
            {
                v2f o;
                v.vertex.xyz += v.normal * _OutlineWidth;
                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return _OutlineColor;
            }

          ENDCG

        }


讲解:

在描边效果的Pass中,为了不遮挡后面的Pass,需要将深度写入关闭。

在顶点着色器中,需要将顶点的法线向量乘以_OutlineWidth属性,用于控制法线向量的长度,乘积与顶点相加,使顶点沿着法线偏移,从而产生膨胀效果。

在片段着色器中不需要做任何计算,直接返回 _OutlineColor属性即可。


2.4.描边Pass


代码:
          CGPROGRAM
  
          #pragma surface surf StandardSpecular fullforwardshadows
  
          struct Input
          {
              float2 uv_Albedo;
          };
  
          sampler2D _Albedo;
          sampler2D _Specular;
          sampler2D _Normal;
          sampler2D _AO;
  
          void surf (Input IN , inout SurfaceOutputStandardSpecular o)
          {
              fixed4 c = tex2D (_Albedo , IN.uv_Albedo);
              o.Albedo = c.rgb;
  
              fixed4 specular = tex2D(_Specular , IN.uv_Albedo);
              o.Specular = specular.rgb;
              o.Smoothness = specular.a;
  
              o.Normal = UnpackNormal(tex2D (_Normal, IN.uv_Albedo));
              o.Occlusion = tex2D(_AO, IN.uv_Albedo);
          }


讲解:

使用Surface Shader编写模型的正常效果。在编译指令中,定义光照模型为StandarSpecular,然后添加fullforwardshadows指令,使物体支持所有灯光类型的投影。

在表面着色器中使用Albedo的纹理坐标uv_Albedo 对所有的贴图进行采样,然后分别输出到对应的表面属性上。其中Smoothness被合并在Specular纹理的Alpha通道中,因此将specular.a输出到Smoothness。


下面是完整代码:

Shader "Sample/Outline"
{
    Properties
    {
        [Header(Texture Group)]
        [Space(10)]
        _Albedo ("Albedo", 2D) = "white" {}
        [NoScaleOffset]_Specular ("Specular (RGB - A)" , 2D) = "black" {}
        [NoScaleOffset]_Normal("Normal" , 2D) = "bump" {}
        [NoScaleOffset]_AO ("Ambient Occlusion" , 2D) = "white" {}

        [Header(Outline Properties)]
        [Space(10)]
        _OutlineColor ("OutlineColor" , Color) = (1,0,1,1)
        _OutlineWidth ("OutlineWidth" , Range(0,1)) = 0.01
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Transparent" }
        LOD 100

        Pass
        {
            ZWrite Off
            
            CGPROGRAM
    
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct v2f
            {
                float4 vertex : SV_POSITION;
            };

            fixed4 _OutlineColor;
            fixed _OutlineWidth;

            v2f vert (appdata_base v)
            {
                v2f o;
                v.vertex.xyz += v.normal * _OutlineWidth;
                o.vertex = UnityObjectToClipPos(v.vertex);

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                return _OutlineColor;
            }

        ENDCG

        }

        //----------Regular layer----------
      CGPROGRAM
  
      #pragma surface surf StandardSpecular fullforwardshadows
  
      struct Input
      {
          float2 uv_Albedo;
      };
  
      sampler2D _Albedo;
      sampler2D _Specular;
      sampler2D _Normal;
      sampler2D _AO;
  
      void surf (Input IN , inout SurfaceOutputStandardSpecular o)
      {
          fixed4 c = tex2D (_Albedo , IN.uv_Albedo);
          o.Albedo = c.rgb;
  
          fixed4 specular = tex2D(_Specular , IN.uv_Albedo);
          o.Specular = specular.rgb;
          o.Smoothness = specular.a;
  
          o.Normal = UnpackNormal(tex2D (_Normal, IN.uv_Albedo));
          o.Occlusion = tex2D(_AO, IN.uv_Albedo);
      }
      ENDCG
    }
}
相关文章
|
图形学 Android开发 iOS开发
|
13天前
|
图形学
【Unity Shader#自定义材质面板_第二篇】
【Unity Shader#自定义材质面板_第二篇】
|
13天前
|
存储 图形学
【Unity Shader 消融效果_案例分享】
【Unity Shader 消融效果_案例分享】
|
13天前
|
图形学
【Unity的HDRP下ShaderGraph实现权重缩放全息投影_案例分享(内附源码)】
【Unity的HDRP下ShaderGraph实现权重缩放全息投影_案例分享(内附源码)】
|
13天前
|
图形学 异构计算
【Unity Shader 中Pass相关介绍_第四篇】
【Unity Shader 中Pass相关介绍_第四篇】
|
13天前
|
图形学 C++
【Unity Shader入坑篇---有ASE创建Unity Shader,那么Unity Shader代码的相关知识还有必要学或了解吗?】
【Unity Shader入坑篇---有ASE创建Unity Shader,那么Unity Shader代码的相关知识还有必要学或了解吗?】
|
13天前
|
图形学
【Unity Shader#自定义材质面板_第一篇】
【Unity Shader#自定义材质面板_第一篇】
|
13天前
|
图形学
【#Unity Shader#Amplify Shader Editor(ASE)_第八篇】
【#Unity Shader#Amplify Shader Editor(ASE)_第八篇】
|
13天前
|
图形学
【#Unity Shader#Amplify Shader Editor(ASE)_第三篇】
【#Unity Shader#Amplify Shader Editor(ASE)_第三篇】
|
13天前
|
图形学
【Unity URP渲染管线下设置灯光数量上限_灯光不显示问题案例分享】
【Unity URP渲染管线下设置灯光数量上限_灯光不显示问题案例分享】