Você vai ter que processar o objeto duas vezes em algum ponto. Você pode renderizar apenas os rostos voltados para a câmera uma vez e os rostos voltados para longe da câmera uma vez, mas tem suas vantagens e desvantagens.
A solução comum mais simples é feita renderizando o objeto duas vezes na mesma passagem:
- Você usa um sombreador de vértice para inverter as normais do objeto e "ampliá-lo" pelo tamanho do contorno e um sombreador de fragmento para renderizá-lo na cor do contorno
- Sobre essa renderização de contorno, você renderiza o objeto normalmente. A ordem z é geralmente automaticamente correta, mais ou menos, já que o contorno é feito pelas faces que estão na parte de trás do objeto, enquanto a própria figura é composta de faces voltadas para a câmera.
Isso é simples o suficiente para criar e implementar e evita truques de renderização para textura, mas tem algumas desvantagens visíveis:
- O tamanho do contorno, se você não o escalar pela distância da câmera, variará. Objetos mais distantes terão um contorno menor do que os próximos. Claro, isso pode ser o que você realmente deseja .
- O sombreador de vértice "explodido" não funciona muito bem para objetos complexos, como o esqueleto no seu exemplo, introduzindo facilmente artefatos de combate z na renderização. Para corrigi-lo, é necessário renderizar o objeto em duas passagens, mas evita que você reverta as normais.
- O contorno e o objeto podem não funcionar muito bem quando outros objetos estão ocupando o mesmo espaço e, em geral, são um problema para acertar quando combinados com shaders de reflexão e refração.
A idéia básica para um sombreador se parece com a seguinte (Cg, para Unity - o código é um sombreador de toon ligeiramente modificado que encontrei em algum lugar e não anotei a fonte, portanto, é mais uma prova de conceito mal escrita do que uma shader 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"
}
O outro método comum renderiza o objeto duas vezes também, mas evita o sombreador de vértice completamente. Por outro lado, não pode ser feito facilmente em uma única passagem e precisa renderizar para textura: renderize o objeto uma vez com um sombreador de fragmento "plano" na cor de contorno e use um borrão (ponderado) nessa renderização em espaço na tela e , em seguida, renderize o objeto normalmente.
Há também um terceiro método e, possivelmente, mais fácil de implementar, embora trate um pouco mais a GPU e faça com que seus artistas o matem enquanto você dorme, a menos que você facilite a geração: Os objetos têm o esboço como separado malha o tempo todo, apenas totalmente transparente ou movido para algum lugar onde não seja visto (como no subsolo) até que você precise