<!DOCTYPE html> <html> <head> <title>Comp 86 example: Stereo 1</title> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.156.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.156.0/examples/jsm/" } } </script> <script type="module"> /* * Example: Stereo, homemade version, without library * * Uses 2 cameras but 1 scene * Crude approach, camera geometry may be slightly off, * built in library version does it better. */ /* * Same scene as previous code */ class SceneGraph extends THREE.Scene { constructor () { super () // First item = a box, rotated and translated let box = new THREE.Mesh ( new THREE.BoxGeometry (1, 3, 1), new THREE.MeshPhongMaterial ({ color: "yellow" })) // Move it back from origin and to the right box.position.set (5, 0, -4) // Rotate it in X and Y box.rotation.set (45 * Math.PI/180, 30 * Math.PI/180, 0) // Plug it in to our scene this.add (box) // A sphere, in a different location let sphere = new THREE.Mesh ( new THREE.SphereGeometry(1, 32, 32), new THREE.MeshPhongMaterial ({ color: "red", shininess: 60 })) sphere.position.set (-5, 0, -3) this.add (sphere) // A pyramid, using our subroutine below let pyramid = new Pyramid (new THREE.MeshPhongMaterial ({ color: "green", shininess: 60 })) pyramid.position.set (0, 0, -6) pyramid.rotation.set (0, 45 * Math.PI/180, 0) this.add (pyramid) } } /* * Same as previous example */ class Pyramid extends THREE.Object3D { constructor (material) { super () // Bottom Box, located slightly down // (relative to origin of the pyramid) let bot = new THREE.Mesh ( new THREE.BoxGeometry (3, 3, 3), material) bot.position.set (0, -3/2, 0) this.add (bot) // Middle Box, slightly up let mid = new THREE.Mesh ( new THREE.BoxGeometry (2, 2, 2), material) mid.position.set (0, 2/2, 0) this.add (mid) // Top box, further up let top = new THREE.Mesh ( new THREE.BoxGeometry (1, 1, 1), material) top.position.set (0, 2 + 1/2, 0) this.add (top) } } /* * An object to hold the lights, same as previous */ class Lights { constructor (scene) { // Uses typical lighting setup, like portrait or TV studio... // Main (key) light, directional, // from 45 deg. user's right, above, bright white this.mainLight = new THREE.DirectionalLight ("white", 1 * 2*Math.PI) this.mainLight.position.set (1, 0.5, 1) scene.add (this.mainLight) // Fill light, directional, from 45deg. user's left, // white, half as bright this.fillLight = new THREE.DirectionalLight ("white", 0.5 * 2*Math.PI) this.fillLight.position.set (-1, 0, 1 ) scene.add (this.fillLight) // Ambient light, white, still less bright this.ambientLight = new THREE.AmbientLight ("white", 0.05 * 2*Math.PI) scene.add (this.ambientLight); } } /* * New version of usual boilerplate, to handle stereo */ import * as THREE from "three"; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; class Main { constructor () { this.eyeOffset = 20.5/12 // ie sort of as if WC units = feet this.renderer1 = new THREE.WebGLRenderer( { antialias: true } ); //*1 2 renderers this.renderer1.setClearColor( new THREE.Color ("lightgrey")) this.renderer2 = new THREE.WebGLRenderer( { antialias: true } ); //*1 this.renderer2.setClearColor( new THREE.Color ("lightgrey")) this.size = window.innerWidth/2 - 50 this.renderer1.setSize( this.size, this.size ); this.renderer2.setSize( this.size, this.size ); document.body.appendChild( this.renderer1.domElement ); //*1 document.body.appendChild( this.renderer2.domElement ); //*1 this.renderer1.domElement.style = "border-style: solid; border-color: black; border-width: 1px"; this.renderer2.domElement.style = "border-style: solid; border-color: black; border-width: 1px"; // Create the scene this.scene = new SceneGraph(); // Add our lights to the scene, we keep in a separate class new Lights(this.scene); // Put 2 cameras into the scene this.camera1 = new THREE.PerspectiveCamera( 60, 1, 1, 1000 ); //*2 2 cameras this.camera2 = new THREE.PerspectiveCamera( 60, 1, 1, 1000 ); //*2 this.camera1.position.set(0, 0, 5); //*2 this.camera2.position.set(this.eyeOffset, 0, 5); //*2 let direction = new THREE.Vector3() this.camera1.getWorldDirection(direction) this.camera2.lookAt (direction) // Create one camera (ie head) contol new OrbitControls( this.camera1, this.renderer1.domElement ); // Start animation loop this.animate() // In case window is resized window.onresize = () => this.onResize() } // Animation loop animate() { //*3 camera2 position = camera1 + eye offset requestAnimationFrame (() => this.animate()); // Take the latest camera1 position and set camera2 based on it this.camera2.position.set (this.camera1.position.x + this.eyeOffset, this.camera1.position.y, this.camera1.position.z) //*3 let dir = new THREE.Vector3() this.camera1.getWorldDirection(dir) this.camera2.lookAt (dir) this.render() } // Render the scene on both cameras render() { //*4 Render same scene on both cameras this.renderer1.render( this.scene, this.camera1 ); //*4 this.renderer2.render( this.scene, this.camera2 ); //*4 } // In case window is resized onResize () { this.renderer1.setSize( this.size, this.size ); this.renderer2.setSize( this.size, this.size ); this.camera1.aspect = 1 this.camera2.aspect = 1 this.camera1.updateProjectionMatrix(); this.camera2.updateProjectionMatrix(); this.render(); } } window.onload = () => new Main () </script> </head> </html>