Skip to content

Commit 81b2f92

Browse files
authored
fixed exposure bug and added figures (#4648)
* fixed exposure bug and added figures * link docs in FAQ
1 parent 2da8180 commit 81b2f92

File tree

7 files changed

+135
-59
lines changed

7 files changed

+135
-59
lines changed

packages/model-viewer/src/three-components/Renderer.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,8 +435,9 @@ export class Renderer extends
435435
const exposureIsNumber =
436436
typeof exposure === 'number' && !Number.isNaN(exposure);
437437
const env = element.environmentImage;
438+
const sky = element.skyboxImage;
438439
const compensateExposure = toneMapping === CustomToneMapping &&
439-
(env == null || env === 'neutral' || env === 'legacy');
440+
(env === 'neutral' || env === 'legacy' || (env == null && sky == null));
440441
this.threeRenderer.toneMappingExposure =
441442
(exposureIsNumber ? exposure : 1.0) *
442443
(compensateExposure ? COMMERCE_EXPOSURE : 1.0);
2.76 MB
Binary file not shown.
2.76 MB
Binary file not shown.

packages/modelviewer.dev/data/docs.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@
887887
"htmlName": "toneMapping",
888888
"description": "Selects the function that compresses the HDR rendering to an SDR image on your screen. ACES is a film industry standard that is commonly used, though it has serious color-accuracy problems. AgX is a new and improved tone mapper seeing broad adoption in film and games.Commerce is a function designed specifically for accurate color reproduction in e-commerce. Our current default is and has been ACES, but in v4.0 this default will change to Commerce.",
889889
"links": [
890-
"<a href=\"../examples/lightingandenv/#renderExposure\"><span class='attribute'>exposure</span> example</a>"
890+
"<a href=\"../examples/lightingandenv/#toneMapping\"><span class='attribute'>tone-mapping</span> example</a>"
891891
],
892892
"default": {
893893
"default": "aces",

packages/modelviewer.dev/data/faq.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@
6767
"<a href='../fidelity'>glTF fidelity comparison</a>"
6868
]
6969
},
70+
{
71+
"name": "How do I ensure accurate colors?",
72+
"htmlName": "color",
73+
"description": "The short answer is to use neutral (grayscale) lighting and our new 'commerce' tone-mapping function, which is designed specifically to ensure faithful rendering of the glTF's colors. This is a complicated topic, so follow the links for much more in-depth explanations. The single biggest problem with color in 3D e-commerce is the industry-standard ACES tone mapper, so please consider using our commerce alternative.",
74+
"links": [
75+
"<a href='../examples/lightingandenv/#toneMapping'><span class='attribute'>tone-mapping</span> example</a>",
76+
"<a href='../examples/color'>Achieving Color-Accurate Presentation with glTF</a>",
77+
"<a href='../examples/tone-mapping'>Tone Mapping Considerations for Physically-Based Rendering</a>"
78+
]
79+
},
7080
{
7181
"name": "Why am I getting CORS errors?",
7282
"htmlName": "cors",

packages/modelviewer.dev/examples/color.html

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<meta name="viewport" content="width=device-width, initial-scale=1">
2424
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png"/>
2525
<link type="text/css" href="../styles/examples.css" rel="stylesheet" />
26-
<script type='module' src='https://modelviewer.dev/node_modules/@google/model-viewer/dist/model-viewer.min.js'></script>
26+
<script type='module' src='../../../node_modules/@google/model-viewer/dist/model-viewer.js'></script>
2727
<script defer src="https://web3dsurvey.com/collector.js"></script>
2828
<script>
2929
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
@@ -404,17 +404,17 @@ <h3 id="photography">How does rendering compare to photography?</h3>
404404
skews (blue to purple, red to orange) and saturation loss. AgX is a newer
405405
and better tone mapper that holds hue better, but still has significant
406406
contrast and saturation loss, which is desirable for its intended use cases
407-
in games and film. For detail, please see our <a
408-
href="tone-mapping.html">technical document</a> on the tradeoffs in tone
409-
mapping and how our Commerce tone mapper was designed.</p>
407+
in games and film. For detail, please see our technical document on the
408+
tradeoffs in <a href="tone-mapping.html">tone mapping</a> and how our
409+
Commerce tone mapper was designed.</p>
410410

411411
<figure>
412412
<model-viewer
413413
id="tone-mapping"
414414
src="../assets/ShopifyModels/Mixer.glb"
415415
tone-mapping="commerce"
416416
camera-controls
417-
alt="Tone mapping comparisons for difference 3D models"
417+
alt="Tone mapping comparisons for different 3D models"
418418
>
419419
<p>Tone Mapping:
420420
<select id="tone">
@@ -432,7 +432,7 @@ <h3 id="photography">How does rendering compare to photography?</h3>
432432
</select>
433433
</p>
434434
</model-viewer>
435-
<figcaption>Comparison of tone mapping functions for difference models.</figcaption>
435+
<figcaption>Comparison of tone mapping functions for different models.</figcaption>
436436
</figure>
437437

438438
<h3 id="validate-render">How do we validate a glTF 3D render?</h3>
@@ -471,15 +471,15 @@ <h3 id="validate-render">How do we validate a glTF 3D render?</h3>
471471
post-processing step. Some things don't apply to rendering, like masking out
472472
shadows to make them semi-transparent - a 3D renderer can do this
473473
automatically. In addition to color-neutral tone mapping, sometimes the
474-
colors are intentionally "corrected". Why? After all, if the measured light
474+
colors are intentionally "corrected". Why? After all, if the captured light
475475
from the actual scene is not correct, what is?</p>
476476

477477
<p>It may be that some of the post-processing color correction is simply a
478478
matter of expediency. Before digital photography, to get the right look, the
479479
lighting and scene had to be adjusted, which involves manual labor. Digital
480480
post-processing can allow the lighting to be less precise. However in 3D
481481
rendering, the lighting is equally digital, so it is generally a better
482-
practice to keep the post-processing step simple (e.g. just ACES tone
482+
practice to keep the post-processing step simple (e.g. just tone
483483
mapping) and adjust the environment image if necessary. Since 3D rendering is
484484
happening completely automatically in real time, there is no way to make
485485
manual bespoke color adjustments for each frame.</p>
@@ -577,7 +577,7 @@ <h3 id="takeaway">What's the takeaway?</h3>
577577
material baseColor, which is what allows the demo below to be compellingly
578578
realistic.</p>
579579

580-
<figure>
580+
<figure>
581581
<model-viewer
582582
id="environments"
583583
src="../assets/ShopifyModels/Mixer.glb"
@@ -592,6 +592,26 @@ <h3 id="takeaway">What's the takeaway?</h3>
592592
realistic.</figcaption>
593593
</figure>
594594

595+
<p>Still, for most e-commerce vendors that are likely managing separate
596+
contracts for 3D model generation and interactive website design, simplicity
597+
is key to cost-effectiveness. My recommendation is to use our Commerce tone
598+
mapping and if you already have approved marketing colors for your products,
599+
then tell your artists to use those for the baseColor. Use a grayscale
600+
lighting environment, adjusting if necessary to place highlights and change
601+
exposure.</p>
602+
603+
<p>If the color is not known, but being matched by eye, then ensure the
604+
artist's tools also use the Commerce tone mapper and a lighting environment
605+
that is as close as possible to the production grayscale lighting
606+
environment. Since our Commerce tone mapper is relatively new, it may not be
607+
available in tools; in this case the next best thing is to turn off tone
608+
mapping entirely. Make sure to educate your artists on the differences they
609+
can expect - blown out highlights with hue skews, and saturation loss for
610+
dark colors. Ensure they also frequently test their output in a
611+
near-production environment, hopefully using &lt;model-viewer&gt; with
612+
<code>tone-mapping="commerce"</code>. This way they can calibrate and ensure
613+
the end user will see realistic colors.</p>
614+
595615
</div>
596616
<div style="margin-top:24px"></div>
597617
<div class="footer">

packages/modelviewer.dev/examples/tone-mapping.html

Lines changed: 93 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<meta name="viewport" content="width=device-width, initial-scale=1">
2424
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png"/>
2525
<link type="text/css" href="../styles/examples.css" rel="stylesheet" />
26-
<script type='module' src='https://modelviewer.dev/node_modules/@google/model-viewer/dist/model-viewer.min.js'></script>
26+
<script type='module' src='../../../node_modules/@google/model-viewer/dist/model-viewer.js'></script>
2727
<script defer src="https://web3dsurvey.com/collector.js"></script>
2828
<script>
2929
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
@@ -146,19 +146,19 @@ <h3 id="purpose">The purpose of tone mapping</h3>
146146
and even things like sepia can all be combined into a single resulting
147147
function that here we're calling tone mapping. However, we are only
148148
interested in hue-neutral functions and those are the only type of
149-
tone mapping functions we'll be discussing here. Therefore the focus will be
150-
primarily on luma, or brightness.</p>
149+
tone mapping functions we'll be discussing here.</p>
151150

152-
<p>The tone mapping function used by <code>&lt;model-viewer&gt;</code> is
153-
ACES, which is a standard developed by the film industry and is widely used
154-
for 3D rendering. Like most tone mapping curves, it is fairly linear in the
155-
central focus of its contrast range, then asymptotes out to smoothly
151+
<p>The default tone mapping function used by
152+
<code>&lt;model-viewer&gt;</code> has been ACES, which is a standard
153+
developed by the film industry and is widely used for 3D rendering, though
154+
it has some problems. Like most tone mapping curves, it is fairly linear in
155+
the central focus of its contrast range, then asymptotes out to smoothly
156156
compress the long tails of brights and darks into the required zero to one
157157
output range, the idea being that humans perceive less difference between
158158
over-bright and over-dark zones as compared to the bulk of the scene.
159159
However, since some output range is reserved for these extra-bright
160160
highlights, the range left over to represent the input range of matte
161-
baseColors is also reduced somewhat. This is why the paper-white sphere does
161+
baseColors is also reduced. This is why a paper-white material does
162162
not produce white pixels.</p>
163163

164164
<p>Sometimes when working with matte objects and trying to compare output
@@ -186,28 +186,30 @@ <h3 id="purpose">The purpose of tone mapping</h3>
186186
<figcaption>Toggle ACES tone mapping to see the difference it makes.</figcaption>
187187
</figure>
188188

189-
<p>Note that once again the shiny and matte white plastic spheres are
190-
indistinguishable, even without cranking up the exposure. Since half of the
191-
matte white sphere is now rendering pure white, there is no headroom for
192-
shiny highlights. Likewise, the top half of the sphere loses its 3D
193-
appearance since the shading was removed by clamping the values. Tick the
194-
checkbox to go back to ACES tone mapping for a quick comparison. Remember to
195-
look away and back again after switching; another trick of human perception
196-
is how dependent it is on anchoring. The yellow will look washed-out
197-
immediately after switching from saturated yellow, but this perception fades
198-
after looking around.</p>
189+
<p>This model has six spheres with uniform materials: The top row are white
190+
(baseColor RGB: [1, 1, 1]), while the bottom row are yellow (baseColor RGB:
191+
[1, 1, 0]). From left to right they are shiny metal (metalness: 1,
192+
roughness: 0), shiny plastic (metalness: 0, roughness: 0), and matte plastic
193+
(metalness: 0, roughness: 1). The left-most can be thought of approximately
194+
as polished silver and gold.</p>
195+
196+
<p>Tick the checkbox to remove tone mapping for a quick comparison. Note
197+
that the shiny and matte white plastic spheres are now indistinguishable.
198+
Since half of the matte white sphere is now rendering pure white, there is
199+
no headroom for shiny highlights. Likewise, the top half of the sphere loses
200+
its 3D appearance since the shading was removed by clamping the values.
201+
</p>
199202

200203
<p>This example also highlights a second key element of good tone mapping
201204
functions: desaturating overexposed colors. Look at the golden sphere
202-
(lower-left) and compare to the previous version with ACES tone mapping
203-
applied. The baseColor of a metal multiplies the incoming light, so a white
204-
light on a golden sphere produces a yellow reflection (fully saturated
205-
yellow, in this case of a fully saturated baseColor). With clamped tone
206-
mapping, the highlight is indeed saturated yellow, but this does not look
207-
perceptually right, even though you could make the argument it is physically
208-
correct.</p>
209-
210-
<p>Good tone mapping curves like ACES not only compress the luma, but also
205+
(lower-left) and compare to when ACES tone mapping is applied. The baseColor
206+
of a metal multiplies the incoming light, so a white light on a golden
207+
sphere produces a yellow reflection (fully saturated yellow, in this case of
208+
a fully saturated baseColor). With clamped tone mapping, the highlight is
209+
indeed saturated yellow, but this does not look perceptually right, even
210+
though you could make the argument it is physically correct.</p>
211+
212+
<p>Tone mapping curves like ACES not only compress the luma, but also
211213
push colors toward white the brighter they are. This is why the highlights
212214
on the golden sphere become white instead of yellow. This follows both the
213215
behavior of camera sensors and our eyes when responding to overexposed
@@ -270,13 +272,20 @@ <h3 id="#tradeoffs">Tradeoffs</h3>
270272

271273
<figure>
272274
<model-viewer
273-
src="../../shared-assets/models/silver-gold.gltf"
274-
tone-mapping="aces"
275+
id="reachable"
276+
src="../assets/ACESset.glb"
277+
camera-orbit="150deg auto auto"
275278
camera-controls
276-
alt="3D model of six example material spheres"
279+
alt="3D model of ACES/Commerce tone mapping reachable colors."
277280
>
281+
<p>
282+
<select id="set">
283+
<option value="../assets/ACESset.glb">ACES</option>
284+
<option value="../assets/CommerceSet.glb">Commerce</option>
285+
</select>Tone Mapping Function
286+
</p>
278287
</model-viewer>
279-
<figcaption>The ACES reachable colors.</figcaption>
288+
<figcaption>Comparison of the ACES and Commerce tone mapping reachable colors. The cube represents the [0, 1] space in linear light - no sRGB curve has been applied.</figcaption>
280289
</figure>
281290

282291
<p>Note that canary yellow, bright greens and blues are all impossible to
@@ -398,7 +407,10 @@ <h3 id="#commerce">Commerce tone mapper</h3>
398407
only other parameter in this tone mapper controls the rate of desaturation,
399408
which I chose as 0.15, which is significantly slower to approach its
400409
asymptote than the compression function. This is what helps produce our
401-
smoother gradients and hide the aggressiveness of our compression.</p>
410+
smoother gradients and hide the aggressiveness of our compression. In some
411+
sense I am replacing the lost brightness with desaturation, thus giving the
412+
brain an alternate perceptual cue, which smoothly encodes several orders of
413+
magnitude more brightness than is available in the output screen.</p>
402414

403415
<p>The complete shader code is quite small, with only three divides and
404416
those only applied to colors over the 1:1 limit:<br/>
@@ -447,7 +459,7 @@ <h3 id="#commerce">Commerce tone mapper</h3>
447459
<figure>
448460
<model-viewer
449461
id="demo"
450-
src="https://cdn.glitch.global/93f14439-9407-4d48-8b56-aa0afd9b2886/MacbethBalls.glb?v=1704772991939"
462+
src="../../shared-assets/models/MacbethBalls.glb"
451463
tone-mapping="commerce"
452464
ar
453465
camera-controls
@@ -479,6 +491,50 @@ <h3 id="#commerce">Commerce tone mapper</h3>
479491
<figcaption>Tone mapper test demo.</figcaption>
480492
</figure>
481493

494+
<h3 id="#validation">Validation</h3>
495+
496+
<p>The best end-to-end validation we have for color accuracy is to apply an
497+
unrealistic, analytic lighting environment: a white furnace test, where the
498+
lighting is exactly uniform [1, 1, 1] white everywhere. This allows us to
499+
expect a nearly-exact reproduction of baseColor to the output render, and
500+
thus ensure our tone mapping function is not introducing further
501+
changes.</p>
502+
503+
<p>Our 3D Macbeth chart model is ideal for this validation because tone
504+
mapping is not applied at all to unlit materials, so the unlit spheres serve
505+
as ground truth color comparisons for the PBR spheres. As you can see, they match
506+
very well, in fact as close as is possible to match for PBR: there are
507+
two expected sources of difference.</p>
508+
509+
<figure>
510+
<model-viewer
511+
id="demo"
512+
src="../../shared-assets/models/MacbethBalls.glb"
513+
skybox-image="../../shared-assets/environments/white_furnace.hdr"
514+
tone-mapping="commerce"
515+
ar
516+
camera-controls
517+
shadow-intensity="1"
518+
>
519+
</model-viewer>
520+
<figcaption>Commerce tone mapper validation.</figcaption>
521+
</figure>
522+
523+
<p>The first difference is from the Fresnel effect: on shiny materials, the
524+
reflection loses material color near grazing angles. This is a physical
525+
reality and causes the white halos on the edges of the shiny (back) spheres.
526+
If you turn the model until the unlit spheres overlap the middle of the
527+
shiny spheres, you'll see that the color match is exact at normal (center)
528+
reflection.</p>
529+
530+
<p>The second effect is multi-scattering, which causes the dark-colored
531+
matte (front) spheres to be slightly darker than their unlit comparisons.
532+
This is also intentional, as matte materials are rough, thus forming
533+
microscopic cavities that cause slight ambient occlusion and allow dark
534+
materials more light bounces to absorb energy. Accurate PBR renderers
535+
include this effect because a single material will in fact become brighter
536+
as it is polished.</p>
537+
482538
<h3 id="#white">White point</h3>
483539

484540
<p>The tl;dr of this section is that you can safely skip it. It is a
@@ -583,21 +639,10 @@ <h3 id="#white">White point</h3>
583639
updateToneMapper(toneMV, checkbox.checked ? "4" : "1");
584640
});
585641

586-
const envMV = document.querySelector("#environments");
587-
const envCycle = [
588-
"../../shared-assets/environments/spruit_sunrise_1k_HDR.hdr",
589-
"../../shared-assets/environments/whipple_creek_regional_park_04_1k.hdr",
590-
"../../shared-assets/environments/lebombo_1k.hdr",
591-
"../../shared-assets/environments/aircraft_workshop_01_1k.hdr",
592-
"../../shared-assets/environments/music_hall_01_1k.hdr",
593-
"../../shared-assets/environments/pillars_1k.hdr",
594-
"../../shared-assets/environments/neutral.hdr"
595-
];
596-
597-
setInterval(() => {
598-
const cycleIndex = envCycle.indexOf(envMV.skyboxImage);
599-
envMV.skyboxImage = envCycle[(cycleIndex + 1) % envCycle.length];
600-
}, 3000);
642+
const reachMV = document.querySelector("#reachable");
643+
document.querySelector('#set').addEventListener("input", (event) => {
644+
reachMV.src = event.target.value;
645+
});
601646
</script>
602647
</body>
603648
</html>

0 commit comments

Comments
 (0)