A slightly open white door is floating above a picnic blanket that has food, a laptop, and a basket on it. The background is purple.

𝑫𝑶𝑶𝑹

To learn more about URP features like postprocessing and scriptable Renderer Features, I made this little scene inspired by OMORI.

Tech

HLSL, Shader Graph, Unity

View online

door.czw.sh

For one of the homeworks of our procedural graphics class, CIS 5660, we were to create a stylized scene mimicking some concept art that we pick out. In particular, we focused on using Unity Renderer Features to achieve non-photorealistic rendering (NPR).

Concept art

The natural pick for me was anything from OMORI. Any opportunity I get to work with this game, I take it. Here are the two main pieces that guided me, both by OMOCAT:

OMORI characters looking at photobook on a picnic blanket in a forest.

Concept art 1. Source.

Mari and Basil wearing animal ears on a picnic blanket. Lots of other characters are in the
background.

Concept art 2. Source.

I’ve always adored OMORI’s unique art style, and in this homework set out to recreate the following aspects:

  • Scribbly, colored pencil textures
  • Wobbly and multicolored outlines that animated over time
  • Purple OTHERWORLD sky and picnic blanket motif

Shaders

Homework is still homework and there were two shaders I was required to create.

Toon shading

Screenshot of Unity Shader Graph for my toon shader

Shader Graph for my toon shader.

What exemplifies NPR more than good ol’ toon shading? I will spare you a detailed walkthrough because thousands of people online have already explained it better than I ever will, but this was my general process:

  1. Calculate the Lambertian coefficient by taking the dot product between the surface normal and light direction.
  2. Clamp it between 0 and 1 inclusive, because values less than zero should be in shadow.
  3. I use three colors of decreasing brightness representing a highlight, a midtone, and shadow. The coefficient is compared against two thresholds that then decides which of the three colors is chosen.

Animated and textured surface

Another requirement we had to meet was to create an additional shader that animated over time. But, this was already a given for me; if you look at any in-game footage, the textures are always “shifting” around. It’s a bit hard to explain, so here’s a YouTube video:

To create the drawn look as seen in-game, I drew two textures using Procreate’s default pencil brushes. The lighter one emphasizes the scribbles more by being more sparse.

White square with lighter black scribbles all over it.White square with heavier black scribbles all over it.

The textures being black and white make them very versatile. The values can be interpreted literally and used to multiply by another color, or interpreted as booleans: black (0) for false, and true for everything else.

I can now “animate” these textures by rotating them by a random amount. To emulate the game’s behavior of animating across a fixed set of sprites, I sample the UV orientation from a triangle wave that I’ve increased the amplitude of, and then floored so I could get staggered values that repeated infinitely.

Staircase-like graph from the Graphtoy window

Staggered triangle wave. I used Graphtoy to figure out values before programming it.

Hopefully the image conveys my implementation visually. Each of those steps are a value I plug into my UV rotation. That results in this final output:

The green-yellow-red color scheme is for me testing out the highlight, midtone, and shadow colors.

Outlines

To perform edge detection we turn to Unity’s Scriptable Renderer Features. We first render geometry normals to a RenderTexture, allowing us to sample from it later using screen position UVs. We also enable the depth texture which is just a built-in checkbox, much easier.

For outlines I turned to the Roberts cross operator. The following two links were very useful in helping me understand what’s going on. The video showcases a Shader Graph solution while the article has GLSL code.

This gives us basic outlines.

A sphere floating above a plane with black outlines.

However, outlines in OMORI seem to be made up of three overlapping parts: a green, pink, and purple part. Zooming in on the second concept art you can see it very clearly.

Close-up of Mari's elbow, showcasing the multicolored outlines.

We need three outlines, so we do exactly that. Each outline has a different color and has its UVs slightly offset to give us the chromatic aberration look. I also use the Random Range node to vary the outline thickness based on screen position. Finally, I replace the solid outline with the scribble texture and animate it using the same method as the surface shader.

The same sphere and plane but now rendered using OMORI's outlines.

Assembling a scene

I found all the models on Sketchfab. They are credited in the README here. All of the items are in some way thematic or relevant to the game, including the floating door and laptop.

Speaking of laptop, I created a custom shader that specifically draws a texture to the screen geometry.

Unity scene view of the laptop screen with the shader applied.

To prevent the door from self-shadowing and ruining the 𝓪𝓮𝓼𝓽𝓱𝓮𝓽𝓲𝓬, I added a UseShadow shader parameter that completely turns off shadow attenuation in the main toon shader.

Scrolling background

I couldn’t figure out how to achieve this effect using Unity UI or another render pass, so I resorted to this hack.

As the image above betrays, the purple scrolling background you see is actually a giant plane that is perpendicular to the camera view vector. I made it a child of the camera so any transforms are applied to the plane as well.

Unity scene view of the background plane being projected along our camera's view vector.

I scroll the UVs of an image in a shader that I specifically wrote for the background. The material is then applied to the plane.

Interactivity

Press spacebar to toggle between two different views.

Alternative scene view when spacebar is pressed. Everything becomes darker, redder, and scarier.

A number of changes occur:

  • The background translates faster in the opposite way and the image is replaced.
  • Similarly, the turntable camera rotates faster and in the opposite direction.
  • The subtle white vignette around the screen border gets replaced. Instead, I use the scribble texture from before as an interpolation value between a pure red color and the scene color. This gives it the “veiny” look. It is also animated.
  • I change the door’s material to a modified version of the surface shader: the animation speed is cranked up.
  • The laptop screen displays… something else. IYKYK.

Web build

The version you see at door.czw.sh (live demo above) has some web-specific goodies: I toggle between a red and white background color for the body, and I also update the favicon.

I should probably add a hint to press the spacebar, but I’ll let future me take care of that. For now, thanks for reading.