[HN Gopher] How to draw an outline in a video game
       ___________________________________________________________________
        
       How to draw an outline in a video game
        
       Author : alexanderameye
       Score  : 362 points
       Date   : 2025-01-04 09:14 UTC (13 hours ago)
        
 (HTM) web link (ameye.dev)
 (TXT) w3m dump (ameye.dev)
        
       | ashoeafoot wrote:
       | Duplicate the polys, vertex scale by normal of outline, invert
       | normals, draw in black? no shadow pass?
       | 
       | edit: turns out there is more .. thanks
        
       | hnlmorg wrote:
       | I remember the first time I saw this effect was on Wacky Races on
       | the Dreamcast.
       | 
       | I remember at the time there was a lot of PR around this being
       | the first game to introduce that effect and how the developers
       | basically invented it.
       | 
       | I can't comment on whether that was actually true or just PR BS,
       | but it was definitely the first time _I_ experienced it as a
       | gamer.
        
         | cubefox wrote:
         | Apparently it released simultaneously with the (more famous)
         | Jet Set Radio, also for Dreamcast, which had similar effects.
         | Quite the coincidence.
        
           | evanmoran wrote:
           | Not quite as much of a coincidence as you might think. Many
           | of these algorithms come from graphics researchers presenting
           | at SIGGRAPH (https://www.siggraph.org, the leading
           | conference).
           | 
           | So if so if Jet Set Radio was released June 2000, you can
           | look for related papers a couple years before to see if new
           | techniques were appearing. And, in fact, they were!
           | 
           | Disney paper (1998) on texture mapping for cell shading (the
           | color of a cartoon):
           | 
           | https://media.disneyanimation.com/uploads/production/publica.
           | ..
           | 
           | NYU paper (1998) applying outlines to 3d objects (the black
           | outline of a cartoon) :
           | 
           | https://mrl.cs.nyu.edu/publications/npr-
           | course1999/hertzmann...
        
             | cubefox wrote:
             | Interesting. There actually are various recent hobbyists
             | who have achieved these effects even on an N64 due to the
             | quite programmable hardware. But if the basic idea or
             | concrete algorithms were only invented in 1998, it makes
             | sense that contemporary games didn't use it until the
             | Dreamcast was out.
        
         | mewse wrote:
         | I suspect a lot of people 'invented' the effect at
         | approximately the same time. Honestly, the Dreamcast was the
         | first piece of hardware really capable of doing the effect to a
         | high level of quality in real-time.
         | 
         | I developed the cel shading effect for the Dreamcast game
         | 'Looney Tunes: Space Race' (developed by Infogrames Melbourne
         | House) literally during the first week we had access to a
         | Dreamcast development kit. Infogrames Sheffield (devs of Wacky
         | Racers) were shown an early version of our implementation, and
         | added the similar effect to their game. It looked great, but
         | went into their game pretty late in production, so the game
         | hadn't really been optimised for it the way that ours was.
         | 
         | And the folks behind Jet Grind Radio came up with the effect on
         | their own as well, and beat both of us to market. They were
         | using exactly the same algorithm, but were using it in a very
         | different way; they were fully embracing and leaning into the
         | uneven, wide and jagged outlines, where Sheffield and we were
         | fighting against them and trying to match a more uniform and
         | traditional art style.
         | 
         | And then only about a year later, somebody seemed to have
         | figured out how to make the edge-detection cel shading approach
         | work in real-time on Xbox, for the game "Dragons Lair 3D". I
         | had done a test implementation of that approach on the
         | Dreamcast, but it wasn't _nearly_ performant enough for us to
         | run it on multiple characters at once while playing a game too!
         | Not sure whether it was due to the Xbox being more powerful or
         | them just having a smarter algorithm than mine, but you can 't
         | argue with their results! If you're making a game that you want
         | to look like an actual hand-drawn cartoon, that is still
         | absolutely the best quality way to do it, IMHO.
         | 
         | Someday I'll find an excuse to try my hand at implementing one
         | of those again. Performance shouldn't be a problem at all any
         | more, I imagine!
        
           | hnlmorg wrote:
           | Thanks for sharing that. It was a great read.
        
       | __jonas wrote:
       | These are great notes!
       | 
       | When looking into the edge detection approach recently, I came
       | across this great method from the developer of Mars First
       | Logistics:
       | 
       | https://www.reddit.com/r/Unity3D/comments/taq2ou/improving_e...
        
       | alcover wrote:
       | That is excellent and the result can be very pleasing as this
       | render in the article : https://ameye.dev/notes/rendering-
       | outlines/edge-detection/co...
       | 
       | It looks like a frame from dutch comic book Franka !
        
         | alexanderameye wrote:
         | It's a recreation from a panel of Tintin The Black Island!
        
       | Lucckky wrote:
       | Yes
        
       | fleabitdev wrote:
       | One day, I'd love to dive into stylised 3D graphics as an R&D
       | project. There's been decent progress recently, but I think
       | there's a lot of low-hanging fruit left to pick.
       | 
       | Some open questions:
       | 
       | - How do you reduce the detail of a toon-rendered 3D model as the
       | camera zooms out? How do you seamlessly transition between its
       | more-stylised and less-stylised appearance?
       | 
       | - Hand-drawn 2D animations often have watercolour backgrounds.
       | Can we convincingly render 3D scenery as a watercolour painting?
       | How can we smoothly animate things like brush-strokes and paper
       | texture in screen space?
       | 
       | - How should a stylised 3D game portray smoke, flames, trees,
       | grass, mud, rainfall, fur, water...?
       | 
       | - Hand-drawn 2D animations (and some recent 3D animations) can be
       | physically incorrect: the artist may subtly reshape the "model"
       | to make it look better from the current camera angle. In a game
       | with a freely-moving camera, could we automate that?
       | 
       | - When dealing with a stylised 3D renderer, what would the ideal
       | "mesh editor" and "scenery editor" programs look like? Do those
       | assets need to have a physically-correct 3D surface and 3D
       | armature, or could they be defined in a more vague, abstract way?
       | 
       | - Would it be possible to render retro pixel art from a simple 3D
       | model? If so, could we use this to make a procedurally-generated
       | 2D game?
       | 
       | - Could we use stylisation to make a 3D game world feel more
       | physically correct? For example, when two meshes accidentally
       | intersect, could we make that intersection less obvious to the
       | viewer?
       | 
       | There are probably enough questions there to fill ten careers,
       | but I suppose that's a good thing!
        
         | rng_civ wrote:
         | > Hand-drawn 2D animations often have watercolour backgrounds.
         | Can we convincingly render 3D scenery as a watercolour
         | painting? How can we smoothly animate things like brush-strokes
         | and paper texture in screen space?
         | 
         | There are various techniques to do this. The most prominent one
         | IMO is from the folks at Blender [0] using geometry nodes. A
         | Kuwahara filter is also "good enough" for most people.
         | 
         | > When dealing with a stylised 3D renderer, what would the
         | ideal "mesh editor" and "scenery editor" programs look like? Do
         | those assets need to have a physically-correct 3D surface and
         | 3D armature, or could they be defined in a more vague, abstract
         | way?
         | 
         | Haven't used anything else but Blender + Rigify + shape keys +
         | some driver magic is more than sufficient for my needs.
         | Texturing in Blender is annoying but tolerable as a hobbyist.
         | For more NPR control, maybe DillonGoo Studio's fork would be
         | better [1]
         | 
         | > Would it be possible to render retro pixel art from a simple
         | 3D model? If so, could we use this to make a procedurally-
         | generated 2D game?
         | 
         | I've done it before by rending my animations/models at a low
         | resolution and calling it a day. Results are decent but takes
         | some trial and error. IIRC, some folks have put in more legwork
         | with fancy post-processing to eliminate things like pixel
         | flickering but can't find any links right now.
         | 
         | [0]: https://www.youtube.com/watch?v=ljjUoup2uTw
         | 
         | [1]: https://www.dillongoostudios.com/gooengine
        
           | kridsdale1 wrote:
           | The best results I've seen at procedurally generated "old
           | school" style pixel art sprites have come from highly LORA-ed
           | diffusion models. You can find some on Civit AI.[1]
           | 
           | So the future here may be a 3D mesh based game engine on a
           | system fast enough to do realtime stable-diffusion style
           | conversion of the frame buffer to strictly adhering (for pose
           | and consistency) "AI" pixel art generation.
           | 
           | [1] https://civitai.com/search/models?sortBy=models_v9&query=
           | Pix...
        
         | fidotron wrote:
         | The future of 3D graphics is going to be feeding generative NNs
         | with very simple template scenes, and using the NN to apply
         | almost all the lighting and styling.
         | 
         | It's kind of ridiculous that this occurs just as the dream of
         | raytracing hardware approaches viability.
        
           | CyberDildonics wrote:
           | _It 's kind of ridiculous that this occurs_
           | 
           | It hasn't 'occurred' at all. People extrapolated what they
           | saw in the 50s to cars the size of houses, personal flying
           | cars and robot helpers too.
        
             | fidotron wrote:
             | There is certainly some erroneous 'extrapolation' going on
             | here.
        
         | egypturnash wrote:
         | Pixel art from 3d models: see Dead Cells.
         | https://www.gamedeveloper.com/production/art-design-deep-div...
        
         | DonHopkins wrote:
         | Not 3D, but Stamen (a data visualization and cartography
         | studio) made a beautiful watercolor map renderer:
         | 
         | https://maps.stamen.com/watercolor/#14/52.3718/4.8958
        
         | Jare wrote:
         | > Would it be possible to render retro pixel art from a simple
         | 3D model?
         | 
         | Not exactly retro pixel art, or maybe it is since it's been 25
         | years (omfg) but in Commandos 2+ we had 3d models for the
         | characters, vehicles, etc which we rendered at runtime to a 2d
         | sprite which we then mixed with the rest of the pre-rendered 2d
         | sprites and backgrounds.
        
           | Reticularas wrote:
           | A more modern example would be Dead Cells
        
       | danwills wrote:
       | I loved the article that this links to about 'Jump Flood
       | Algorithm'!:
       | 
       | https://bgolus.medium.com/the-quest-for-very-wide-outlines-b...
       | 
       | So fascinating! Thanks for indirectly leading me to this! I love
       | thinking about all the various approaches available at the
       | pixel/texel/etc level!
       | 
       | It's also another case where it's a very clever way of generating
       | a type of SDF (Signed Distance Field) that is doing a lot of the
       | heavy-lifting. Such a killer result here as well! Any-width-
       | outline-you-like in linear time?!! Amazing when compared to the
       | cost of the brute-force ones at huge widths!
       | 
       | I wholeheartedly endorse SDFs, whether they are 'vector' ones,
       | function-based, like Inigo Quilez's amazing work, Or 'raster'
       | ones like in the article, texel-or-voxel-based. Houdini supports
       | raster-SDFs very well I think, has a solid, mature set of SDF-
       | tools worth checking out (there's a free version if you don't
       | have a lic)!
       | 
       | And of course there's all the many other places SDFs are used!!
       | So useful! Definitely worth raising-awareness of I reckon!
        
         | johndough wrote:
         | Note that the 'Jump Flood Algorithm' is O(N log N) where N is
         | the number of pixels. There is a better O(N) algorithm which
         | can be parallelized over the number of rows/columns of an
         | image:
         | 
         | https://news.ycombinator.com/item?id=36809404
         | 
         | Unfortunately, it requires random access writes (compute
         | shaders) if you want to run it on the GPU. But if CPU is fine,
         | here are a few implementations:
         | 
         | JavaScript: https://parmanoir.com/distance/
         | 
         | C: https://github.com/983/df
         | 
         | C++:
         | https://github.com/opencv/opencv/blob/4.x/modules/imgproc/sr...
         | 
         | Python:
         | https://github.com/pymatting/pymatting/blob/afd2dec073cb08b8...
        
         | alexanderameye wrote:
         | In my own projects I use JFA/SDF-based outlines the most
         | because of their quality as well as the possibility to render
         | distance-based effects like pulsating outlines.
         | 
         | This (https://x.com/alexanderameye/status/1663523972485357569)
         | 3D line painting tool also uses SDFs that I then write to a
         | tiny texture and sample at runtime.
         | 
         | SDFs are very powerful!
        
       | kace91 wrote:
       | I started my career working on VR apps and soon pivoted to webdev
       | due to the better market.
       | 
       | Articles like this one make me miss the field - working with 3d
       | graphics, collisions, shaders, etc. had a magical feeling that is
       | hard to find in other areas. You're practically building worlds
       | and recreating physics (plus, math comes up far more practically
       | and frequently than in any other programming field).
        
         | chefandy wrote:
         | Well, in web dev, people vacuuming up all of your code to feed
         | a NN creates something that eliminates the tedious annoying
         | parts and leaves the more interesting work. Conversely, even in
         | the technical end of art, people vacuuming up all of your
         | output to feed a NN eliminates the interesting, fulfilling
         | parts and for professional tasks, leaves the existing tedious
         | annoying parts, and even adds a few more, while devaluing the
         | entire skillset. And then everyone in tech pretends they know
         | more about your job than you do, and calls you a jerk for not
         | being happy that SV companies killed your job market by
         | offering C- results for F prices without the inconvenience of
         | any of the people that created the initial "data" getting
         | paychecks. So, considering the pros and cons, I think you made
         | the right choice.
        
         | superconduct123 wrote:
         | I know exactly what you mean
         | 
         | I went the other way from webdev to working in games and in my
         | experience it really is as fun/interesting as it sounds, the
         | satisfaction of the work is so much higher and the
         | ceiling/depth of the topic is very high.
         | 
         | Been doing it for 4 years so far and I've never hit a wall of
         | boredom like I did in webdev
         | 
         | Nothing beats coming in to work on monday, opening up the
         | engine editor, seeing the mini world you're working on being
         | rendered, and thinking about what cool feature you'll add next
        
           | jayd16 wrote:
           | Personally I think opening up steam and see your game
           | (shipping) can beat opening up the editor but I agree that
           | it's a very fulfilling industry.
        
       | zschuessler wrote:
       | What a phenomenal article and reading UX.
       | 
       | Explaining a difficult concept in terms anyone can understand.
       | Great diagrams and examples. And top marks on readability UX for
       | spacing and typography.
       | 
       | OP, what inspired you to create your current theme? Have you ever
       | considered creating an engineer-focused publishing platform?
        
       | Seb-C wrote:
       | For my game Astral Divide, I ended-up making a technique that is
       | not listed.
       | 
       | It's similar to the one described as "Blurred Buffer", except
       | that instead of doing a blur pass, I'm exploiting the borders
       | created by the antialiasing (I think via multi sampling, or maybe
       | texture filtering).
       | 
       | I draw the object in plain opaque white on a transparent black
       | background, and in the fragment shader I filter what does not
       | have a fully opaque or transparent alpha channel (according to
       | some hardcoded threshold). It gives decent enough results, it's
       | cheap performance-wise and is very simple to implement.
        
         | eknkc wrote:
         | Wouldn't that cause aliasing if you set those pixels to a solid
         | color? Or do you keep the alpha and set a color so the outline
         | is faint but not aliased?
        
           | Seb-C wrote:
           | I'm reusing the alpha channel indeed. And I reverse it for
           | the innermost side of the border.                   const
           | float MIN_ALPHA_THRESHOLD = 0.3;         const float
           | MAX_ALPHA_THRESHOLD = 0.7;              [...]              if
           | (fragmentColor.a < MIN_ALPHA_THRESHOLD) {             //
           | Outer edge of the border             float outputAlpha =
           | fragmentColor.a / MIN_ALPHA_THRESHOLD;
           | outputColor = vColor * outputAlpha;         } else if
           | (fragmentColor.a > MAX_ALPHA_THRESHOLD) {             //
           | Inner edge of the border             float outputAlpha = 1 -
           | ((fragmentColor.a - MAX_ALPHA_THRESHOLD) / (1 -
           | MAX_ALPHA_THRESHOLD));             outputColor = vColor *
           | outputAlpha;         } else {             // "Inside" of the
           | border             outputColor = vColor;         }
           | 
           | So if the border goes like transparent->opaque, I divide it
           | into segments using a threshold
           | (transparent->min_threshold->max_threshold->opaque) and
           | change the alpha values:
           | 
           | - Smoothen out the transparent->min_threshold segment, so
           | that it goes linearly from a=0 to a=1
           | 
           | - make opaque (a=1) the min_threshold->max_threshold segment
           | 
           | - Revert and smoothen out the max_threshold->opaque segment
           | so that it goes linearly from a=1 to a=0
        
         | alexanderameye wrote:
         | Interesting! I'll try it out. Thanks for sharing.
        
       | PaulHoule wrote:
       | Quite like the techniques used to make cel-style graphics using
       | the usual 3-d pipeline as seen in quite a few Nintendo games like
       | 
       | https://en.wikipedia.org/wiki/Pok%C3%A9mon_Sun_and_Moon
       | 
       | and also used to make other illustration-like styles such as
       | 
       | https://en.wikipedia.org/wiki/Valkyria_Chronicles
        
       | merksoftworks wrote:
       | Technical art is definitely my first love in software. I'm
       | excited for godot to add an easier compute shader pipeline for
       | post processing effects - their current compositor plugin set up
       | is a bit boiler plate intensive.
       | 
       | this repo is a great example of post processing in godot:
       | https://github.com/sphynx-owner/JFA_driven_motion_blur_demo
        
       | superconduct123 wrote:
       | The edge detection part reminds me a lot of the game Rollerdrome
       | 
       | https://store.steampowered.com/app/1294420/Rollerdrome/
       | 
       | I wonder if they used something like that
        
         | alexanderameye wrote:
         | They used edge detection with a custom input buffer, a bit like
         | this
         | 
         | https://linework.ameye.dev/section-map/
        
           | superconduct123 wrote:
           | Ah neat
           | 
           | Great article by the way OP
        
       | Killwinner wrote:
       | Igra
        
       | mezentius wrote:
       | A simple technique not listed here for drawing contour edges:
       | 
       | 1) Create an array storing all unique edges of the faces (each
       | edge being composed of a vertex pair V0, V1), as well as the two
       | normals of the faces joined by that edge (N0 and N1).
       | 
       | 2) For each edge, after transformation into view space: draw the
       | edge if sign(dot(V0, N0)) != sign(dot(V0, N1)).
        
       ___________________________________________________________________
       (page generated 2025-01-04 23:00 UTC)