Vous devrez rendre l'objet deux fois à un moment donné. Vous pouvez vous en tirer avec le rendu des visages face à la caméra une seule fois et des visages opposés à la caméra une fois, mais il a ses compromis.
La solution commune la plus simple consiste à rendre l'objet deux fois dans la même passe:
- Vous utilisez un vertex shader pour inverser les normales de l'objet et le "gonfler" par la taille du contour et un fragment shader pour le rendre dans la couleur du contour
- Sur ce rendu de contour, vous rendez l’objet normalement. L'ordre z est généralement automatiquement correct, plus ou moins, car le contour est fait par les visages qui sont à l'arrière de l'objet tandis que la figure elle-même est composée de visages face à la caméra.
C'est assez simple à construire et à implémenter et évite toutes les astuces de rendu à la texture, mais présente quelques inconvénients notables:
- La taille du contour, si vous ne la mettez pas à l'échelle par rapport à la caméra, variera. Les objets plus éloignés auront un contour plus petit que ceux à proximité. Bien sûr, c'est peut-être ce que vous voulez réellement .
- Le vertex shader "blow up" ne fonctionne pas très bien pour des objets complexes comme le squelette dans votre exemple, introduisant facilement des artefacts de combat z dans le rendu. Le corriger nécessite que vous rendiez l'objet en deux passes, mais vous évite d'inverser les normales.
- Le contour et l'objet peuvent ne pas fonctionner très bien lorsque d'autres objets occupent le même espace et sont généralement pénibles à bien combiner avec des shaders de réflexion et de réfraction.
L'idée de base pour un tel shader ressemble à ceci (Cg, pour Unity - le code est un shader toon légèrement modifié que j'ai trouvé quelque part et je n'ai pas noté la source, c'est donc une preuve de concept plus mal écrite qu'un ready- shader à utiliser):
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'autre méthode courante rend également l'objet deux fois, mais évite complètement le vertex shader. D'un autre côté, cela ne peut pas être fait facilement en une seule passe, et nécessite un rendu en texture: rendez l'objet une fois avec un shader de fragment de contour "plat" et utilisez un flou (pondéré) sur ce rendu dans l'espace d'écran , puis restituer l'objet comme d'habitude au-dessus.
Il existe également une troisième méthode, et peut-être la plus simple à mettre en œuvre, bien qu'elle taxe un peu plus le GPU et donnera envie à vos artistes de vous assassiner dans votre sommeil, sauf si vous les facilitez à générer: Demandez aux objets d'avoir le contour comme un élément distinct maillage tout le temps, juste totalement transparent ou déplacé quelque part où il n'est pas vu (comme profondément sous terre) jusqu'à ce que vous en ayez besoin
En plus de la réponse de Martin Sojkas, pour les objets statiques (ou sprites), vous pouvez vous en sortir avec quelque chose de plus simple.
Vous pouvez enregistrer le même sprite mais avec le contour dans votre atlas de texture ou une autre texture entièrement, ce qui facilite le changement. Cela vous permettra également de créer des contours personnalisés plus visuellement attrayants ou tout simplement différents.
L'autre méthode consiste à enregistrer l'image-objet sous la forme d'une seule couleur légèrement plus grande et à effectuer le rendu juste avant que l'image-objet elle-même ne soit rendue. Cela vous donnera la possibilité de changer facilement la couleur de la sélection, et vous n'aurez peut-être pas besoin d'autant de formes de couleurs différentes que vous auriez besoin de sprites de contour avec la méthode # 1.
Les deux augmenteront cependant votre empreinte mémoire.
la source
Comme souligné dans les commentaires de la réponse de Martin Sojka, un effet similaire peut également être obtenu en utilisant le pochoir ou le tampon de profondeur, comme détaillé par Max McGuire sur FlipCode:
http://www.flipcode.com/archives/Object_Outlining.shtml
Il s'agit essentiellement de dessiner une version filaire du modèle que vous souhaitez décrire avec une largeur de ligne accrue (ou dans le cas où cela n'est pas possible, comme dans D3D par exemple, en utilisant des quadrilatères face à la caméra pour les lignes) tout en définissant la mémoire tampon du pochoir à une valeur constante.
Cette approche peut être un peu datée à l'aide d'OpenGL d'aujourd'hui et pour que le contour de l'objet apparaisse flou, le rendu de la texture sera toujours nécessaire.
la source