Skip to content

Commit bbab0d8

Browse files
committed
Implement HDR image support (fixes #65) and transitions to using
scene.background for skybox (#196), but blocked on three.js r101 before the background hack can be removed.
1 parent f61a0b1 commit bbab0d8

File tree

15 files changed

+625
-190
lines changed

15 files changed

+625
-190
lines changed

examples/assets/attributions.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,32 @@ Damaged Helmet by <a href="https://sketchfab.com/theblueturtle_">theblueturtle_<
1818
licensed under Creative Commons Attribution-NonCommercial
1919
(<a href="https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/DamagedHelmet">source</a>)
2020

21-
## equirectangular.png
22-
23-
equirectangular.png by <a href="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/WestLangley">WestLangley</a>
24-
licensed under <a href="https://github.com/mrdoob/three.js/blob/dev/LICENSE">MIT</a>
25-
(<a href="https://github.com/mrdoob/three.js/blob/dev/examples/textures/equirectangular.png">source</a>)
26-
2721
## Shishkebab.*
2822

2923
Shishkebab by <a href="https://poly.google.com/user/4aEd8rQgKu2">Poly</a>,
3024
licensed under <a href="https://creativecommons.org/licenses/by/2.0/">CC-BY</a>
3125
(<a href="https://poly.google.com/view/6uTsH2jqgVn">source</a>)
3226

33-
# whipple_creek_regional_park_04
27+
## whipple_creek_regional_park_04
3428

3529
whipple_creek_regional_park_04_1k.jpg by <a href="https://hdrihaven.com">HDRI Haven</a>
3630
licensed under <a href="https://hdrihaven.com/p/license.php">CC0</a>
3731
(<a href="https://hdrihaven.com/hdri/?h=whipple_creek_regional_park_04">source</a>)
3832
Originally in HDR format
3933

40-
# small_hangar_01
34+
## small_hangar_01
4135

4236
small_hangar_01_1k.jpg by <a href="https://hdrihaven.com">HDRI Haven</a>
4337
licensed under <a href="https://hdrihaven.com/p/license.php">CC0</a>
4438
(<a href="https://hdrihaven.com/hdri/?h=small_hangar_01">source</a>)
4539
Originally in HDR format
4640

47-
## cube.gltf, offcenter-cube.gltf, pbr-spheres.glb, reflective-sphere.gltf
41+
## spruit_sunrise_2k
42+
43+
spruit_sunrise_2k.hdr, spruit_sunrise_2k.jpg by <a href="https://hdrihaven.com">HDRI Haven</a>
44+
licensed under <a href="https://hdrihaven.com/p/license.php">CC0</a>
45+
(<a href="https://hdrihaven.com/hdri/?c=outdoor&h=spruit_sunrise">source</a>)
46+
47+
## cube.gltf, offcenter-cube.gltf, pbr-spheres.glb, radiance.glb, reflective-sphere.gltf
4848

4949
Contributed by <a href="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/jsantell">jsantell</a>
-643 KB
Binary file not shown.

examples/assets/radiance.glb

467 KB
Binary file not shown.
5.66 MB
Binary file not shown.
668 KB
Loading

examples/background-image.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,22 @@ <h2>With Equirectangular background-image</h2>
6262
</template>
6363
</demo-snippet>
6464

65+
<demo-snippet>
66+
<h2>JPG vs HDR</h2>
67+
<template>
68+
<model-viewer
69+
controls
70+
background-image="assets/spruit_sunrise_2k.jpg"
71+
src="assets/radiance.glb">
72+
</model-viewer>
73+
<model-viewer
74+
controls
75+
background-image="assets/spruit_sunrise_2k.hdr"
76+
src="assets/radiance.glb">
77+
</model-viewer>
78+
</template>
79+
</demo-snippet>
80+
6581
<demo-snippet>
6682
<h2>With very reflective model</h2>
6783
<template>
@@ -104,7 +120,7 @@ <h2>Cycling background image</h2>
104120
<template>
105121
<model-viewer
106122
id="toggle-image"
107-
backgroud-color="#ff0077"
123+
background-color="#ff0077"
108124
src="assets/DamagedHelmet/DamagedHelmet.gltf"
109125
alt="A 3D model of a damaged helmet depicted within changing environments"
110126
controls auto-rotate>

src/features/environment.js

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,27 @@
1313
* limitations under the License.
1414
*/
1515

16-
import {Color} from 'three';
16+
import {BackSide, BoxBufferGeometry, Color, Mesh, ShaderLib, ShaderMaterial, UniformsUtils} from 'three';
1717

1818
import {$needsRender, $onModelLoad, $renderer, $scene, $tick} from '../model-viewer-base.js';
19+
1920
const DEFAULT_BACKGROUND_COLOR = '#ffffff';
20-
const GAMMA_TO_LINEAR = 2.2;
2121

2222
const WHITE = new Color('#ffffff');
2323

24-
const $currentCubemap = Symbol('currentCubemap');
24+
const $currentEnvMap = Symbol('currentEnvMap');
2525
const $setEnvironmentImage = Symbol('setEnvironmentImage');
2626
const $setEnvironmentColor = Symbol('setEnvironmentColor');
2727
const $setShadowLightColor = Symbol('setShadowLightColor');
2828
const $hasBackgroundImage = Symbol('hasBackgroundImage');
2929
const $hasBackgroundColor = Symbol('hasBackgroundColor');
3030
const $deallocateTextures = Symbol('deallocateTextures');
3131

32+
// TODO remove in #196
33+
const $setBackground = Symbol('setBackground');
34+
const $backgroundMesh = Symbol('backgroundMesh');
35+
const $activeBackground = Symbol('activeBackground');
36+
3237
export const EnvironmentMixin = (ModelViewerElement) => {
3338
return class extends ModelViewerElement {
3439
static get properties() {
@@ -75,17 +80,11 @@ export const EnvironmentMixin = (ModelViewerElement) => {
7580
}
7681
}
7782

78-
[$tick](time, delta) {
79-
super[$tick](time, delta);
80-
const camera = this[$scene].getCamera();
81-
this[$scene].skysphere.position.copy(camera.position);
82-
}
83-
8483
[$onModelLoad](e) {
8584
super[$onModelLoad](e);
8685

87-
if (this[$currentCubemap]) {
88-
this[$scene].model.applyEnvironmentMap(this[$currentCubemap]);
86+
if (this[$currentEnvMap]) {
87+
this[$scene].model.applyEnvironmentMap(this[$currentEnvMap]);
8988
this[$needsRender]();
9089
}
9190
}
@@ -95,7 +94,7 @@ export const EnvironmentMixin = (ModelViewerElement) => {
9594
*/
9695
async[$setEnvironmentImage](url) {
9796
const textureUtils = this[$renderer].textureUtils;
98-
const textures = await textureUtils.toCubemapAndEquirect(url);
97+
const textures = await textureUtils.generateEnvironmentTextures(url);
9998

10099
// If the background image has changed
101100
// while fetching textures, abort and defer to that
@@ -113,13 +112,13 @@ export const EnvironmentMixin = (ModelViewerElement) => {
113112
return;
114113
}
115114

116-
const {cubemap, equirect} = textures;
115+
const {skybox, envmap} = textures;
117116

118-
this[$scene].skysphere.material.color = new Color(0xffffff);
119-
this[$scene].skysphere.material.map = equirect;
120-
this[$scene].skysphere.material.needsUpdate = true;
121-
this[$currentCubemap] = cubemap;
122-
this[$scene].model.applyEnvironmentMap(cubemap);
117+
// TODO #196
118+
this[$setBackground](skybox);
119+
120+
this[$currentEnvMap] = envmap;
121+
this[$scene].model.applyEnvironmentMap(envmap);
123122

124123
this[$setShadowLightColor](WHITE);
125124

@@ -134,35 +133,97 @@ export const EnvironmentMixin = (ModelViewerElement) => {
134133

135134
this[$deallocateTextures]();
136135

137-
const skysphereColor = this[$scene].skysphere.material.color =
138-
new Color(color);
139-
skysphereColor.convertGammaToLinear(GAMMA_TO_LINEAR);
140-
this[$setShadowLightColor](skysphereColor);
136+
const parsedColor = new Color(color);
137+
138+
// TODO #196
139+
this[$setBackground](parsedColor);
141140

142-
this[$scene].skysphere.material.map = null;
143-
this[$scene].skysphere.material.needsUpdate = true;
141+
this[$setShadowLightColor](parsedColor);
144142

145143
// TODO can cache this per renderer and color
146-
const cubemap = textureUtils.generateDefaultEnvMap();
147-
this[$currentCubemap] = cubemap;
148-
this[$scene].model.applyEnvironmentMap(this[$currentCubemap]);
144+
const envmap = textureUtils.generateDefaultEnvMap();
145+
this[$currentEnvMap] = envmap;
146+
this[$scene].model.applyEnvironmentMap(this[$currentEnvMap]);
149147

150148
this[$needsRender]();
151149
}
152150

151+
/**
152+
* Work around for #196, can remove this function
153+
* and set backgrounds on `scene.background` directly
154+
* once upgraded to r100. Most of the logic from
155+
* three's WebGLBackground for WebGLRenderTargetCubes.
156+
*/
157+
[$setBackground](background) {
158+
// We only support cube maps and colors for backgrounds
159+
const isCube = !!(background && background.isWebGLRenderTargetCube);
160+
161+
if (isCube && !this[$backgroundMesh]) {
162+
const mesh = this[$backgroundMesh] =
163+
new Mesh(new BoxBufferGeometry(1, 1, 1), new ShaderMaterial({
164+
type: 'BackgroundCubeMaterial',
165+
uniforms: UniformsUtils.clone(ShaderLib.cube.uniforms),
166+
vertexShader: ShaderLib.cube.vertexShader,
167+
fragmentShader: ShaderLib.cube.fragmentShader,
168+
side: BackSide,
169+
depthTest: false,
170+
depthWrite: false,
171+
fog: false
172+
}));
173+
174+
mesh.name = 'SceneBackgroundHack';
175+
mesh.geometry.removeAttribute('normal');
176+
mesh.geometry.removeAttribute('uv');
177+
178+
mesh.onBeforeRender = function(renderer, scene, camera) {
179+
this.matrixWorld.copyPosition(camera.matrixWorld);
180+
};
181+
182+
// enable code injection for non-built-in material
183+
Object.defineProperty(mesh.material, 'map', {
184+
get: function() {
185+
return this.uniforms.tCube.value;
186+
}
187+
});
188+
189+
// WebGLBackground can manually place the background to render
190+
// first -- we don't have access to that here -- set the render order.
191+
mesh.renderOrder = 1;
192+
}
193+
194+
if (isCube) {
195+
this[$backgroundMesh].material.uniforms.tCube.value =
196+
background.texture;
197+
this[$backgroundMesh].material.uniforms.tFlip.value = 1;
198+
this[$backgroundMesh].material.needsUpdate = true;
199+
this[$scene].add(this[$backgroundMesh]);
200+
// Remove any color background
201+
this[$scene].background = null;
202+
// scene.background expects a WebGLRenderTargetCube
203+
this[$activeBackground] = background;
204+
} else {
205+
// `scene.background` works for colors
206+
this[$scene].background = background;
207+
this[$scene].remove(this[$backgroundMesh]);
208+
this[$activeBackground] = background;
209+
}
210+
}
211+
153212
[$setShadowLightColor](color) {
154213
this[$scene].shadowLight.color.copy(color);
155214
this[$scene].shadowLight.color.lerpHSL(WHITE, 0.5);
156215
}
157216

158217
[$deallocateTextures]() {
159-
if (this[$scene].skysphere.material.map) {
160-
this[$scene].skysphere.material.map.dispose();
161-
this[$scene].skysphere.material.map = null;
218+
// TODO Re-enable in #196
219+
// const background = this[$scene].background;
220+
const background = this[$activeBackground];
221+
if (background && background.dispose) {
222+
background.dispose();
162223
}
163-
if (this[$currentCubemap]) {
164-
this[$currentCubemap].dispose();
165-
this[$currentCubemap] = null;
224+
if (this[$currentEnvMap]) {
225+
this[$currentEnvMap].dispose();
226+
this[$currentEnvMap] = null;
166227
}
167228
}
168229
}

0 commit comments

Comments
 (0)