Unity使用Shader实现3D模型外描边效果

一、前言

有同学问我3D模型的外描边怎么弄,其实网上有很多文章写了实现方式,我就再写个简单的实现和操作流程吧~

二、3D模型外描边效果

在这里插入图片描述

三、如何制作

将最下面的shader代码保存为ObjectOutline.shader文件,创建一个material材质球:ObjectOutline.mat
shader拖动到材质球ObjectOutline
在这里插入图片描述
给材质球设置贴图和描边,
在这里插入图片描述
最后将材质球拖给模型即可
在这里插入图片描述

三、shader代码

Shader "Custom/ObjectOutline" {
    
    

    Properties{
    
      
        _Diffuse("Diffuse", Color) = (1,1,1,1)  
        _OutlineCol("OutlineCol", Color) = (1,0,0,1)  
        _OutlineFactor("OutlineFactor", Range(0,1)) = 0.1  
        _MainTex("Base 2D", 2D) = "white"{
    
    }  
    }
    
    //子着色器    
    SubShader  
    {
    
      
        //描边使用两个Pass,第一个pass沿法线挤出一点,只输出描边的颜色  
        Pass  
        {
    
      
            //剔除正面,只渲染背面,对于大多数模型适用,不过如果需要背面的,就有问题了  
            Cull Front
            
            CGPROGRAM  
            //使用vert函数和frag函数  
            #pragma vertex vert  
            #pragma fragment frag  
            #include "UnityCG.cginc"  
            fixed4 _OutlineCol;  
            float _OutlineFactor;
            
            struct v2f  
            {
    
      
                float4 pos : SV_POSITION;  
            }; 
            
            v2f vert(appdata_full v)  
            {
    
      
                v2f o;  
                //在vertex阶段,每个顶点按照法线的方向偏移一部分,不过这种会造成近大远小的透视问题  
                //v.vertex.xyz += v.normal * _OutlineFactor;  
                o.pos = UnityObjectToClipPos(v.vertex); 
                //将法线方向转换到视空间  
                float3 vnormal = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);  
                //将视空间法线xy坐标转化到投影空间,只有xy需要,z深度不需要了  
                float2 offset = TransformViewToProjection(vnormal.xy);  
                //在最终投影阶段输出进行偏移操作  
                o.pos.xy += offset * _OutlineFactor;  
                return o;  
            }
            
            fixed4 frag(v2f i) : SV_Target  
            {
    
      
                //这个Pass直接输出描边颜色  
                return _OutlineCol;  
            }  
            ENDCG  
        }
        
        //正常着色的Pass  
        Pass  
        {
    
      
            CGPROGRAM     
            //使用vert函数和frag函数  
            #pragma vertex vert  
            #pragma fragment frag  
            //引入头文件  
            #include "Lighting.cginc"  
            //定义Properties中的变量  
            fixed4 _Diffuse;  
            sampler2D _MainTex;  
            //使用了TRANSFROM_TEX宏就需要定义XXX_ST  
            float4 _MainTex_ST;
            
            //定义结构体:vertex shader阶段输出的内容  
            struct v2f  
            {
    
       
                float4 pos : SV_POSITION;  
                float3 worldNormal : TEXCOORD0;  
                float2 uv : TEXCOORD1;  
            };
            
            //定义顶点shader,参数直接使用appdata_base(包含position, noramal, texcoord)  
            v2f vert(appdata_base v)  
            {
    
      
                v2f o;  
                o.pos = UnityObjectToClipPos(v.vertex);  
                //通过TRANSFORM_TEX宏转化纹理坐标,主要处理了Offset和Tiling的改变,默认时等同于o.uv = v.texcoord.xy;  
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);  
                o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);  
                return o;  
            }
            
            //定义片元shader  
            fixed4 frag(v2f i) : SV_Target  
            {
    
      
                //unity自身的diffuse也是带了环境光,这里我们也增加一下环境光  
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse.xyz;  
                //归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
                fixed3 worldNormal = normalize(i.worldNormal);  
                //把光照方向归一化  
                fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);  
                //根据半兰伯特模型计算像素的光照信息  
                fixed3 lambert = 0.5 * dot(worldNormal, worldLightDir) + 0.5; 
                //最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
                fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz + ambient;  
                //进行纹理采样  
                fixed4 color = tex2D(_MainTex, i.uv);  
                color.rgb = color.rgb* diffuse;  
                return fixed4(color);  
            }      
            ENDCG  
        }  
    }  
    //前面的Shader失效的话,使用默认的Diffuse  
    FallBack "Diffuse"  
}

猜你喜欢

转载自blog.csdn.net/linxinfa/article/details/108358127