白泽图

  • 文章
    • Unity渲染
    • Unity项目开发
    • 工具
    • 数学
    • 算法
    • 网站搭建
    • 网络&操作系统
蒋程个人博客
互联网技术经验总结&分享
  1. 首页
  2. Unity渲染
  3. 正文

UnityShader Outline (法线外扩法)

2021-01-16 2695点热度 0人点赞 0条评论

实现原理

1.先用一个Pass将原来的物体沿着该物体的顶点法线外移,使它变得比之前大

2.使用Outline的颜色填充整个物体,并且剔除正面的渲染,得到一个放大的背面

3.用第二个Pass正常渲染渲染,第二次正常渲染会挡住第一个渲染的背面,但是第一个Pass变大导致部分挡不住,则挡不住的那部分就变成了该物体的外描边

代码图如:

//法线外扩实现法
Shader "Custom/Outline"
{
    Properties
    {
        _MainTex("MainTex", 2D) = "white" {}
        _Outline("Outline",float) = 0.1
        _OutlineColor("OutlineColor",Color) = (0,0,0,1)
    }
        SubShader
        {
            Tags { "RenderType" = "Transparent" } //Transparent可以在opaque之后画,否则会被天空盒遮住
            LOD 100
            Pass
            {
                Name "Outline_Fst"//第一个Pass,法线外扩+正面剔除
                Cull Front
                ZWrite Off
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                struct appdata
                {
                    float4 vertex : POSITION;
                    float3 normal : NORMAL;
                };
                struct v2f
                {
                    float4 vertex : SV_POSITION;
                };
                float _Outline;
                float4 _OutlineColor;
                v2f vert(appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    //把法线转换到视图(相机)空间,UNITY_MATRIX_IT_MV为模型视图矩阵的逆转置矩阵
                    float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);
                    //把法线转换到投影(剪裁)空间
                    float2 clip_normal_xy = mul((float2x2)UNITY_MATRIX_P,vnormal.xy);
                    //向法线方向延伸
                    o.vertex.xy = o.vertex.xy + clip_normal_xy * _Outline;
                    return o;
                }

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

                Name "Outline_Sec" //第二个Pass,正常渲染
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv :TEXCOORD0;
                };
                struct v2f
                {
                    float4 vertex : SV_POSITION;
                    float2 uv:TEXCOORD0;
                };
                sampler2D _MainTex;
                v2f vert(appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
                fixed4 frag(v2f i) : SV_Target
                {
                    return tex2D(_MainTex,i.uv);
                }
                ENDCG
            }
        }
}

核心点:

1.渲染类型改为Transparent目的是在opaque之后画,否则会被天空盒(opaque)遮住

2.法线从模型空间转到视图(相机)空间,使用的是UNITY_MATRIX_IT_MV而不是UNITY_MATRIX_I_MV,原因是非等比缩放情况下,使用和顶点变换一样的UNITY_MATRIX_I_MV会导致法线异常,我在非等比缩放模型法线转换一文中有写过详细推导,此处只给结论:法线的变换矩阵为模型变换矩阵的逆转置矩阵

效果图:

优点:不需要写深度缓冲

缺点:棱角分明的物体(顶点非连续)的物体会穿帮

效果图:

总结,项目中的模型一般来说不会出现棱角分明的情况,所以此方法是项目中最常使用的方法,真正的问题在于此方法是硬描边,无法达到类似光(带模糊效果)的描边效果

标签: 暂无
最后更新:2021-01-16

蒋程

这个人很懒,什么都没留下

点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论

COPYRIGHT © 2023 白泽图. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

登录
注册|忘记密码?