Si dovrà avere per rendere l'oggetto per due volte ad un certo punto. Si può ottenere via con il rendering solo i volti di fronte alla telecamera una volta e le facce rivolte via dalla fotocamera, una volta, ma ha i suoi compromessi.
La soluzione comune più semplice viene eseguita eseguendo il rendering dell'oggetto due volte nello stesso passaggio:
- Si utilizza uno shader di vertice per invertire le normali dell'oggetto e "ingrandirlo" per le dimensioni del contorno e uno shader di frammenti per renderlo nel colore del contorno
- Oltre quel rendering di contorno, si esegue il rendering dell'oggetto normalmente. L'ordine z di solito è automaticamente corretto, più o meno, poiché il contorno è costituito dalle facce che si trovano nella "parte posteriore" dell'oggetto mentre la figura stessa è composta da facce rivolte verso la telecamera.
Questo è abbastanza semplice da costruire e implementare ed evita qualsiasi trucco da render a texture, ma presenta alcuni inconvenienti evidenti:
- La dimensione del contorno, se non la ridimensionate in base alla distanza dalla fotocamera, varierà. Gli oggetti più lontani avranno un contorno più piccolo di quelli vicini. Certo, questo potrebbe essere quello che vuoi davvero .
- Lo shader di vertice "blow up" non funziona molto bene per oggetti complessi come lo scheletro nel tuo esempio, introducendo facilmente nel rendering manufatti che combattono z. La correzione richiede il rendering dell'oggetto in due passaggi, ma consente di evitare di invertire le normali.
- Il contorno e l'oggetto potrebbero non funzionare molto bene quando altri oggetti occupano lo stesso spazio e in generale sono un dolore da ottenere se combinati con shader di riflessione e rifrazione.
L'idea di base per un tale shader è simile a questa (Cg, per Unity - il codice è un toon shader leggermente modificato che ho trovato da qualche parte e non ho notato la fonte, quindi è una prova di concetto più mal scritta di un pronto- shader da usare):
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"
}
L'altro metodo comune esegue il rendering anche dell'oggetto due volte, ma evita completamente lo shader di vertice. D'altra parte, non può essere facilmente eseguito in un singolo passaggio e ha bisogno di render-to-texture: renderizza l'oggetto una volta con uno shader di frammenti di contorno "piatto" e usa una sfocatura (ponderata) su questo rendering in spazio dello schermo , quindi renderlo come al solito sopra di esso.
C'è anche un terzo e forse il metodo più semplice da implementare, sebbene tassi un po 'di più la GPU e farà desiderare ai tuoi artisti di ucciderti nel sonno a meno che non sia facile per loro generare: Avere gli oggetti hanno il contorno come un separato mesh tutto il tempo, solo completamente trasparente o spostato da qualche parte dove non è visto (come deep underground) fino a quando non ne hai bisogno