UNPKG

aframe

Version:

A web framework for building virtual reality experiences.

164 lines (144 loc) 5.67 kB
/* global XRWebGLBinding */ import * as THREE from 'three'; import { registerComponent as register } from '../../core/component.js'; // source: view-source:https://storage.googleapis.com/chromium-webxr-test/r886480/proposals/lighting-estimation.html function updateLights (estimate, probeLight, directionalLight, directionalLightPosition) { var intensityScalar = Math.max(estimate.primaryLightIntensity.x, Math.max(estimate.primaryLightIntensity.y, estimate.primaryLightIntensity.z)); probeLight.sh.fromArray(estimate.sphericalHarmonicsCoefficients); probeLight.intensity = 3.14; if (directionalLight) { directionalLight.color.setRGB( estimate.primaryLightIntensity.x / intensityScalar, estimate.primaryLightIntensity.y / intensityScalar, estimate.primaryLightIntensity.z / intensityScalar); directionalLight.intensity = intensityScalar; directionalLightPosition.copy(estimate.primaryLightDirection); } } export var Component = register('reflection', { schema: { directionalLight: { type: 'selector' } }, sceneOnly: true, init: function () { var self = this; this.cubeRenderTarget = new THREE.WebGLCubeRenderTarget(16); this.cubeCamera = new THREE.CubeCamera(0.1, 1000, this.cubeRenderTarget); this.lightingEstimationTexture = (new THREE.WebGLCubeRenderTarget(16)).texture; this.needsVREnvironmentUpdate = true; // Update WebXR to support light-estimation var webxrData = this.el.getAttribute('webxr'); var optionalFeaturesArray = webxrData.optionalFeatures; if (!optionalFeaturesArray.includes('light-estimation')) { optionalFeaturesArray.push('light-estimation'); this.el.setAttribute('webxr', webxrData); } this.el.addEventListener('enter-vr', function () { if (!self.el.is('ar-mode')) { return; } var renderer = self.el.renderer; var session = renderer.xr.getSession(); if (session.requestLightProbe) { self.startLightProbe(); } }); this.el.addEventListener('exit-vr', function () { if (self.xrLightProbe) { self.stopLightProbe(); } }); this.el.object3D.environment = this.cubeRenderTarget.texture; }, stopLightProbe: function () { this.xrLightProbe = null; if (this.probeLight) { this.probeLight.components.light.light.intensity = 0; } this.needsVREnvironmentUpdate = true; this.el.object3D.environment = this.cubeRenderTarget.texture; }, startLightProbe: function () { this.needsLightProbeUpdate = true; }, setupLightProbe: function () { var renderer = this.el.renderer; var xrSession = renderer.xr.getSession(); var self = this; var gl = renderer.getContext(); if (!this.probeLight) { var probeLight = document.createElement('a-light'); probeLight.setAttribute('type', 'probe'); probeLight.setAttribute('intensity', 0); this.el.appendChild(probeLight); this.probeLight = probeLight; } // Ensure that we have any extensions needed to use the preferred cube map format. switch (xrSession.preferredReflectionFormat) { case 'srgba8': gl.getExtension('EXT_sRGB'); break; case 'rgba16f': gl.getExtension('OES_texture_half_float'); break; } this.glBinding = new XRWebGLBinding(xrSession, gl); gl.getExtension('EXT_sRGB'); gl.getExtension('OES_texture_half_float'); xrSession.requestLightProbe() .then(function (lightProbe) { self.xrLightProbe = lightProbe; lightProbe.addEventListener('reflectionchange', self.updateXRCubeMap.bind(self)); }) .catch(function (err) { console.warn('Lighting estimation not supported: ' + err.message); console.warn('Are you missing: webxr="optionalFeatures: light-estimation;" from <a-scene>?'); }); }, updateXRCubeMap: function () { // Update Cube Map, cubeMap maybe some unavailable on some hardware var renderer = this.el.renderer; var cubeMap = this.glBinding.getReflectionCubeMap(this.xrLightProbe); if (cubeMap) { var rendererProps = renderer.properties.get(this.lightingEstimationTexture); rendererProps.__webglTexture = cubeMap; this.lightingEstimationTexture.needsPMREMUpdate = true; this.el.object3D.environment = this.lightingEstimationTexture; } }, tick: function () { var scene = this.el.object3D; var renderer = this.el.renderer; var frame = this.el.frame; if (frame && this.xrLightProbe) { // light estimate may not yet be available, it takes a few frames to start working var estimate = frame.getLightEstimate(this.xrLightProbe); if (estimate) { updateLights( estimate, this.probeLight.components.light.light, this.data.directionalLight && this.data.directionalLight.components.light.light, this.data.directionalLight && this.data.directionalLight.object3D.position ); } } if (this.needsVREnvironmentUpdate) { scene.environment = null; this.needsVREnvironmentUpdate = false; this.cubeCamera.position.set(0, 1.6, 0); this.cubeCamera.update(renderer, scene); scene.environment = this.cubeRenderTarget.texture; } if (this.needsLightProbeUpdate && frame) { // wait until the XR Session has started before trying to make // the light probe this.setupLightProbe(); this.needsLightProbeUpdate = false; } }, remove: function () { this.el.object3D.environment = null; if (this.probeLight) { this.el.removeChild(this.probeLight); } } });