three
Version:
JavaScript 3D library
240 lines (170 loc) • 6.37 kB
JavaScript
import { WebGLCoordinateSystem, WebGPUCoordinateSystem } from '../constants.js';
import { Object3D } from '../core/Object3D.js';
import { PerspectiveCamera } from './PerspectiveCamera.js';
const fov = - 90; // negative fov is not an error
const aspect = 1;
/**
* A special type of camera that is positioned in 3D space to render its surroundings into a
* cube render target. The render target can then be used as an environment map for rendering
* realtime reflections in your scene.
*
* ```js
* // Create cube render target
* const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 256, { generateMipmaps: true, minFilter: THREE.LinearMipmapLinearFilter } );
*
* // Create cube camera
* const cubeCamera = new THREE.CubeCamera( 1, 100000, cubeRenderTarget );
* scene.add( cubeCamera );
*
* // Create car
* const chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeRenderTarget.texture } );
* const car = new THREE.Mesh( carGeometry, chromeMaterial );
* scene.add( car );
*
* // Update the render target cube
* car.visible = false;
* cubeCamera.position.copy( car.position );
* cubeCamera.update( renderer, scene );
*
* // Render the scene
* car.visible = true;
* renderer.render( scene, camera );
* ```
*
* @augments Object3D
*/
class CubeCamera extends Object3D {
/**
* Constructs a new cube camera.
*
* @param {number} near - The camera's near plane.
* @param {number} far - The camera's far plane.
* @param {WebGLCubeRenderTarget} renderTarget - The cube render target.
*/
constructor( near, far, renderTarget ) {
super();
this.type = 'CubeCamera';
/**
* A reference to the cube render target.
*
* @type {WebGLCubeRenderTarget}
*/
this.renderTarget = renderTarget;
/**
* The current active coordinate system.
*
* @type {?(WebGLCoordinateSystem|WebGPUCoordinateSystem)}
* @default null
*/
this.coordinateSystem = null;
/**
* The current active mipmap level
*
* @type {number}
* @default 0
*/
this.activeMipmapLevel = 0;
const cameraPX = new PerspectiveCamera( fov, aspect, near, far );
cameraPX.layers = this.layers;
this.add( cameraPX );
const cameraNX = new PerspectiveCamera( fov, aspect, near, far );
cameraNX.layers = this.layers;
this.add( cameraNX );
const cameraPY = new PerspectiveCamera( fov, aspect, near, far );
cameraPY.layers = this.layers;
this.add( cameraPY );
const cameraNY = new PerspectiveCamera( fov, aspect, near, far );
cameraNY.layers = this.layers;
this.add( cameraNY );
const cameraPZ = new PerspectiveCamera( fov, aspect, near, far );
cameraPZ.layers = this.layers;
this.add( cameraPZ );
const cameraNZ = new PerspectiveCamera( fov, aspect, near, far );
cameraNZ.layers = this.layers;
this.add( cameraNZ );
}
/**
* Must be called when the coordinate system of the cube camera is changed.
*/
updateCoordinateSystem() {
const coordinateSystem = this.coordinateSystem;
const cameras = this.children.concat();
const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = cameras;
for ( const camera of cameras ) this.remove( camera );
if ( coordinateSystem === WebGLCoordinateSystem ) {
cameraPX.up.set( 0, 1, 0 );
cameraPX.lookAt( 1, 0, 0 );
cameraNX.up.set( 0, 1, 0 );
cameraNX.lookAt( - 1, 0, 0 );
cameraPY.up.set( 0, 0, - 1 );
cameraPY.lookAt( 0, 1, 0 );
cameraNY.up.set( 0, 0, 1 );
cameraNY.lookAt( 0, - 1, 0 );
cameraPZ.up.set( 0, 1, 0 );
cameraPZ.lookAt( 0, 0, 1 );
cameraNZ.up.set( 0, 1, 0 );
cameraNZ.lookAt( 0, 0, - 1 );
} else if ( coordinateSystem === WebGPUCoordinateSystem ) {
cameraPX.up.set( 0, - 1, 0 );
cameraPX.lookAt( - 1, 0, 0 );
cameraNX.up.set( 0, - 1, 0 );
cameraNX.lookAt( 1, 0, 0 );
cameraPY.up.set( 0, 0, 1 );
cameraPY.lookAt( 0, 1, 0 );
cameraNY.up.set( 0, 0, - 1 );
cameraNY.lookAt( 0, - 1, 0 );
cameraPZ.up.set( 0, - 1, 0 );
cameraPZ.lookAt( 0, 0, 1 );
cameraNZ.up.set( 0, - 1, 0 );
cameraNZ.lookAt( 0, 0, - 1 );
} else {
throw new Error( 'THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: ' + coordinateSystem );
}
for ( const camera of cameras ) {
this.add( camera );
camera.updateMatrixWorld();
}
}
/**
* Calling this method will render the given scene with the given renderer
* into the cube render target of the camera.
*
* @param {(Renderer|WebGLRenderer)} renderer - The renderer.
* @param {Scene} scene - The scene to render.
*/
update( renderer, scene ) {
if ( this.parent === null ) this.updateMatrixWorld();
const { renderTarget, activeMipmapLevel } = this;
if ( this.coordinateSystem !== renderer.coordinateSystem ) {
this.coordinateSystem = renderer.coordinateSystem;
this.updateCoordinateSystem();
}
const [ cameraPX, cameraNX, cameraPY, cameraNY, cameraPZ, cameraNZ ] = this.children;
const currentRenderTarget = renderer.getRenderTarget();
const currentActiveCubeFace = renderer.getActiveCubeFace();
const currentActiveMipmapLevel = renderer.getActiveMipmapLevel();
const currentXrEnabled = renderer.xr.enabled;
renderer.xr.enabled = false;
const generateMipmaps = renderTarget.texture.generateMipmaps;
renderTarget.texture.generateMipmaps = false;
renderer.setRenderTarget( renderTarget, 0, activeMipmapLevel );
renderer.render( scene, cameraPX );
renderer.setRenderTarget( renderTarget, 1, activeMipmapLevel );
renderer.render( scene, cameraNX );
renderer.setRenderTarget( renderTarget, 2, activeMipmapLevel );
renderer.render( scene, cameraPY );
renderer.setRenderTarget( renderTarget, 3, activeMipmapLevel );
renderer.render( scene, cameraNY );
renderer.setRenderTarget( renderTarget, 4, activeMipmapLevel );
renderer.render( scene, cameraPZ );
// mipmaps are generated during the last call of render()
// at this point, all sides of the cube render target are defined
renderTarget.texture.generateMipmaps = generateMipmaps;
renderer.setRenderTarget( renderTarget, 5, activeMipmapLevel );
renderer.render( scene, cameraNZ );
renderer.setRenderTarget( currentRenderTarget, currentActiveCubeFace, currentActiveMipmapLevel );
renderer.xr.enabled = currentXrEnabled;
renderTarget.texture.needsPMREMUpdate = true;
}
}
export { CubeCamera };