1/x Over the next weeks, I want to post a few threads about the stylized rendering we’ve implemented for our game “Dungeons of #Hinterberg”. I’ll start with an overview, but I'll get pretty technical. Follow @MicrobirdGames, if you think the game looks rad! :)
#gamedev #indiedev
2/x #Hinterberg is #MadeWithUnity. We use a completely custom deferred solution, based on the built-in render pipeline. Our standard shader mimics Unity's, but was built from scratch, as were the lighting pass shaders. @catlikecoding's tutorials were an invaluable resource here.
3/x The main secret ingredient is that we store collections of various material properties in a StructuredBuffer on the GPU, and let deferred shaders write their material IDs (i.e. their index into the StructuredBuffer) into the gbuffer.
4/x Each Unity material has a material ID that links it to a "GPU material". There are 256 material IDs per scene, and Unity materials can share those. Among the things our GPU materials store are: outline color and width, brdf/toon-ramp type and fresnel/spec intensity scale.
5/x So the lighting pass, post FX, transparent VFX etc. all know much more about the pixels they're working with than they would with a regular gbuffer. Take our screentone post FX (turned up here): they can include/exclude specific materials or use different colors per material. Image
6/x We use ScriptableObjects to manage collections of these "GPU materials" and have custom material inspectors that let us edit them in place, alongside "regular" Unity material properties. Downside to all this: every shader needs to be adapted or written from scratch. Image
7/x Our gbuffer looks like this:
RT0: albedo & occlusion
RT1: smoothness, mat. ID, "section ID" & "section outline threshold" (more on that later).
RT2: normal, and metal in a 2-bit alpha channel (4 levels of "metalness")
RT3: emission, and diffuse brightness (after light pass)
8/x Side-note: I store material IDs in RT1.alpha, because the gbuffer's RGB channels seem to go through some sort of color transform (IIRC, it was some kind of dithering?), and high IDs lost precision. IDs were only off by +/- 1. Alpha didn't have that problem.
9/x Let's look at our lighting stage: One thing I really dislike about most cel shading solutions is how whenever two light sources overlap, the light is just ramped for each light and then additively blended. Usually everything just becomes a mess.
10/x Lights in comics usually work in levels (e.g. dark / medium / light), and overlapping lights should not produce intermediate steps, but change the light's shape on the surface. For colored lights, we want either gradients, or something like cross-hatching (future work :)).
11/x To achieve this, we split the lighting stage into two passes. First comes a "regular" deferred lighting pass, except we render diffuse light color + intensity into RGB and diffuse intensity into alpha. At this stage, we ignore albedo. ImageImage
12/x The second pass extracts a normalized light color from the first pass and combines it with a ramped version of the light intensity and albedo. It also stores the final light intensity in alpha, so post FX later in the pipeline can use that information. ImageImage
13/x During these passes, we use ramp textures stored with our "GPU materials". In the 1st pass, this lets us do things like half-lambert for materials like skin. In the 2nd pass (the toon ramp), we can go crazy & tint different materials based on how much light they get. Image
14/x A *lot* of our art style depends on taking liberties with that toon ramp texture, such as making shadows warmer or highlights cooler for specific materials. Compare the left screenshot (a neutral toon ramp) with the right! Image
15/x For the longest time, I thought deferred rendering's major downside was its lack of flexibility, but really the idea of storing material IDs in the gbuffer opens up so many possibilities, and I feel we've only started scratching the surface.
16/16 That was our first tech-art thread on #Hinterberg. I'll take a look at our outline rendering next time. If you enjoyed, follow us @MicrobirdGames and/or subscribe to our newsletter at dungeonsofhinterberg.com (only major updates, maybe 3 times a year or so).

• • •

Missing some Tweet in this thread? You can try to force a refresh

Keep Current with Philipp Seifried

Philipp Seifried Profile picture

Stay in touch and get notified when new unrolls are available from this author!

Read all threads

This Thread may be Removed Anytime!


Twitter may remove this content at anytime! Save it as PDF for later use!

Try unrolling a thread yourself!

how to unroll video
  1. Follow @ThreadReaderApp to mention us!

  2. From a Twitter thread mention us with a keyword "unroll"
@threadreaderapp unroll

Practice here first or read more on our help page!

Did Thread Reader help you today?

Support us! We are indie developers!

This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

Become a Premium Member ($3/month or $30/year) and get exclusive features!

Become Premium

Too expensive? Make a small donation by buying us coffee ($5) or help with server cost ($10)

Donate via Paypal Become our Patreon

Thank you for your support!

Follow Us on Twitter!