Hi-Fi RUSH Toon Rendering Guide
Hi-Fi RUSH Toon Rendering Guide
*Please note that the original powerpoint contained movie files and gif
animations which became converted into still images in the pdf.
Image sharpness was also lost during the powerpoint to pdf conversion.
Kosuke Tanaka
Programmer @ Tango Gameworks since 2011.
・VFX Programmer for “The Evil Within”.
・Graphics Programmer for “The Evil Within 2”.
・Lead Graphics Programmer for “Hi-Fi RUSH”.
We’d like to start off the talk with brief speaker introductions.
Final Words
I’ll cover the core toon rendering topics in the blue frame and Komada-san
will present our toon face shadow implementation in the green frame.
Agenda
Game Introduction
Deferred Toon Rendering
Comic Shader
Toon Light
Shadows
Static Shadow Map
Global Illumination
Volumetric Fog
Toon Face Shadow
Final Words
Okay, I’m going to start off with a brief introduction of our game.
Pop-Up Character Introductions
Chai
Kale Rock Star
CEO
I’m sure not all of you have played our game, so I’d like to introduce our
presentation’s pop-up characters first.
The character on the left, Kale, is one of the enemy characters in the game.
Since he’s an evil guy, he’ll mostly be making difficult demands for the
graphics team.
The character in the middle, Chai, is the main protagonist of the game. Since
he’s our friendly hero, he’ll mostly be making enthusiastic comments.
And finally, the robot on the right is John. Despite not having a catchy food
name, John made a strong creative push for our toon visuals, had a
conveniently available texture, and is Hi-Fi RUSH’s creative director, so I’ve
included his comments.
Hi-Fi RUSH Features (Toon Rendering)
Let’s render
everything in a toon
style.
Kale
CEO
It’s the main topic of our talk today, but Hi-Fi RUSH is a game utilizing toon
rendering.
A lot games use toon rendering for characters, but not the environment. In
our game everything is rendered in a toon style.
A big challenge for the graphics team was how to utilize Unreal Engine 4’s
modern graphics pipeline and make it work for a 2D cartoon look.
Hi-Fi RUSH Features (60FPS)
60FPS is a must. We’re a
rhythm action game.
Kale
CEO
With goals of great graphics, the keywords that our art director defined for
the team were sharp, clean, and colorful.
Rock Solid 60 FPS & High Resolution
Xbox Series X 4K 60FPS
Xbox Series S 1440p 60FPS
Let’s strive for native resolution but support super resolution plugins for
lower end specs.
Everyone, I want our renderer and our assets need to be well optimized!
Kale
CEO
I’ll go over the technical make up of our attempt at great graphics in more
detail but notice that two of our key words were sharp and clean.
Image quality was very important to us, and in addition to high frame rate,
one of the key technical features of our game is its high resolution.
We aimed for native resolution on our console targets for sharp, clean,
“artifact-free” image quality. We’re aware of super resolution tech, and we
support super resolution plugins for PC, and they’re great. Because our
resolution is high to begin with, they allowed us to push the limits of our PC
low end specs even further.
Balancing Visual Quality & Performance
Digital Foundry Review
・Adventurous Toon Rendering
・Solid 60FPS
・Xbox Series S 1440p
・Xbox Series X 4K
https://www.youtube.com/watch?v=8qppoWhanwk
Chai
Rock Star
I’ll now discuss our toon rendering starting off with an explanation of the core
tech; our deferred toon renderer.
Deferred Rendering
Hi-Fi RUSH uses Unreal Engine 4, which out of the box is a photorealistic
deferred renderer and not a toon renderer.
The lighting is forced, but this is what Hi-Fi RUSH looks like when toon
rendering features are disabled from the game.
The main point of the image is that, if we use the default UE4 lighting, there is
gradation everywhere, and the image ends up looking too much like it’s
rendered using a 3D engine.
Deferred Toon Rendering
This is the same scene rendered with toon rendering features re-enabled. This
is what we were going for as a toon look. Shadow gradation is replaced with
sharper shadows and our comic shader has been applied to the various 3D
render passes adding a 2D touch to the image. This is what sharp, clean and
colorful looks like.
Deferred Toon Renderer 3D Features
・Toon Light ・Volumetric Fog
・Dynamic Shadow Map ・Camera Motion Blur
・Static Shadow Map ・Toon Motion Blur
・Capsule Shadow ・Tone Map
・AO Volume Shadow ・Bloom
・Diffuse Global Illumination ・Lens Flare
・SSAO ・Temporal AA
・SSR
・Post Process Outline
・Post Process Toon Shading
Lighting is complete
at this point.
Kale
CEO
SSR
SSAO
Fog、
Deferred Toon Rendering Transparency
Similar to deferred
lights except the
geometry is a box and
the shader is a toon
post process.
Depth Buffer
Gbuffer A, B, C
In the slide video, when I move the billboard mesh inside the smaller toon
post process volume, notice that its toon rendering changes.
At its core, our toon post process volumes are using standard deferred
rendering calculations. We use the scene depth to recreate the world position
and use GBuffer information to apply the volume’s toon post processing on
not the volume box geometry, but the underlying scene geometry.
Lighting With Toon Post Process Volumes
HbkPostProcessVolume_St05AIVC
HbkPostProcessVolume_St05AreaA_Room
Our environment artists place many toon process box volumes throughout
our levels. By placing these volumes, our artists adjust toon shading locally
per volume within each area of the level.
Deferred Toon Rendering Timeline
Depth Buffer
Global Post Process
Shadow Depth Illumination Outlines
Dynamic Screen
Space Shadows Static Shadow
Map
SSAO, Capsule
Shadows Decal Light
SSR
The above is a Pix for Windows capture of the timeline for our deferred toon
rendering pipeline.
I’m showing you the render passes that are executed in an actual scene in
preparation for the toon post process pass, the render pass of the box
volumes in the previous slides.
Deferred Toon Rendering Input Buffer
Quite a few render targets end up getting generated for the toon post process
pass. My earlier test map was very simple looking, but there are quite a bit
more lighting layers in an actual scene. Toon shaded environment tend to
look simplified, but there are a lot of layers to our environmental lighting.
The Toon Post Process Pass
Render
Before Toon Post
Process the Toon
Post
Process
Pass
Input
Buffers
SSR
SSAO
The toon post process has available to it all these render target. What do we
do with them?
Because we have all lighting results as input textures, as was one of our initial
goals, we can apply comic stylizations to each lighting layer with individual
parameters.
What We Do With Our Numerous Input Render Targets (Shadow Overlap)
Because we apply all shadow types to the scene inside the toon post process
pass, we can also easily control the visual look when different shadow types
overlap. For example, when the player shadow is drawn inside our cascaded
shadow maps, we can detect this overlap and adjust the player shadow
darkness, so it doesn’t appear too dark or light. Stylized shadows inside
shadows is a very cool toon thing and we wanted to make it look good.
Making Our Toon World Look Good With A 3D Engine
Chai
Rock Star
That was an overview of our deferred toon rendering. Next, I’d like to
highlight unique aspects of our toon lighting during deferred toon rendering.
I’ll cover shadow color 3D textures and ambient cubemaps. The major
requirement for our toon lighting was artists need to be able to add color and
adjust brightness, but it can’t look too 3D. To make the scene not look
artificial for toon, artists needed to be able to paint the colors themselves.
Environmental Shadow Color
Environment artists need a way to give color to each location. Our art
director’s key words were sharp, clean and colorful, so it was important that
artists be able to control the environment’s shadows with the colors they
wanted. In the slide image, the shadow color is a uniform black color.
Shadow Color Volume Applied
This is what the scene looks like with shadow color volumes applied.
The black shadows are now properly colored with artist authored shadow
colors.
I’ll explain in more detail in the following slides, but shadow color volumes
work by mapping a 3D texture to a 3D world position.
Shadow Color Volume Rendering
Calc world position from scene depth.
Here in the slide video, I’m adding a new local shadow color volume to the
scene with a whiter/greyer shadow color 3d texture.
The rendering of the shadow color volumes is like our toon post process
volume rendering.
The shadows are not applied to the volume box geometry but are projected
onto the geometry enclosed by the box.
3D Texture Mapped to World Positions
If we look carefully, the shadow color volume colors gradually become darker
with increasing height. This positional color change happens because the
enclosed surface world position is converted into the shadow color volume’s
normalized local 3D coordinates and this coordinate is used to the sample the
3d texture. The gradually changing colors inside the 3d textures are hand
adjusted by artists to give the desired shadow color nuance our art director
wants for the scene.
Shadow Color Volume Applied
Enable
Ambient
Cubemap
We use deferred
rendering of box
volumes a lot in
Hi-Fi RUSH.
Chai
Rock Star
Like with our shadow color volumes, we apply ambient cubemaps to the
underlying geometry using a box volume.
As mentioned earlier, our interior scenes are initially unlit, and to this state
we add lighting layers, one of which is ambient cubemaps.
Ambient Cubemap Volume
In the slide image, the ambient cubemap volume is being toggled on and off.
Notice that in the scene, the underside of the surface is lit brighter than the
top and the sides, representing a difference in directional ambient brightness.
This directional brightness comes from the cubemap texture, which our
environment artists create by hand.
Agenda
Game Introduction
Deferred Toon Rendering
Comic Shader
Toon Light
Shadows
Static Shadow Map
Global Illumination
Toon Face Shadow
Final Words
In the next section, I’d like to talk about our comic shaders.
Comic Shader
Lit
・Halftone Dot
Shadow
・Hatching Lines
It’s a recognizable trait of our toon look, but we implemented a comic shader
for rendering halftone dots and hatching lines.
Halftone Dots
SSR
Halftone dots are the round dots that we apply to the lit portions of our
lighting.
Hatching Lines
SSAO
Hatching lines are the rotated lines we apply to the dark shadowed areas of
lighting.
Why Apply A Comic Shader
I want everything in the
world to look toon.
Characters, environment,
everything.
Here’s what a scene from our game looks like with the comic shader turned
off.
SSAO, GI, Bloom make the world look richer, but since we’re just applying 3D
engine functionality, the final image looks 3D.
Also, the performance-oriented, low-sample count nature of some of our
rendering passes become especially noticeable on our clean toon textures.
Comic Shader On
Comic Shader On
Here’s what the same scene looks like with the comic shader enabled.
The scene is stylized, but at the same time, gradation is also removed, making
everything look less 3D.
The dirty look of GI and SSAO have been made sharper with lines and dots,
and the image looks much cleaner.
Comic Shader On/Off
(0,0)
float3 CalcHalftone(float2 UV, float3 HalftoneColor, float3 BGColor, float Radius)
{
float2 UV2 = 2.0*UV.xy - 1.0;
float Dist = length(UV2);
if(Dist < Radius) { return HalftoneColor; } // Radius is 0.5.Halftone Color is white.
return BGColor; // BGColor is black.
}
We just do basic stuff in terms of SDF. For clarity, I’ll go over an example
halftone shader.
The first step is to calculate the distance from the center of the screen. If this
distance is below the halftone radius, we draw a white color, if the distance is
above the halftone radius, we draw a black color.
Implementing Halftone (Aspect Ratio)
float3 CalcHalftone(float2 UV, float3 HalftoneColor, float3 BGColor, float Radius, float Freq)
{
float2 UV2 = 2.0*frac(UV.xy*Freq)-1.0;
float Dist = length(UV2);
if(Dist < Radius) { return HalftoneColor; } // Radius is 0.5.Halftone Color is white.
return BGColor; // BGColor is black.
}
We don’t want large circular dots, but want multiple smaller dots in a grid
across the screen.
Using the frac instruction, we scale and wrap the screenspace UVs.
Implement Halftone (45 Degree Rotation)
Art wanted the halftones rotated 45 degrees. For this we just rotate the grid
UV.
Implementing Halftone (Aliasing)
Because we are using a simple 0/1 step function for the distance function
threshold, aliasing can occur.
Implementing Halftone (Anti-Aliasing)
Step Smoothstep
By using smoothstep instead of step and alpha blending the boundary region
of the halftone dots, we can implement anti-aliasing.
Implementing Halftone (Smoothstep Width)
The area inside the yellow frame is the width of the smoothstep function
where AA is applied.
Since we want to apply anti-aliasing only on the threshold border pixel, we
use the ddx, ddy instruction to calculate the per-pixel rate of change of the
distance function and use this value as the basis for our width.
Bloom Halftone AA On/Off In-Game (Zoomed)
No AA With AA
*Image sharpness was lost during the powerpoint to pdf conversion, so it may
be hard to see a difference in the pdf file here.
Implementing Hatching Lines
float3 CalcLines(float2 UV, float3 LinesColor, float3 BGColor, float Radius, float Freq)
{
float2 UV2 = 2.0*frac(UV.xy*Freq)-1.0;
UV2.y = 0.0;
float Dist = length(UV2);
return lerp(LinesColor, BGColor, StepWithAA(Radius, Dist));
}
So that was an explanation of how halftones are rendered using screen space
UVs. Hatching lines are the same shader code, just with UV.y fixed to 0.
SDF UV Grid Generation (Screen Space)
Screen Space UVs Screen Space Halftone Using Screen Space UVs
For effects such as bloom and volumetric fog, we use screen UV coordinates
in actual scenes.
We don’t do anything complicated, and the example shader is close to how
we render actual in-game screen space halftones.
SDF UV Grid Generation (World Space)
Screen space UVs work well for stuff without depth, such as bloom and
volumetric fog, but on actual polygonal surfaces, we want our halftones and
lines to look as though they are printed on the surface and not attached to
the front of the camera. For these cases, we use world space UVs.
SDF UV Grid Generation (World Space)
To calculate world space UVs, we use the GBuffer world space normal to
calculate the dominant axis of the surface and just project onto this axis’s
plane.
For example, if the z-value is the largest direction of the world normal, we
want to project onto the xy plane, and so we simply use a scaled
WorldPosition.xy as our UV coordinates.
SDF UV Grid Generation (World Space)
*Start animation.
In the next section, I’d like to talk about our toon lights.
Key Light (Lit/Shade)
Hi-Fi RUSH’s toon shading uses a simple 2-tone lit/shade toon shading model.
The toon shading’s lit/shade calculation is not performed in a deferred pass
but is calculated by a global key light that is forward rendered in UE4’s base
pass.
Why Forward Render Key Lights?
Kale Chai
Rock Star
CEO
We eventually needed to support 2 key lights and it may have been more
performant to associate the key light with a toon post process volume and
render it in a later deferred pass. Our lighting’s mix of deferred and forward is
probably something we can improve on.
Placeable Toon Lights
Light actors such as forward lights and decal lights can be placed to perform lighting.
Besides key lights, artists can use familiar light actors such as point lights and
spotlights to perform local environmental lighting.
Toon Light (Forward Light)
Kale
CEO
Forward Light
For placeable lights, again we supported forward lights for the same reason as
key lights. A lot of smaller teams probably struggle with this, but how much
UE4 engine modifications is safe is something we struggled with especially in
the earlier phases of development.
We initially experimented with forward lights, but decal lights, which run in an
original deferred pass, were easier to optimize and eventually more useful for
our artists.
My toon light explanation will focus on decal lights since they’re more
interesting.
3D Light (With Gradation)
The gradation makes the
lighting look 3D. I want it
to look more 2D.
I like it.
Add a decal light with cutout light attenuation and the scene is lit by a 3D light
without making the scene look 3D.
Decal lights support arbitrary cutout textures. Our art director prepared 7
cutout texture patterns to give variation to the decal lights used throughout
our maps.
Decal Light Rendered Simply As Lights
Decal lights are Hi-Fi’s RUSH deferred lights, and so when a decal light is
placed in the world, they are deferred rendered with a sphere or cone
geometry just like regular deferred lights.
Decal Lights Rendered Using Decal Volumes
How to use a decal light
decal volume
Decal light’s namesake and what makes them special is that they can be
assigned to decal light decal volumes to render as a decal.
Environment artists can place lights as light sources in a traditional way, but
they can limit the projection volume of the light separately using decal
volumes.
Decal Light Decal Volume Optimization
VS
Rendering the spot light cone. Rendering a thin box decal volume.
For example, we can add a separate decal light with its own projection
texture specifically to light the wall.
• Play animation.
The scene displays a star cutout projection for the ground, but a teardrop
cutout projection for the wall.
Preventing Light Leaks
In the Evil Within 2, our PBR lights could use clip volumes to prevent
spotlights from light leaking to the next room.
Decal light decal volumes also function as clip volumes and can prevent light
leaking.
* Play animation.
What About Characters?
Let’s try lighting our game’s hero Chai with a decal light.
60FPS, native
resolution with
proper
shadowing is
what I want.
Kale
CEO
Shadow On/Off
・Shadow-only lights are modified UE4 PBR lights customized to only cast shadows.
・Toon lights and shadow-only light are separate light actors.
・Shadow-only lights usage is limited to areas where the quality/cost tradeoff made sense.
For dynamic shadows other than cascaded shadow maps, artists are required
to use shadow-only lights.
Shadow-only lights are customized UE4 PBR lights specializing in only casting
dynamic shadows.
Because we were going for a 2D toon look, having different lights for lighting
and shadowing was not a problem.
Player Shadow-Only Light
Giant bosses are allowed their own shadow-only light because their shadows
can look very dramatic.
The character shadow-only light in the slide doesn’t cast shadows from the
player or the environment and only casts shadows from the giant boss.
Shadow/Light Transition Stylizations
Player shadows and some of our static shadow maps are drawn with
stylizations for the shadow-to-light transition.
The hatching lines become thinner the further away the shadow becomes
from the shadow casting object representing a 2d toon style shadow to light
transition.
Capsule Shadows
Non-player characters
use capsule shadows.
Non-player characters use the more cost-effective capsule shadows for their
shadowing.
Capsule shadows are a standard UE4 functionality. Artists prepare capsule
shapes approximating the character mesh and these shapes are used to
calculate per-pixel visibility towards a global light direction to approximate
soft shadows.
It wouldn’t work for a photorealistic game, but we can get away with the
blobby look of capsule shadows because we are toon.
Capsule Shadows Cast Shadows Anywhere
CapsuleShadow On/Off
Because capsule shadows don’t require shadow casting local lights, we get
cost-effective character shadows outdoors, indoors, regardless of lighting
conditions.
AO Volume Decal Shadows
・Flying robots and our
partner cat are too far
away from the ground for
capsule shadows.
Capsule shadows become too large and dispersed for small characters that
are floating in the air far away from the ground.
For these characters, we implemented a simple AO volume decal system. CPU
raycasts are used to find the proper decal placement world position.
Environment Shadow-Only Light
I’ve mentioned we have a static shadow map system. I’d like to explain this
system in more depth in the next section.
Static Shadow Map
Static shadow maps are an important element of Hi-Fi RUSH’s shadow system.
In the slide image, I’m toggling on and off the static shadow map display.
The environment team had strict limitations on dynamic shadow map usage
and interior environmental shadows will typically disappear completely
without static shadow maps.
Static Shadow Map Actor
Scene Capture Camera
Again we use
deferred box
Screen Space Shadow Map volumes for
Render Volume rendering.
Chai
Rock Star
When the static shadow map actor is selected, the captured depth map is
displayed in the lower right corner of the screen in the editor.
Maximizing Depth Map Coverage
Ortho Width Adjusted
Artists use the depth map preview in the lower right portion of the screen to
manually maximize depth map coverage of shadow casters to improve
shadow map quality.
Calculating The Screen Space Shadow Map
Compare
Light Space Depth Pos Distance
Standard comparisons between the pixel’s light space depth position and the
depth map’s light space depth position are performed to render a screen
space shadow map inside the static shadow map decal volume.
Screen Space Shadow Map AA
Without AA, our screen space shadow maps look very jaggy. We use 4x4 PCF
for antialiasing our shadow maps.
Screen Space Shadow Map AA
Each static shadow map camera renders its own depth map and its associated
render volume owns that static shadow map’s depth map texture data.
A Different Static Shadow Map Camera
Even the small handrail in the slide image has its own static shadow map.
Static Shadow Map Streaming
・Focus on efficiently distributing shadow map texture streaming across multiple frames.
・Multiple static smaller shadow maps instead of a single large static shadow map.
・Static shadow map streaming levels with a per-frame memory limit.
Our artists use many static shadow map cameras not just for artistic reasons,
but for texture streaming efficiency as well.
One of the first concerns we had when we considered using static shadow
maps was memory usage and streaming hitches.
Our static shadow maps are placed in their own streaming level and within
the streaming level, we distribute texture streaming across multiple frames by
placing a per-frame streaming memory limit.
Agenda
Game Introduction
Deferred Toon Rendering
Comic Shader
Toon Light
Shadows
Static Shadow Map
Global Illumination
Toon Face Shadow
Final Words
The final topic I’d like to talk about is how we handle global illumination.
Toon with Global Illumination
GI is noticeable around
the center data sphere.
Engine
customization
permission given.
Let’s improve bake
iteration speed and
useability.
Kale
CEO
Light Probe Debug View
We had noticed that Lightmass volumetric lightmaps could be used for the
environment. Lightmass volumetric lightmap light probes bake faster and
don’t require lightmap UVs.
We decided that we would customize the volumetric lightmap functionality
with a focus on further improving bake iteration speed and useability.
Local Volume GI Lighting
A big factor that allowed further customization is the fact that our game has a
toon art direction, and we can be artistically selective in which parts of the
map we apply global illumination.
In the slide image, only a small area around the window has global
illumination, and that can be okay for our look.
GI Baking Limited To Necessary Areas
Here’s the same level with the GI light probe debug display enabled.
The environment artist has decided to bake probes around strong emissives,
but in areas without a strong light source, no probes are baked.
World Volume Lighting Volume
What’s the key point in our volumetric lightmap Baking & rendering done per World Volume
customization. Lighting Volume.
UE4 has a functionality called Lightmass importance volume that limit light
probe baking to the area within the volume.
We customized this functionality to implement our own global illumination
lighting volume actor.
In vanilla UE4, light probe data is stored per-level. We customized UE4 so that,
in our game, light probe data can be stored per actor.
Optimizing Volumetric Lightmap Data
I’ll should mention that we did optimize the UE4 volumetric lightmap data to
save memory. Because we only need directionless ambient global illumination,
we cut down the light probe spherical harmonics 3d textures from 8 to 1. It’s
a big memory saving.
World Volume Lighting Baking Workflow
Since I emphasized bake iteration speed and useability as keypoints, I’ll give a
quick overview of what our GI bake workflow looks like.
We start by enclosing the area we want to add global illumination to with a GI
lighting volume.
World Volume Lighting Baking Workflow
3) Wait 1, 2 minutes.
After the volume is placed, artists select from the menu “Build Selected
Volumes.”
A customized Lightmass GI baking code is executed, and the GI bake is limited
to the area enclosed by the lighting volume.
Bake times are dependent on Lightmass settings and PC specs, but as a
general idea, for a standard dev PC at our company, a GI bake should finish in
a minute or two.
Since the bake is isolated to the selected actor, artists can iterate baking on a
specific area quickly.
World Volume Lighting Volume Parameters
Cell Size 30 (Used Memory: 0.25MB) Cell Size 100 (Used Memory: 0.04MB)
Light probe cell size is adjusted to balance memory usage and bake quality.
The GI lighting volume contains parameters for both baking and rendering.
An often-used parameter for baking is the detail cell size parameter, which
controls the distribution granularity of the generated light probes.
Our environment artists use this parameter to balance bake quality and
memory usage.
Rendering A World Volume Lighting Volume
Once again,
we use
deferred box
volumes for
rendering.
Chai
Rock Star
HbkWorldVolumeLightingVolum
e_St07ParkMain_11 On/Off
Finally, the GI lighting volume not only defines the GI bake extent, but also
serves as a rendering volume.
Here, I’m toggling on/off the GI box rendering for a specific volume. Note how
all the other volumes in the scene are unaffected by the toggling.
Deferred rendering a box volume is a local lighting technique that is used
throughout our game and makes a final presentation appearance in this slide.
My References
Stefan Gustavson. 2012. “Procedural Textures in GLSL”. Linköping University Electronic Press (Same as OpenGL
Insights, Chapter 7).
Kevin Myers. 2016. “Sparse Shadow Tree”. SIGGRAPH 2016.
Li Bo. 2019. “A Scalable Real-Time Many-Shadowed-Light Rendering System”. SIGGRAPH 2019.
Zhenzhong Yi. 2020. “From Mobile to Console: Genshin Impact’s rendering technology on Console”. Unite Seoul 2020.
Nikolay Stefanov. 2016. “Global Illumination in Tom Clancy’s The Division”. GDC 2016.
Noriaki Shinoyama. 2018. “Lightmass Deep Dive 2018 Vol.1”.
Noriaki Shinoyama. 2018. “Lightmass Deep Dive 2018 Vol.2”.
Carsten Wenzel. 2006. “Real-time Atmospheric Effects in Games”. Siggraph 2006.
Miles Macklin. 2010. “Inscattering Demo”. http://blog.mmacklin.com/2010/05/29/in-scattering-demo/
Yossarian King. 2000. “2D Lens Flare”. Game Programming Gems 1.
Now, I’m going to hand it off to Komada-san for his section on the tech
behind our toon face shadows.
Agenda
Game Introduction
Deferred Toon Rendering
Comic Shader
Toon Light
Shadows
Static Shadow Map
Global Illumination
Toon Face Shadow
Final Words
Toon Face Shadow
I’m going to explain the face shadow of Hi-Fi Rush in this section.
Hi-Fi RUSH Character Self Shadow
Cel-shading style.
→The shapes of the shadow areas
should be “always clean”!
// Pixel Shader
NdotL = Dot(normal, lightVector)
If (NdotL > threshold) {
return baseColor ;
} else {
return shadowColor ;
}
The first problem is that using vertex normals makes it difficult to create a
smooth curve.
As shown in the image on the right, depending on the direction of the light,
the shadow shape can become quite messy.
It is because there is a limit to how much geometry can be divided, and the
shadow shape is affected by how it is divided.
Problems With Vertex Normals
Problem 2: Facial motion breaks the shadow shape.
The second problem is that the shape of shadows created by vertex normals
is easily broken by facial motion.
When the bone orientation changes due to facial motion, the orientation of
the vertex normals also changes, which unintentionally breaks the shadow
shape.
In this scene where the facial expression is extreme, the shadow shape
becomes considerably broken depending on the direction of the light.
Problems With Vertex Normals
Problem 3: It is difficult for artists to solve problems 1 and 2
by adjusting the model.
// pixel shader
height = TexSample(thresholdMap , uv)
If (height > threshold) {
return baseColor ;
} else {
return shadowColor ;
}
First, calculate the angle between the horizontal component of the light
direction and the forward direction of the head.
The horizontal component here refers to the horizontal component in the
coordinate system of the head bones.
The threshold value is the angle divided by pi and normalized to a range of 0
to 1 .
Then, it is determined whether the area is a shadow area or not based on
whether the sample value of the threshold map is greater than the threshold
value.
Shading With A Threshold Map
• The shadow only moves
horizontally.
• When the light hits from the left
side, flip the texture horizontally.
Reference:
Unity Seoul 2018
" From mobile to high-end PC: Achieving
high quality anime style rendering on
Unity "
Jack He (miHoyo)
Since only the horizontal component is considered, the shadow can only be
moved in the horizontal direction.
However, this was enough for Hi-Fi RUSH.
In this video, the light is shining on the right side of the face, but when it is on
the left side, the texture is reversed horizontally.
・・・
First, use a DCC tool to bake the shadows of the face into textures by moving
a directional light at fixed angles 180 degrees around the character, as shown
in the image.
We moved the light every 5 degrees, so we baked 36 textures.
At this point, shadows are shaded using the vertex normals, so the shape may
not be smooth or shadows may appear in unnecessary areas.
How To Author A Threshold Map
2. Artists can manually retouch the baked textures.
As you can see, retouching textures is much more intuitive to adjust than
vertex normals.
Artists can also preview the final look of shadows in our DCC tool.
How To Author A Threshold Map
3. Merge into one sheet using distance field-like interpolation
using in-house tools.
・・・
Finally, using a dedicated in-house tool, all textures are interpolated like a
distance field interpolation and merged into a single texture.
The threshold map is now complete.
Why Threshold Maps Solve The Problems
I’ll now review how threshold maps solved our original vertex normal
problems.
Regarding problem 1, the shape does not become a smooth curve with the
vertex normal, but with a threshold map, it is always smooth as shown in the
video.
This is thanks to the artist adjusting the shadow shape through retouching.
Also, since the textures are connected smoothly using distance field-like
interpolation, the shape changes smoothly when the direction of the light
changes.
Problem 2, where the shape is broken due to facial motion, does not occur
with the threshold map.
Because in the case of threshold map, the shape is mapped with UV so is not
affected by bones.
Why Threshold Maps Solve The Problems
When using vertex normals.
Problem 3 is that shading using vertex normals is difficult for artists to adjust.
Again, in the case of a threshold map, artists can intuitively modify the shape
by retouching the baked textures.
When shading using vertex normals, the vertex normals were directly used for
shading.
When shading using a threshold map, in between light baking and texture
merging, there is a step where artists can make intuitive quality adjustments.
・Provide the team with the best tech for our goals.
Our game wanted to do a stylized cel-shaded look for both characters and the
environment and this presented unique rendering challenges.
Unique problems call for unique solutions and we developed an original
deferred post process volume approach for our attempt at toon rendering.
We’re using UE4 and we adapted many 3D graphics features for toon
rendering. We use well established graphics techniques and build on the great
works of others.
We talked about tech today, but its toon rendering and as you can imagine
there was a lot crafting and optimizations by our artists not covered today
that ultimately determined the quality of the toon graphics. We had goals of a
rock solid 60fps, high resolution and great looking toon and the tech we
talked about today helped the Hi-Fi RUSH team achieve these goals.
We had more tech we wanted to share info about but didn’t want to tire
people with too much information so there are unpresented bonus slides.
They’ll be available for download with our slides.
Special Thanks Our Art Director & Artists
I want everything System Programming Team
in the world to look Environment Programming Team
toon. Characters, Our QA & Bethesda QA
environment, Intel, AMD, Nvidia Tech Support
everything. Microsoft ATG
Epic Games Japan
We get to present this stuff, but our toon rendering was made possible by the
hard work of everyone on the Hi-Fi RUSH team.
Our final slide is a special thanks page to give a shout out to all the people
who made today’s talk possible.
*Chris, our advisor, gave great feedback to help us revise, came to our talk
and was just overall awesome. Thanks Chris!
Stuff We Couldn’t Fit In Our Talk
Chai
Rock Star
Agenda (For Stuff We Couldn’t Fit In The Talk)
GBuffer Stencil
Volumetric Fog
Toon Lensflare
GPU Physics Simulation
Deferred Toon Renderer GBuffer
Chai
Rock Star
We ended up modifying the engine source code to use the specular and
GBufferAO channels for our own purposes.
To get the most information out of 2 8-bit channels, we converted the 2
channels to bitmask stencil values.
GBuffer Stencil
// GBUFFERAO GBUFFER STENCIL BITS (Partial Listing)
#define HBK_AO_STENCIL_CHARACTER_MASK 4
#define HBK_AO_STENCIL_SKIP_NORMAL_OUTLINE 16
#define HBK_AO_STENCIL_SKIP_DEPTH_OUTLINE 32
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_MAIN 1
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_2 2
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_3 4
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_4 8
#define HBK_SPECULAR_STENCIL_ENV_SKIP_AMBIENT_CUBEMAP 16
#define HBK_SPECULAR_STENCIL_ENV_SKIP_STATIC_SHADOWMAP 32
#define HBK_SPECULAR_STENCIL_ENV_SKIP_SSAO 64
Custom Stencil Use Case
I think packing stencil flags into a GBuffer channel is something a lot games do.
We internally called our modification the GBuffer stencil.
This is to distinguish it from UE4’s custom stencil functionality, which we also
used for special game-side post processes.
It doesn’t work for emissive or transparencies, but a great benefit of GBuffer
stencil over standard custom stencil is that we’re already rendering to the
GBuffer and we don’t incur additional render pass costs.
GBuffer Stencil (Character Stencil)
// GBUFFERAO GBUFFER STENCIL BITS (Partial Listing)
#define HBK_AO_STENCIL_CHARACTER_MASK 4
#define HBK_AO_STENCIL_SKIP_NORMAL_OUTLINE 16
#define HBK_AO_STENCIL_SKIP_DEPTH_OUTLINE 32
*Play animation.
In Hi-Fi RUSH, characters and environment are both toon shaded, but have
their own toon lighting systems.
A lot of times, we want to skip the environmental lighting from affecting
characters.
During battle, the number of characters drawn on screen and the screen
space they occupy can become large. Being able to distinguish character
pixels without an additional render pass was an important performance win
for us.
GBuffer Stencil (Material Parameters)
// GBUFFERAO GBUFFER STENCIL BITS (Partial List)
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_MAIN 1
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_2 2
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_3 4
#define HBK_SPECULAR_STENCIL_SKIP_SHADOW_4 8
#define HBK_SPECULAR_STENCIL_ENV_SKIP_AMBIENT_CUBEMAP 16
#define HBK_SPECULAR_STENCIL_ENV_SKIP_STATIC_SHADOWMAP 32
#define HBK_SPECULAR_STENCIL_ENV_SKIP_SSAO 64
Artists prepare unique material instances for *Some Gbuffer stencil values can be set per-actor as
each GBuffer stencil combination. well.
A lot of our GBuffer stencil values are parameters that artists use to skip the
application of certain lighting passes such as shadows, ssao, toon outlines, etc
to control the look of the toon shading.
We allow artists to set the GBuffer stencil values per-material and for some
parameters per-mesh actor as well.
GBuffer Stencil In Action
SSAO
On/Off
No Change
to SSAO
Only the sliding door’s material’s GBuffer
stencil flags have been changed.
Here’s an image showing our GBuffer stencil in action giving artists more
control over how lighting layers are applied to the scene.
SSAO and static shadow maps are toggled on/off on the front door using
GBuffer stencil flags. Notice that the ground uses a separate material from
the door and is unaffected.
Agenda (For Stuff We Couldn’t Fit In The Talk)
GBuffer Stencil
Volumetric Fog
Toon Lensflare
GPU Physics Simulation
What Is Analytic Fog?
We called our volumetric fog technique analytic fog. The technique name
might sound unfamiliar, but the algorithm we use is nothing new
Volumetric Fog Types
Point Light Analytic Fog Spot Light Analytic Fog Light Shaft Mesh
The first type looks better, but the second type is more performant, and
artists have more freedom with the light shaft shape. Each are used
accordingly and work well for our toon look.
Analytic Fog Actor Level Placement
Environment artists place analytic fog actors locally to light the scene with
volumetric fog. Easy artist control in our toon environment was a big reason
we choose our volumetric fog solution.
Analytic Fog Implementation Details
・Possible to implement without engine
modifications.
Our analytic fog shader requires only the scene depth texture meaning
analytic fog can be implemented inside standard UE4 transparent materials.
UE4 has a froxel-based volumetric fog implementation with local control
through vfx materials. Besides needing engine modifications, we noticed
lowering the froxel resolutions could result in block artifacts which goes
against our goals for a sharp clean look.
Fog Scatter Calculations
Inscattering Demo Blog Article
Inspired by Miles Macklin’s blog article.
Env
The rich look of our analytic fog shader comes from the fog in-scatter
calculation.
We calculate fog scattering by analytically solving the line integral for how
much a point light source scatters through a fog medium before it reaches the
camera.
Our method can be ALU intensive; the more expensive spot light analytic fog
actor drawn at full-screen can cost around 1ms at 1440p on XSS, but it is
performant enough to be used where effective.
We decided against raymarched approaches, because we thought our
solution would provide a cleaner look without any sampling artifacts.
Point Light Analytic Fog
Env
Render a sphere shaped mesh. Draw mesh back face, so the camera can enter the
sphere volume.
*DepthWrite is on in the above screenshot to make it more obvious that the
mesh backface is drawn. DepthWrite is off during the actual rendering.
Depth write is disabled, so the fog pierces Scene Depth < Fog Volume Mesh Depth
through backgrounds.
If this Use the scene’s world position in
happens. the fog calculations.
Env
Our spot light analytic fog has a similar setup to our point light type.
We use a sphere mesh as the fog mesh and the spot light’s cone shape is
generated by calculating a cone-ray intersection inside the shader.
Spot Light Analytic Fog Aperture
Env
Env
This is what our spot light analytic fog looks like with camera movement.
The scattering shape correctly widens to reflect the true width of the
spotlight cone and looks pretty cool with camera movement.
Like mentioned before, spot light analytic fog can be costly when looked at
head on. 1 ms is a big cost for a 60FPS game, so they are selectively used in
spots where they are effective.
Agenda (For Stuff We Couldn’t Fit In The Talk)
GBuffer Stencil
Volumetric Fog
Toon Lensflare
GPU Physics Simulation
Implementing Game-Side Render Passes In UE4
What to do?
For our post process lensflare, we added render passes to our game-side code
without engine modification.
We used standard UE4 render commands to sync rendering data between the
game thread and the render thread.
We utilized render thread delegate callbacks to execute from the engine-side
render thread, render passes implemented in our game module.
Render Thread Delegate Render Passes
UI Copy Backbuffer
UE4 has a post process lensflare implementation. However, for Hi-Fi RUSH,
art requested a lensflare implementation where the lensflare ghosts could be
drawn with artist prepared textures.
Taking A Look At Our Post Process Lensflare
The goal of our post process lensflare is to show camera lens ghosting that
can occur from strong light sources.
The ghost sizes, intensity, and screen space position changes depending on
where the light source is relative to the camera.
Post Process Lensflare Render Passes
1x1 Occlusion Ratio RT
The slide shows the final rendering result. All lensflare ghosts have been
rendered into a single render target.
Ghost Quad Rendering
Our reference:
Yossarian King. 2000. “2D Lens Flare”. Game Programming Gems 1.
When the lensflare light source is occluded, we want the lensflare to fade
depending on how occluded the light source is.
Calculating Lensflare Occlusion
It’s possible to use gpu hardware occlusion querys to calculate the light
source’s occlusion ratio. However, for our game, we wrote a shader to
calculate an occlusion quad’s occlusion ratio using the depth buffer. Using a
shader has lower UE4 implementation costs and it also avoid hardware GPU
query’s 1 frame lag due to CPU/GPU query result handoff.
Calculating Lensflare Occlusion Ratio
The occlusion quad’s depth buffer
1x1 RT area is sampled 32 times to calculate
the occlusion ratio.
The more occluded the occlusion quad, the more the ghost’s intensity is lowered.
The lensflare rendertarget is merged with the scene color inside a UE4 post
process material rendered after tonemapping to complete the lensflare
rendering.
Agenda (For Stuff We Couldn’t Fit in The Talk)
GBuffer Stencil
Volumetric Fog
Toon Lensflare
GPU Physics Simulation VFX
GPU Physics Simulation VFX
From here we will move on to the GPU physics simulation section, thank you.
Later Stage Battle Spoiler Alert
In this section, we will talk about the technology used in a battle in one of
the final stages.
Spoiler warning!
If you want to avoid spoilers, I think it's best to close your eyes and cover
your ears.
movie
In the boss battle at the end of the game, there is a scene where you fight in a
room full of coins.
We needed a coin effect that would react to the movements of the characters
and gimmicks, but it would be difficult to create effects manually for every
movement, so we considered implementing it in as a physics simulation.
Coin Effect Requirements
This section explains the scene setup for the physics simulation.
First, I’m setting up a box to cover the area where you'll fight the boss.
64,000 coins are spawned and simulated inside the box.
The coins collide with characters and gimmicks inside the box and scatter
creating the coin simulation effect.
(The coins you see in this image are static mesh foliage coins covering the
surface and are not the simulation coins.)
Physics Simulation Scene Setup
Initial placement of coins is random.
If the placement is in a grid pattern, the scattering trajectory of the coins
will be too symmetrical and unnatural.
The initial placement of coins is not in a grid pattern, but rather in a slightly
random placement as shown in the image.
This is done to add noise to the trajectory because if it were in a grid pattern,
a strong symmetry would appear in the trajectory of the coins scattering and
it would feel unnatural.
Physics Simulation Scene Setup
• The simulation box floor is sandwiched between the static mesh coin floor and the
platform floor.
• Coins being simulated are not visible to the user when sleeping on the floor.
→ By not showing the coins that fell on the box floor, you can prevent coins from falling
into each other and hide the lack of quantity.
→ By raising the collision box floor, scattering from collision with the player collider sphere
will bounce upwards and look better.
The simulation box is placed between the scaffold floor and the coin floor
mesh covering the surface.
The only time the simulated coins are visible to the user is when they pop out
of the static mesh coin floor.
The reason for this setting is to avoid ruining the appearance.
Even though there are 64,000 coins, this number is not enough quantity to
cover the entire surface.
Also, since collisions between coins are not calculated, coins may penetrate
through each other.
These problems are hidden by the static mesh coin floor.
The reason why the box floor is higher than the platform floor is because this
creates a better effect as the coins scatter upwards.
The Setup For Physics Simulation
• Since it uses rigid body physics, it also simulates
the rotation of a coin.
Use a moment of inertia that matches the shape of the coin.
Since coins are treated as rigid bodies, rotational forces are also simulated.
I am using the moment of inertia that matches the shape of the coin.
Coins collide with boxes and dedicated colliders added to characters and
gimmicks.
The collider shape supported are spheres and capsules.
For example, the player character has a capsule and sphere collider set up like
in the picture.
PBD References
• Used Position Based Dynamics for physics simulation.
(hereinafter referred to as PBD )
Used due to robustness of behavior.
• Referenced paper.
Matthias Müller, Miles Macklin et al.
Detailed Rigid Body Simulation with Extended Position Based Dynamics.
ACM SIGGRAPH / Eurographics Symposium on Computer Animation 2020
• Colliders update
• Sleep states update
• Integrations
• Collision constraints solving
Colliders update
Sleep states update
Integrations
Collision constraints solving
The box that covers the entire scene does not move.
On the other hand, the collider attached to characters and gimmicks can
move around and enter and leave the simulation.
Therefore, every frame, the CPU side implementation detects colliders with a
specific collision channel and passes the information about the shape,
position, orientation and size of colliders to the simulation.
Sleep state update
movie
I think that in normal rigid body simulation implementations, coins are often
treated as cylindrical meshes, as shown in the image on the right.
However, this would require calculations for multiple vertices and edges,
which would increase the load.
Therefore, we included two approximations in our implementation.
The first is that coins are treated as disk shapes without considering the
thickness.
Second, only the deepest point in the disk is treated as a impact point.
As shown in the figure, when the coin is penetrating into the capsule, the
impact point, the penetration depth, and the normal of the collider at the
closest point are calculated from the red point that is the deepest penetration
point in the disk.
Collision Constraint Solving
Apply the following to collided coins:
Extrusion of penetration
Repulsion, dynamic friction (box only)
Repulsion and dynamic friction were not used in the characters and gimmicks because
there was no problem with the expression even if they were not included.
* The box and collider do not move even if they collide. Treated as infinite mass.
If the coin collides with the box, apply an extrusion of penetration, dynamic
friction, and repulsion.
If the coin collides with a character or gimmick collider, only the extrusion will
be applied.
For characters and gimmicks, simulation results lacking repulsion and dynamic
friction were acceptable, so are omitted as a performance optimization.
Boxes and colliders do not move when they collide. Only coins are move.
Static friction was also removed because dynamic friction alone was sufficient
to express friction.
Collision and static friction between coins are essential for convincing coin
piling.
Also, the fact that our coins are unrealistically large is an optimization.
If the coins were of real size, the number of simulations would increase,
making it difficult.
In addition to simulations, there are also issues such as the rendering load of
the coin floor, and the fact that realistic sizes were actually hard to see.
The size was decided so that it would not look too unnatural.
Performance
• Measurement condition
Xbox Series S Test build 1440p 64000 pieces
Measured at the timing when many coins are scattered.
• Measurement result
✓ Simulation
Simulation by Niagara : 0.40ms
✓ Rendering (increased processing time due to rendering of coins. coins do not have shadows.)
PrePass : 1.10ms → 1.27ms
Velocity : 0.40ms → 0.64ms
BasePass : 2.08ms → 2.27ms
The first is to implement things that run on the CPU side first.
We recommend implementing it on the CPU side first and porting it to the
shader once it has been tested and bug-free.
This is because debugging shaders is difficult.
When using a shader for drawing, I think it is common practice to debug it by
writing the progress to a buffer and displaying it on the screen.
However, in physical simulations, debugging is often difficult unless you can
step-execute and check the physical quantities.
The amount of implementation will be doubled, but I think the time to
develop will actually decrease in the long run.
Like this.
I've seen many reviews and impressions of Hi-Fi RUSH, but I've never seen
any mention of this setting, so it still remain a mysterious one for Hi-Fi
Rush users.
This concludes the GPU physics simulation section.