6 ms·
How to draw an outline in a video game
- ashoeafoot 2y agoDuplicate the polys, vertex scale by normal of outline, invert normals, draw in black? no shadow pass? edit: turns out there is more .. thanks
- deleted 2y ago[deleted]
- hnlmorg 2y agoI 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 2y agoApparently it released simultaneously with the (more famous) Jet Set Radio, also for Dreamcast, which had similar effects. Quite the coincidence.
- evanmoran 2y agoNot 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 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/publication_asset/45/asset/2DTextureMapping_1998.pdf 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-intro3d.pdf https://mrl.cs.nyu.edu/publications/npr-course1999/hertzmann...
- cubefox 2y agoInteresting. 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.
- hnlmorg 2y agoThe N64 was also the most powerful console in its generation, so really it’s the next most powerful before the Dreamcast. It only looked like crap in its era because carts were expensive compared to CDs. Which is less of an issue now. Also the hardware antialiasing and overuse of fog didn’t help its case. Thankfully the former can be fixed either via hardware mods or emulation. I’d be still be interested to see if those demos you saw were full games or not. I’ve seen a lot of cool effects in games get abandoned because they didn’t scale well to a fully fleshed out game.
- cubefox 2y agoSee here https://youtube.com/watch?v=VqDAxcWnq3g https://youtube.com/watch?v=VqDAxcWnq3g and here https://youtube.com/watch?v=AQphLFA0DZY https://youtube.com/watch?v=AQphLFA0DZY
- mewse 2y agoI 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!
- __jonas 2y agoThese 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_edge_detection_in_my_game_mars_first https://www.reddit.com/r/Unity3D/comments/taq2ou/improving_e...
- alcover 2y agoThat is excellent and the result can be very pleasing as this render in the article : https://ameye.dev/notes/rendering-outlines/edge-detection/contribution-result.png-543w.webp https://ameye.dev/notes/rendering-outlines/edge-detection/co... It looks like a frame from dutch comic book Franka !
- coinfused 2y agoIt’s a recreation from a panel of Tintin The Black Island!
- Lucckky 2y ago[flagged]
- fleabitdev 2y agoOne 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 2y ago> 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 https://www.youtube.com/watch?v=ljjUoup2uTw [1]: https://www.dillongoostudios.com/gooengine https://www.dillongoostudios.com/gooengine
- deleted 2y ago[deleted]
- danwills 2y agoI loved the article that this links to about 'Jump Flood Algorithm'!: https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9 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 2y agoNote 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 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/ https://parmanoir.com/distance/ C: https://github.com/983/df https://github.com/983/df C++: https://github.com/opencv/opencv/blob/4.x/modules/imgproc/src/distransform.cpp https://github.com/opencv/opencv/blob/4.x/modules/imgproc/sr... Python: https://github.com/pymatting/pymatting/blob/afd2dec073cb08b8119300feec18c179a9d436f3/pymatting/util/distance.py#L61 https://github.com/pymatting/pymatting/blob/afd2dec073cb08b8...
- pshc 2y agoDang, I implemented SDFs in 2023 around that time with jump flooding. Wish I had seen this version. Thanks for pointing it out!
- coinfused 2y ago
- torben-friis 2y agoI 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 2y agoWell, 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 2y agoI 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 2y agoPersonally 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.
- 2y ago
- zschuessler 2y agoWhat 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 2y agoFor 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 2y agoWouldn’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 2y agoI'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
- coinfused 2y agoInteresting! I’ll try it out. Thanks for sharing.
- 2y ago
- PaulHoule 2y agoQuite 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 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 https://en.wikipedia.org/wiki/Valkyria_Chronicles
- merksoftworks 2y agoTechnical 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 https://github.com/sphynx-owner/JFA_driven_motion_blur_demo
- superconduct123 2y agoThe edge detection part reminds me a lot of the game Rollerdrome https://store.steampowered.com/app/1294420/Rollerdrome/ https://store.steampowered.com/app/1294420/Rollerdrome/ I wonder if they used something like that
- coinfused 2y agoThey used edge detection with a custom input buffer, a bit like this https://linework.ameye.dev/section-map/ https://linework.ameye.dev/section-map/
- superconduct123 2y agoAh neat Great article by the way OP
- Killwinner 2y ago[flagged]
- mezentius 2y agoA 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)).
- webwielder2 2y agoSide note, whatever happened with the great Unity pricing debacle? Did developers end up moving en masse to Unreal and Godot? Or were Unity’s walkbacks and contrition sufficient to keep it a going concern?
- Auracle 2y agoEh, some people moved, but probably not many people that were knee deep in a project. I'm sticking with Unity for now myself - for all of its annoying eccentricities and bad developer relations it fits in pretty snugly between the power and complexity of Unreal and Godot.
- coinfused 2y agoThey reversed it, I don’t think a huge amount of people changed over but definitely substantial! Godot has been growing quickly.
- smallstepforman 2y agoI’d render the model to a buffer using a single colour (no shading, lighting or texturing), then render the buffer with edge detection. This gives an outline with one additional render pass. Suprised this isn’t obvious.
- LelouBil 2y agoI NEED to get into shader programming and 3D rendering. Articles like this are awesome, I wish I could actually write a shader.
- teaearlgraycold 2y agoI learned using Shadron 8 or so years ago.
- LarsDu88 2y agoFunny, I was just playing around with some of these techniques yesterday for my game https://roguestargun.com https://roguestargun.com now that I got it running reasonably comfortably at 72 fps on the Oculus Quest 2. Mostly due to laziness, as a cell shaded look requires less retexturing for my game than simply creating proper PBR materials. The inverted hull method + cell shaded look I initially used however actually really does have quite a performance hit.
- finlecture 2y agoGreat breakdown of outline rendering techniques! The detailed explanations and code snippets are super helpful for understanding Unity's shader possibilities. Thanks for sharing!
- cannibalXxx 2y ago[dead]