简单的勾边算法
下述代码列出了几种勾边的实现思路,实际游戏中在屏幕空间扩展法线的方案,基本上就够用了:
Shader "Shader/Outline" { Properties { _MainTex("MainTex", 2D) = "white" {} _Color("Color", Color) = (1, 1, 1, 1) _OutLine("OutLine", Float) = 0.1 _OutLineColor("OutLineColor", Color) = (1, 1, 1, 1) _Factor("Factor", Range(0, 1)) = 0.5 } SubShader { Tags { "Queue" = "Opaque" } Pass { Cull Front ZWrite on CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float3 color : COLOR0; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _Color; float _OutLine; float4 _OutLineColor; float _Factor; v2f vert(appdata_base v) { /* // 沿法线方向扩展(问题:近粗远细 深度覆盖 发现分离) v2f o; v.vertex.xyz += v.normal * _OutLine; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; */ /* // 在视空间挤出(问题:法线分离问题) v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 norm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal); float2 offset = TransformViewToProjection(norm.xy); o.pos.xy += offset * o.pos.z * _OutLine; return o; */ /* // 使用顶点位置作为挤出方向(问题:等于从几何中心缩放,不一致) v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 dir = normalize(v.vertex.xyz); dir = mul((float3x3)UNITY_MATRIX_IT_MV, dir); float2 offset = TransformViewToProjection(dir.xy); o.pos.xy += offset * o.pos.z * _OutLine; return o; */ // 在发现和顶点方向间插值 v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); float3 dir = normalize(v.vertex.xyz); float3 dir2 = v.normal; dir = lerp(dir2, dir, _Factor); dir = mul((float3x3)UNITY_MATRIX_IT_MV, dir); float2 offset = TransformViewToProjection(dir.xy); o.pos.xy += offset * o.pos.z * _OutLine; return o; } half4 frag(v2f i) : COLOR { return _OutLineColor; } ENDCG } Pass {...} }
通过offset操作,来实现描边的效果:
Shader "Shader/OutLine_Offset" { Properties { _MainTex("MainTex", 2D) = "white" {} _Color("Color", Color) = (1, 1, 1, 1) _OutLine("OutLine", Float) = 0.1 } SubShader { Tags { "Queue" = "Opaque" } Pass { Cull Front Offset -1,-1 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; }; float4 _OutLine; v2f vert(appdata_base v) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); return o; } half4 frag(v2f i) : COLOR { return _OutLine; } ENDCG } Pass { Offset 2,-1 CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct v2f { float4 pos : SV_POSITION; float3 color : COLOR0; float2 uv : TEXCOORD0; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _Color; v2f vert(appdata_base v) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } half4 frag(v2f i) : COLOR { float4 mainColor = tex2D(_MainTex, i.uv); float4 finalColor = mainColor * _Color; return finalColor; } ENDCG } } FallBack "VertexLit" }
这个方法不是太实用,只是一种实现思路。
使用的方案是UnityPro提供的AssetsPackage中现成的ToonShader。
http://wiki.unity3d.com/index.php/Silhouette-Outlined_Diffuse