GdcVdrLottes PDF
GdcVdrLottes PDF
GdcVdrLottes PDF
2. Screen reflection
Screen Reflection
● Room lighting reflecting off screen
● Places practical limit on contrast ratio
● Example display screen reflectance
● OLED: 1.2% ambient, 2.1% specular
● LCD: 2.2% ambient, 3.1% specular
● Older LCD: 6.5% ambient, 8.5% specular
Example Measured Rooms
● Different room ambient levels
● Easily 15 stop difference in contrast limit
● HDR ready =
good ANSI contrast display + dark room
Part 2 – Tonemapping
Adjusting game display settings
and optimizing tonemapping for
VDR
Eye Adaption to Room Ambient
Lower Mid-Level
Enables More Contrast
Darker Room Between Mid and Highlights
Enables Lower Lower Reflection Drops Black
Mid-Level
Display Black + Reflection
Tonemapping Goals
● Adaption viewing condition for VDR
● Want believably real images
● Effects can be subtle (little details)
● Want {saturation, contrast, hue} changes
fully decoupled from changes in tonality
● Same content with different mid-level
mapping should look consistent
Tonemapping Goals Part 2
● Built in contrast & saturation control
● Existing in-game auto-exposure
● Driving input mid-level
● Stable settings
● No retune as output mid-level changes
Conventions for VDR
● Traditional display calibration/viewing
convention is to drop peak brightness to
comfortable level
Mid-Level Output
Brightness Knob Sets This
Input
Display Black Mid-Level in the Virtual Scene
● Contrast
● Sets toe of curve
Toe
Building a Tonemapper: Part 2.0
● y=x/(x+1);
Output Mid-Level
Input Mid-Level
(* Mathematica source for solving for {b,c} *)
tonemap[x_]:=(x^a) / (((x^a)^d) * b + c)
Solve[{tonemap[midIn] == midOut,
tonemap[hdrMax] == 1}, {b,c}, Reals]
(* output *)
Clipped Highlights
Raw Data Tonemapped
(-3 Stop Mid-Level) (-3 Stop Mid-Level)
Clipped Highlights
Eye Adaption
● 1st image and 2nd image of prior series
● Represents a practical difference between
“lights off” and “lights on” viewing conditions
for VDR when display has good ANSI Contrast
Contrast Changes
Saturation Changes
Separation of Max and RGB Ratio
● Tonemapping is applied to max of {r,g,b}
● peak = max3(rgb); ratio = rgb/peak;
● peak = tonemap(peak); output = peak*ratio;
● Color processing is done separately
● (adjusting ratio done separately)
● Has advantages in over-exposure cases
Tonemapping Tonemapping
RGB Channels Max RGB
Separately (Overexposure)
(Overexposure)
Same
Tonemapping
Desaturation
Parameters
Distorted Color
Processing of Color Ratio
● Channel Crosstalk
● Works similar in concept to real-world film
● Move colors towards white as they
overexpose maintaining perception of
brightness
Path to White
● Path colors take to white is important
● Direct path desaturates fast
● Indirect path which moves brighter
channels faster maintains more
saturation
● Apply crosstalk non-linearly
Tonemap Max RGB, with
“Direct Path to White”
(Desaturates)
Tonemapping RGB Separately
With no added Crosstalk Both show nonlinear plot of highlights
(Saturated Colors Clamp to Pure Hues) (exp2(rgb*16384)-1)/(exp2(16384)-1)
(Desaturated Colors Desaturate More) processed through tonemapper
Tonemapping RGB Separately
With no added Crosstalk Tonemap Max RGB, with
(Saturated Colors Clamp to Pure Hues) “Indirect Path to White”
(Desaturated Colors Desaturate More) (Maintains Saturation)
Tonemapping RGB Separately Can use same control to also increase
With no added Crosstalk Saturation globally
(Saturated Colors Clamp to Pure Hues) (Important when increasing Contrast
(Desaturated Colors Desaturate More) globally using Tonemapper)
// improved crosstalk – maintaining saturation
// final color
color = ratio * tonemappedMaximum;
Tonemapping Optimization
● If tonemapping is applied in texture or
bandwidth bound situation, or used to
build LUTs dynamically at run-time
● Will be using shader based implementation
● Most parameters can be factored out to
constants changed per frame
Optimizing for LUT (3D Lookup)
● If tonemapping applied in an ALU limited
situation or if doing {color grading,
tonemapping, final transfer function}
with LUT simultaneously
● Often a 3D texture will be used
● Large dynamic range is a challenge
Looking at LUT Error Plots
● For the first plots
● Using x/(x+1) as proxy for tonemapper
● 8-bit output for sRGB
● Using 6 stops of range above 1.0
● 32x32x32 LUT
● But showing one dimension in the plot
First Plot = Worst Case
● Using linear input directly
● output = LUT.Sample(s, linearColor/64.0);
Ideal Output
Higher Precision
(Good)
LUT Output
Lower Precision
(Bad)
LUT Precision
(Filled Dark Purple)
Linear Input (Worst Case)
Higher Precision
(Good)
Lower Precision
(Bad)
This LUT Has
Tremendous Error
2nd Plot = Wrap in Sqrt()
● output = LUT.Sample(s,
sqrt(linearColor/64.0)).rgb;
Higher Precision
(Good)
Lower Precision
(Bad)
Improved Precision
(But Not Good Enough)
Higher Precision
(Good)
Lower Precision
(Bad)
Greatly Improved Quality
// pq from linear
// based on implementation in aces 1.0 (see url)
// https://github.com/ampas/aces-
dev/blob/master/transforms/ctl/utilities/ACESlib.Utilities_Color.a1
.0.1.ctl
float pq(float x) {
float m1 = 0.1593017578125;
float m2 = 78.84375;
float c1 = 0.8359375;
float c2 = 18.8515625;
float c3 = 18.6875;
float p = pow(x, m1);
return pow((c1 + c2 * p) / (1.0 + c3 * p), m2); }
4rd Plot = Wrap by Log2()
● PQ(rgb) requires 15 transcendental ops
● More than the tonemapper uses
Higher Precision
(Good)
Lower Precision
(Bad)
Error Plot Similar To PQ
More Complex Cases
● Showing a mix of different options
● Using tonemapper from this presentation
● 10-bit output instead of 8-bit
● Gamma 2.2 output instead of sRGB
Shaper: pq(x)
Output: 10-bit for Gamma 2.2
Tonemapper Params
MidIn: 0.18
MidOut: 0.18
HdrMax: 64.0
Contrast: 1.3
Shoulder: 0.995
Shaper: log2(x*exp2(20.0)+1.0)
Output: 10-bit for Gamma 2.2
Tonemapper Params
MidIn: 0.18
MidOut: 0.18
HdrMax: 64.0
Contrast: 1.3
Shoulder: 0.995
-3 Stop Mid-Level
Shaper: log2(x*exp2(20.0)+1.0)
Output: 10-bit for Gamma 2.2
Tonemapper Params
MidIn: 0.18
MidOut: 0.18 / exp2(3.0)
HdrMax: 64.0
Contrast: 1.3
Shoulder: 0.995
Now 64^3 (Was 32^3)
Shaper: log2(x*exp2(19.0)+1.0)
Output: 10-bit for Gamma 2.2
Tonemapper Params
MidIn: 0.18
MidOut: 0.18 / exp2(3.0)
HdrMax: 64.0
Contrast: 1.3
Shoulder: 0.995
LUT Error Plot Takeaways
● Various options to customize to goals
● Log2() input shaper
● Can run LUT after ALU based tonemapping
and after application of transfer function
● Could also try 32x64x32 (RxGxB) LUT
● Green more perceptually noticeable
Part 3 – Transfer
Functions & Quantization
Techniques to get high quality
output on any display
Quantization Goals
● Enable high quality blacks at any bit depth!
● Energy-preserving (won’t change tone/color)
● Can be applied and work for different transfer
functions even if app can not query transfer
function OS is using
● Can be artistically controlled to serve same
purposes as film grain
Quantization Example
● Next slide shows an example of what is
possible with static high quality
quantization techniques described in this
presentation
● Uses a high quality grain texture
● Looks even better temporally
Both Shots Are 3-Bits/Channel
Transfer Function?
● Often impossible to query or know on PC
PQ 400 nit
PQ 800 nit
PQ 3200 nit
sRGB
Gamma 2.2
Dithering At 3-Bits
● Simple energy-preserving dither
● Add constant amount of grain in linear
● Done before transform and quantization
● Blacks corrected, peaks not (error at top)
● HDR Transfer Functions show “banding”
● Because of symmetrical (-/+) grain
largest linear step
(peak used for dither)
Transfer Functions largest linear step
extends up off screen
(a constant amount of
grain must be sized to
dither this step)
PQ 3200 nit
sRGB
Gamma 2.2
Peaks Not Energy Preserving
● Not correcting for lack of values above
1.0 in this example
● Not important as bit/pixel increases
● Correcting blacks
● Important as bit/pixel increases
-1 0 1 2 3
// simple energy-preserving dither
PQ 400 nit
PQ 3200 nit
sRGB
Gamma 2.2
Asymmetrical Grain Distribution
● Required with HDR Transfer Functions
● Shift so negative side gets more grains
● Shift so positive side gets higher values
● Offset and scale to maintain equal area (-/+)
-
+
Asymmetrical Grain – No Banding
Transfer Functions
PQ 400 nit
PQ 800 nit
PQ 3200 nit
sRGB
Gamma 2.2
Now at 5-Bits
● HDR Transfer Functions
● Need higher amounts of constant grain
because they have a larger max linear step
size (located in towards the peak)
● PQ examples take peak step at display peak
nits (instead of at 10,000 nit encoding peak)
5-bits Per Channel
Transfer Functions
PQ 400 nit
PQ 800 nit
PQ 3200 nit
sRGB
Gamma 2.2
Adaptive Grain at 5-Bits
● Adapting the grain to the step size
● Requires computing step size per channel
● More GPU work
● Workaround for constant grain with PQ
● Might not be required at high bit depths
Adaptive Grain Has No Effect in Brights
Transfer Functions
PQ 400 nit
PQ 3200 nit
sRGB
Gamma 2.2
Takeaway
● Use high quality quantization to get high
quality output for any bit depth and
transfer function
● Great solution to remove banding for
traditional LDR games as well
Part 4 – Choose Your
Own Adventure
Thanks for watching