Usted va a tener que hacer dos veces el objeto en algún momento. Puede salirse con la representación solo de las caras que miran hacia la cámara una vez y las caras que miran hacia la cámara una vez, pero tiene sus desventajas.
La solución común más simple se realiza renderizando el objeto dos veces en la misma pasada:
- Utiliza un sombreador de vértices para invertir las normales del objeto y "explotarlo" por el tamaño del contorno y un sombreador de fragmentos para representarlo en el color del contorno.
- Sobre ese renderizado de contorno, renderiza el objeto normalmente. El orden z suele ser automáticamente correcto, más o menos, ya que el contorno está formado por las caras que se encuentran en la "parte posterior" del objeto, mientras que la figura en sí está compuesta por caras orientadas hacia la cámara.
Esto es lo suficientemente simple como para construir e implementar y evita cualquier truco de renderizado a textura, pero tiene algunos inconvenientes notables:
- El tamaño del contorno, si no lo escala por la distancia desde la cámara, variará. Los objetos más alejados tendrán un contorno más pequeño que los cercanos. Por supuesto, esto podría ser lo que realmente quieres .
- El sombreador de vértices "explotar" no funciona muy bien para objetos complejos como el esqueleto en su ejemplo, introduciendo fácilmente artefactos de lucha z en el render. Arreglarlo requiere que renderices el objeto en dos pasadas, pero te evita invertir las normales.
- Es posible que el contorno y el objeto no funcionen muy bien cuando otros objetos ocupan el mismo espacio y, en general, son difíciles de corregir cuando se combinan con sombreadores de reflexión y refracción.
La idea básica para tal sombreador se ve así (Cg, para Unity: el código es un sombreador de toon ligeramente modificado que encontré en alguna parte y no noté la fuente, por lo que es una prueba de concepto más mal escrita que una lista) sombreador de uso):
Shader "Basic Outline" {
Properties {
_Color ("Main Color", Color) = (.5,.5,.5,1)
_OutlineColor ("Outline Color", Color) = (1,0.5,0,1)
_Outline ("Outline width", Range (0.0, 0.1)) = .05
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
Name "OUTLINE"
Tags { "LightMode" = "Always" }
CGPROGRAM
#pragma exclude_renderers gles
#pragma exclude_renderers xbox360
#pragma vertex vert
struct appdata {
float4 vertex;
float3 normal;
};
struct v2f
{
float4 pos : POSITION;
float4 color : COLOR;
float fog : FOGC;
};
float _Outline;
float4 _OutlineColor;
v2f vert(appdata v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 norm = mul ((float3x3)UNITY_MATRIX_MV, v.normal);
norm.x *= UNITY_MATRIX_P[0][0];
norm.y *= UNITY_MATRIX_P[1][1];
o.pos.xy += norm.xy * _Outline;
o.fog = o.pos.z;
o.color = _OutlineColor;
return o;
}
ENDCG
Cull Front
ZWrite On
ColorMask RGB
Blend SrcAlpha OneMinusSrcAlpha
SetTexture [_MainTex] { combine primary }
}
Pass {
Name "BASE"
Tags {"LightMode" = "Always"}
CGPROGRAM
#pragma fragment frag
#pragma vertex vert
#pragma fragmentoption ARB_fog_exp2
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 viewDir : TEXCOORD1;
float3 normal : TEXCOORD2;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.normal = v.normal;
o.uv = TRANSFORM_UV(0);
o.viewDir = ObjSpaceViewDir( v.vertex );
return o;
}
uniform float4 _Color;
uniform sampler2D _MainTex;
float4 frag (v2f i) : COLOR
{
half4 texcol = tex2D( _MainTex, i.uv );
half3 ambient = texcol.rgb * (UNITY_LIGHTMODEL_AMBIENT.rgb);
return float4( ambient, texcol.a * _Color.a );
}
ENDCG
}
}
FallBack "Diffuse"
}
El otro método común representa el objeto dos veces también, pero evita el sombreador de vértices por completo. Por otro lado, no se puede hacer fácilmente en una sola pasada, y necesita renderizar a textura: renderice el objeto una vez con un sombreador de fragmentos de color de contorno "plano" y use un desenfoque (ponderado) en este renderizado en espacio en pantalla , luego renderice el objeto como de costumbre encima de él.
También hay un tercer método y posiblemente el más fácil de implementar, aunque grava un poco más a la GPU y hará que tus artistas quieran asesinarte mientras duermes, a menos que hagas que sea fácil generarlos: haz que los objetos tengan el contorno como un elemento separado malla todo el tiempo, simplemente transparente o movido a un lugar donde no se ve (como en el subsuelo) hasta que lo necesite