[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)