描边效果
关于描边效果有很多种,单顶点Shader比较常见了就有三种,分别是视角空间法线外拓,裁剪空间法线外拓,以及模型空间法线外拓,这里只详细介绍一种性能相较最好的一种描边效果,模型法线外拓描边效果
描边原理:
通过两个Pass通道,先渲染模型的背面,可以绘制偏大一些,然后在进行正常的模型绘制,这样正常的模型就会覆盖在事先画好的背面颜色上,通过调整渲染的背面图形大小,来实现描边的粗细。
下图为效果图:
1.Cull Front 剔除模型正面
至于为什么只渲染背面,不对正面进行渲染,下面两张图可以给出最好的解释。
下图是不渲染正面,只渲染背面的效果。
可以看到当有另一个模型穿插进去的时候,该模型属于正常状态。
2.下图是正反两面都渲染的的效果。
可以看到,当有另一个模型穿插进去的时候,该模型的描边,就暴露的出来,这显然是不正确的,所以为了避免不必要的问题, 我们需要在描边的一开始就正确的使用渲染方式 Cull Front
剔除正面 。
核心算法:
//物体顶点法线外拓
v.vertex.xyz+=normalize(v.normal)*_Outline/10;
;
附上源码:
Shader脚本 漫反射+描边
Shader "Unlit/ModelOutLine"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Diffuse("Color",Color) =(1,1,1,1)
_OutLineColor("OutlineColor",Color)=(1,1,1,1)
_Outline("Outline",Range(0,1))=1
}
SubShader
{
Tags {"Queue"= "Geometry+1000" "RenderType"="Opaque" }
Pass
{
//不渲染正面 防止模型穿插导致 描边暴露
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _OutLineColor;
float _Outline;
struct v2f
{
float4 vertex:SV_POSITION;
};
v2f vert(appdata_base v)
{
//物体顶点法线外拓
v.vertex.xyz+=normalize(v.normal)*_Outline/10;
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
return o;
};
fixed4 frag(v2f i):SV_TARGET
{
return fixed4(_OutLineColor);
};
ENDCG
}
Pass
{
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
float4 _Diffuse;
struct v2f
{
float4 vertex:SV_POSITION;
float2 uv:TEXCOORD0;
float3 worldLightNormal:TEXCOORD1;
float3 worldNormal:TEXCOORD2;
};
v2f vert(appdata_base v)
{
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
float3 worldPos=mul(unity_ObjectToWorld,v.vertex);
o.worldLightNormal=UnityWorldSpaceLightDir(worldPos);
o.worldNormal=UnityObjectToWorldNormal(v.normal);
return o;
};
fixed4 frag(v2f i):SV_TARGET
{
float3 worldNormalDir=normalize(i.worldNormal);
float3 WorldLightDir=normalize(i.worldLightNormal);
float3 texColor=tex2D(_MainTex,i.uv);
float ambient=UNITY_LIGHTMODEL_AMBIENT.rgb*texColor.rgb;
float3 diffuse=_LightColor0.rgb*_Diffuse.rgb*texColor.rgb*(dot(worldNormalDir,WorldLightDir)*0.5+0.5);
float3 color=diffuse+ambient;
return fixed4(color,1);
};
ENDCG
}
}
Fallback "Diffuse"
}
下面列出三种顶点着色器描边方式:
物体空间法线外拓 性能最好
v2f vert(appdata_base v)
{
//物体顶点法线外拓
v.vertex.xyz+=normalize(v.normal)*_Outline/10;
v2f o;
o.vertex=UnityObjectToClipPos(v.vertex);
return o;
};
视角空间法线外拓
v2f vert(appdata_base v)
{
//视角空间法线外拓
float4 pos = mul(UNITY_MATRIX_V, mul(unity_ObjectToWorld, v.vertex));
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
pos = pos + float4(normal,0) * _Outline;
o.vertex = mul(UNITY_MATRIX_P, pos);
o.vertex=UnityObjectToClipPos(v.vertex);
return o;
};
裁剪空间法线外拓
v2f vert(appdata_base v)
{
//裁剪空间法线外拓
o.vertex = UnityObjectToClipPos(v.vertex);
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV,v.normal));
float2 viewNoraml = TransformViewToProjection(normal.xy);
o.vertex.xy += viewNoraml * _Outline;
return o;
};