lights: lights.html

<!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">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 

Fill light: <input type=checkbox id="fillLight" checked="checked">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;

Ambient light: <input type=checkbox id="ambientLight" checked="checked">
<br/>

</body>
</html>
[download file]