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
Además de la respuesta de Martin Sojkas, para objetos estáticos (o sprites) puede salirse con la suya con algo más simple.
Puede guardar el mismo sprite pero con el contorno en su atlas de texturas u otra textura por completo, lo que facilita el cambio. Esto también le permitirá hacer contornos personalizados que sean más atractivos visualmente o que simplemente se vean diferentes.
El otro método es guardar el sprite como una forma de un color que es ligeramente más grande y renderizarla justo antes de que se renderice el sprite. Esto le dará la capacidad de cambiar fácilmente el color de la selección, y es posible que no necesite formas de color tan diferentes como necesitaría sprites de contorno con el método # 1.
Sin embargo, ambos aumentarán su huella de memoria.
fuente
Como se señaló en los comentarios a la respuesta de Martin Sojka, también se puede lograr un efecto similar utilizando la plantilla o el búfer de profundidad, como detalla Max McGuire en FlipCode:
http://www.flipcode.com/archives/Object_Outlining.shtml
Básicamente, está dibujando una versión de estructura de alambre del modelo que desea delineado con un mayor ancho de línea (o en caso de que esto no sea posible, como en D3D, por ejemplo, usando quads de líneas para cámaras) mientras establece el búfer de la plantilla en un valor constante.
Este enfoque podría estar un poco anticuado usando el OpenGL de hoy y para que el contorno del objeto sea borroso, aún será necesario renderizar a textura.
fuente