<!DOCTYPE html> <html> <head> <title>Comp 86 example: Lights</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"> /* * Same as previous example, except * this time we provide materials. */ 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" })) //*3 Give materials to your facets // 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 ({ //*3 color: "red", shininess: 60 })) //*3 sphere.position.set (-5, 0, -3) this.add (sphere) // A pyramid, using our subroutine below let pyramid = new Pyramid (new THREE.MeshPhongMaterial ({ //*3 color: "green", shininess: 60 })) //*3 pyramid.position.set (0, 0, -6) pyramid.rotation.set (0, 45 * Math.PI/180, 0) this.add (pyramid) } } /* * Same as previous example, except * this time, you pass us the material you want. */ 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, * constructor just adds them to the given scene */ class Lights { //*1 Define your light(s) constructor (scene) { //*2 Add to scene graph // 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) //*1 this.mainLight.position.set (1, 0.5, 1) //*1 scene.add (this.mainLight) //*2 // Fill light, directional, from 45deg. user's left, // white, half as bright this.fillLight = new THREE.DirectionalLight ("white", 0.5 * 2*Math.PI) //*1 this.fillLight.position.set (-1, 0, 1 ) //*1 scene.add (this.fillLight) //*2 // Ambient light, white, still less bright this.ambientLight = new THREE.AmbientLight ("white", 0.05 * 2*Math.PI) //*1 scene.add (this.ambientLight); //*2 // Callbacks from HTML document.getElementById("mainLight").addEventListener('click', //*4 Callbacks from HTML checkboxes (event) => this.mainLight.visible = event.target.checked ) //*4 document.getElementById("fillLight").addEventListener('click', //*4 (event) => this.fillLight.visible = event.target.checked ) //*4 document.getElementById("ambientLight").addEventListener('click', //*4 (event) => this.ambientLight.visible = event.target.checked ) //*4 } } /* * The rest of this is the same boilerplate as previously, * except we also add the lights */ import * as THREE from "three"; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; class Main { constructor () { // Set up window for 3D this.renderer = new THREE.WebGLRenderer( { antialias: true } ); this.renderer.setClearColor( new THREE.Color ("lightgrey")) this.renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( this.renderer.domElement ); // Create our scene this.scene = new SceneGraph(); // Add our lights to the scene, we keep in a separate class new Lights(this.scene); // Create the camera this.camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 ); this.camera.position.z = 5; // Create a camera contol new OrbitControls( this.camera, this.renderer.domElement ); // Start animation loop this.animate() // In case window is resized // use () form of function definition cause need "this" // but don't need "event" arg window.onresize = () => this.onResize() } /* * Render the scene */ render() { this.renderer.render( this.scene, this.camera ); } /* * Animation loop */ animate () { requestAnimationFrame (() => {this.animate()}); this.render() } // In case window is resized onResize () { this.renderer.setSize( window.innerWidth, window.innerHeight ); this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.render() } } window.onload = () => new Main () </script> </head> <body> Main light: <input type=checkbox id="mainLight" checked="checked"> Fill light: <input type=checkbox id="fillLight" checked="checked"> Ambient light: <input type=checkbox id="ambientLight" checked="checked"> <br/> </body> </html>