描边的方式有好几种
一种是根据法线方向扩展模型的方式,这种方式最简单,但效果不好。
第二种是将法线转到投影空间后,再扩展顶点,这种方式比第一种好一点点,但是模型的线还是会有断面
第三种是在第二种基础上,重新计算法线,防止断线,而重新计算的法线数据,可以存入模型的切线数据中,这种出来的描边效果最好。
下面主要是第三种的实现方式:
重新计算法线的方法:
private void WirteAverageNormalToTangent(Mesh mesh) { var vertics = mesh.vertices; var normals = mesh.normals; var averageNormalHash = new System.Collections.Generic.Dictionary<Vector3, Vector3>(); for (var j = 0; j < mesh.vertexCount; j++) { if (!averageNormalHash.ContainsKey(vertics[j])) { averageNormalHash.Add(vertics[j], normals[j]); } else { averageNormalHash[vertics[j]] = (averageNormalHash[vertics[j]] + normals[j]).normalized; } } var averageNormals = new Vector3[mesh.vertexCount]; for (var j = 0; j < mesh.vertexCount; j++) { averageNormals[j] = averageNormalHash[vertics[j]]; } var tangents = new Vector4[mesh.vertexCount]; for (var j = 0; j < mesh.vertexCount; j++) { tangents[j] = new Vector4(averageNormals[j].x, averageNormals[j].y, averageNormals[j].z, 0); } mesh.tangents = tangents; }
下面是Shader的主要部分(非完整的shader),注意有两个外部参数,一个是描边的粗细,一个是描边的颜色
Pass { Tags { "Queue" = "Geometry-1" } Cull Front Zwrite Off CGPROGRAM #include "UnityCG.cginc" #include "GPUSkinningInclude.cginc" #pragma vertex vert #pragma fragment frag #pragma multi_compile_instancing #pragma multi_compile ROOTON_BLENDOFF ROOTON_BLENDON_CROSSFADEROOTON ROOTON_BLENDON_CROSSFADEROOTOFF ROOTOFF_BLENDOFF ROOTOFF_BLENDON_CROSSFADEROOTON ROOTOFF_BLENDON_CROSSFADEROOTOFF struct appdata { float4 vertex : POSITION; float3 normal:NORMAL; float2 uv : TEXCOORD0; float4 uv2 : TEXCOORD1; float4 uv3 : TEXCOORD2; float4 tangent : TANGENT; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 pos:POSITION; }; float4 _StrokeColor; UNITY_INSTANCING_BUFFER_START(Prop) UNITY_DEFINE_INSTANCED_PROP(float, _stroke) UNITY_INSTANCING_BUFFER_END(Prop) v2f vert(appdata v) { UNITY_SETUP_INSTANCE_ID(v); v2f o; float4 vertex = skin2(v.vertex, v.uv2, v.uv3); float4 pos = UnityObjectToClipPos(vertex); float3 norm = mul ((float3x3)UNITY_MATRIX_IT_MV, v.tangent.xyz); float2 offset = normalize(TransformViewToProjection(norm.xy)*pos.w); pos.xy += offset * UNITY_ACCESS_INSTANCED_PROP(Prop,_stroke) *0.01;//根据具体情况看这个里需要不需要*0.01 o.pos= pos; return o; } fixed4 frag (v2f IN):COLOR { return _StrokeColor; } ENDCG }