effet d'objet de contour


26

Comment puis-je obtenir un effet de contour similaire à ceux trouvés dans League of Legends ou Diablo III?

Aperçu de League of Legends Aperçu de League of Legends Contour de Diablo III

Est-ce fait à l'aide d'un shader? Comment?
Je préférerais des réponses qui ne sont pas liées à un moteur particulier mais que je peux adapter à n'importe quel moteur sur lequel je travaille.

Réponses:


19

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


Le tampon de gabarit n'est-il pas une approche couramment utilisée ici?
edA-qa mort-ora-y

1
@ edA-qamort-ora-y: Cela pourrait aussi fonctionner, mais je n'ai jamais essayé cette approche, donc je ne peux pas en parler. :) Si vous avez un algorithme de travail en tête, n'hésitez pas à ajouter cette méthode comme autre réponse également.
Martin Sojka

Je n'en sais pas beaucoup plus moi-même, seulement que les contours sont fréquemment mentionnés en référence aux tampons de pochoir. :) Il semble que ce soit une version plus matérielle de votre première approche (deux passes, la première plus grande).
edA-qa mort-ora-y

L'approche du tampon de gabarit peut utiliser plus de bande passante pour la mise à jour et l'effacement des tampons de gabarit et nécessite plusieurs passes. L'approche des listes de Martin peut être effectuée en un seul passage dans certains cas limités, deux au maximum, et nécessite une surcharge de bande passante minimale.
Sean Middleditch

Cette approche (faire exploser la taille le long des normales) ne fonctionne pas avec des objets comme des cubes (en particulier avec une caméra ortho). Une solution à cela?
NPS

4

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.


4

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.