How can I achieve an outline effect similar to the ones found in League of Legends or Diablo III?

League of Legends outline League of Legends outline Diablo III outline

Is it done using a shader? How?
I would prefer answers that are not tied up to any particular engine but one that I can adapt to whatever engine I'm working on.


3 Answers 3


You will have to render the object twice at some point. You can get away with rendering just the faces facing the camera once and the faces facing away from the camera once, but it has its trade-offs.

The most simplest common solution is done by rendering the object twice in the same pass:

  • You use a vertex shader to invert the normals of the object and "blow it up" by the size of the outline and a fragment shader to render it in the outline colour
  • Over that outline render, you render the object normally. The z-order is usually automatically right, more or less, since the outline is made by the faces which are in the "back" of the object while the figure itself is made up of faces facing the camera.

This is simple enough to build and implement and avoids any render-to-texture tricks, but has a few noticeable drawbacks:

  • The outline size, if you don't scale it by the distance from the camera, will vary. Objects further away will have a smaller outline than those nearby. Of course, this might be what you actually want.
  • The "blow up" vertex shader doesn't work very well for complex objects like the skeleton in you example, easily introducing z-fighting artefacts to the render. Fixing it requires you to render the object in two passes, but saves you from reversing the normals.
  • The outline and the object might not work very well when other objects are occupying the same space and are in general a pain to get right when combined with reflection and refraction shaders.

The basic idea for such a shader looks like this (Cg, for Unity - the code is a slightly modified toon shader I found somewhere and didn't note the source, so it's more badly-written proof-of-concept than a ready-to-use shader):

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" }
#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;
            Cull Front
            ZWrite On
            ColorMask RGB
            Blend SrcAlpha OneMinusSrcAlpha
            SetTexture [_MainTex] { combine primary }
        Pass {
        Name "BASE"
        Tags {"LightMode" = "Always"}
#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 );
    FallBack "Diffuse"

The other common method renders the object twice as well, but avoids the vertex shader fully. On the other hand, it can't be easily done in a single pass, and needs render-to-texture: Render the object once with a "flat" outline-coloured fragment shader and use a (weighted) blur on this render in screen space, then render the object as usual on top of it.

There's also a third and possibly easiest to implement method, though it taxes the GPU a bit more and will make your artists want to murder you in your sleep unless you make it easy for them to generate: Have the objects have the outline as a separate mesh all the time, just fully transparent or moved somewhere where it's not seen (like deep underground) until you need it

  • \$\begingroup\$ Is the stencil buffer not a commonly used approach here? \$\endgroup\$ Aug 20, 2012 at 14:49
  • 1
    \$\begingroup\$ @edA-qamort-ora-y: This could work as well, but I never tried that approach, so can't comment on it. :) If you have a working algorithm in mind, feel free to add this method as another answer as well. \$\endgroup\$ Aug 20, 2012 at 15:04
  • \$\begingroup\$ I don't know much more about it myself, only that outlines are frequently mentioned in reference to stencil buffers. :) It seems like it might just be a more hardware based version of your first approach (two pass, first one larger). \$\endgroup\$ Aug 20, 2012 at 15:53
  • \$\begingroup\$ The stencil buffer approach can use up more bandwidth in updating and clearing the stencil buffers, and requires multiple passes. The approach Martin lists can be done in one pass in some limited cases, two at most, and requires minimal bandwidth overhead. \$\endgroup\$ Aug 20, 2012 at 19:44
  • \$\begingroup\$ This approach (blowing up the size along normals) doesn't work with objects like cubes (especially with ortho camera). Any solution to that? \$\endgroup\$
    – NPS
    May 1, 2013 at 14:22

In addition to Martin Sojkas answer, for static objects (or sprites) you can get away with something simpler.

You can save the same sprite but with the outline into your texture atlas or another texture entirely, which makes switching easy. This will also allow you to do custom outlines that are more visually appealing or just look different.

The other method is to save the sprite as a one color shape that is slightly larger and render that just before the sprite itself is rendered. This will give you the ability to easily change the color of the selection, and you might not need as much different color shapes as you would need outline sprites with method #1.

Both will increase your memory footprint though.


As pointed out in the comments to Martin Sojka's answer, a similar effect can also be achieved by utilizing the stencil or depth buffer, as detailed by Max McGuire on FlipCode:


It's basically drawing a wireframe version of the model you want outlined with increased line width (or in case this is not possible, as in D3D for instance, using camera facing quads for lines) while setting the stencil buffer to a constant value.

This aproach might be a little dated using today's OpenGL and in order to have the object outline applear blurred, rendering to texture will still be necessary.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .