Use the arrows keys to move, A and S to rotate. Adjust the lighting with the controls at the top right. Is that back wall really real?
<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
overflow: hidden;
}
#loading {
font-family: "Helvetica Neue", sans-serif;
position: absolute;
top: 33%;
text-align: center;
width: 100%;
}
</style>
</head>
<body>
<div id="scene"></div>
<div id="stats"></div>
<div id="loading">Loading (<span class="pct">0</span>%)</div>
<script src="https://unpkg.com/three@0.105.2/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.105.2/examples/js/libs/stats.min.js"></script>
<script src="https://unpkg.com/three@0.105.2/examples/js/loaders/OBJLoader.js"></script>
<script src="https://unpkg.com/three@0.105.2/examples/js/libs/dat.gui.min.js"></script>
<script>
let left = 0, right = 0, up = 0, down = 0, a = 0, s = 0;
const controls = { lightIntensity: 1.75, lightDistance: 22, lightColor: "#fff" };
const gui = (_ => {
const gui = new dat.GUI();
gui.add(controls, "lightIntensity", 0, 3);
gui.add(controls, "lightDistance", 1, 100);
gui.addColor(controls, "lightColor").onChange(e => {
pointLight.color = new THREE.Color(e);
});
return gui;
})();
const stats = (_ => {
const stats = new Stats();
stats.domElement.style.position = "absolute";
stats.domElement.style.left = "0px";
stats.domElement.style.top = "0px";
document.getElementById("stats").appendChild(stats.domElement);
return stats;
})();
const scene = new THREE.Scene();
const camera = (_ => {
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 0.1, 1000);
camera.position.set(0, 7.5, 30);
camera.lookAt(0, 7.5, 0);
return camera;
})();
const renderer = (_ => {
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setClearColor("#000");
renderer.setPixelRatio(devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
document.getElementById("scene").appendChild(renderer.domElement);
return renderer;
})();
const floor = (_ => {
const geo = new THREE.PlaneBufferGeometry(20, 20);
const mat = new THREE.MeshPhongMaterial({ shininess: 10, color: "#fff" });
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.x = Math.PI * -.5;
mesh.receiveShadow = true;
scene.add(mesh);
return mesh;
})();
const leftWall = (_ => {
const geo = new THREE.PlaneBufferGeometry(20, 15);
const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.y = Math.PI * 0.5;
mesh.position.set(-10, 7.5, 0);
mesh.receiveShadow = true;
scene.add(mesh);
return mesh;
})();
const rightWall = (_ => {
const geo = new THREE.PlaneBufferGeometry(20, 15);
const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.y = Math.PI * -0.5;
mesh.position.set(10, 7.5, 0);
mesh.receiveShadow = true;
scene.add(mesh);
return mesh;
})();
const backWall = (_ => {
const geo = new THREE.PlaneBufferGeometry(15, 20);
const mat = new THREE.MeshPhongMaterial({ shininess: 1, color: "#fff" });
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.z = Math.PI * -0.5;
mesh.position.set(0, 7.5, -10);
mesh.receiveShadow = true;
scene.add(mesh);
return mesh;
})();
const ceiling = (_ => {
const geo = new THREE.PlaneBufferGeometry(20, 20);
const mat = new THREE.MeshPhongMaterial({ color: "#fff" });
const mesh = new THREE.Mesh(geo, mat);
mesh.rotation.x = Math.PI * 0.5;
mesh.position.set(0, 15, 0);
mesh.receiveShadow = true;
scene.add(mesh);
return mesh;
})();
const lightPanel = (_ => {
const geo = new THREE.CylinderBufferGeometry(2.5, 2.5, 1, 64);
const mat = new THREE.MeshBasicMaterial()
const mesh = new THREE.Mesh(geo, mat);
mesh.position.set(0, 15, 3);
scene.add(mesh);
return mesh;
})();
const pointLight = (_ => {
const light = new THREE.PointLight(controls.lightColor, controls.lightIntensity);
light.position.set(0, 13, 3);
light.distance = controls.lightDistance;
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
scene.add(light);
return light;
})();
const ambientLight = (_ => {
const light = new THREE.AmbientLight("#0c0c0c");
scene.add(light);
return light;
})();
document.addEventListener("keydown", e => {
if (e.key === "ArrowLeft"){
left = 1;
e.preventDefault();
}
if (e.key === "ArrowRight"){
right = 1;
e.preventDefault();
}
if (e.key === "ArrowUp"){
up = 1;
e.preventDefault();
}
if (e.key === "ArrowDown"){
down = 1;
e.preventDefault();
}
if (e.key === "a"){
a = 1;
e.preventDefault();
}
if (e.key === "s"){
s = 1;
e.preventDefault();
}
});
document.addEventListener("keyup", e => {
if (e.key === "ArrowLeft"){
left = 0;
e.preventDefault();
}
if (e.key === "ArrowRight"){
right = 0;
e.preventDefault();
}
if (e.key === "ArrowUp"){
up = 0;
e.preventDefault();
}
if (e.key === "ArrowDown"){
down = 0;
e.preventDefault();
}
if (e.key === "a"){
a = 0;
e.preventDefault();
}
if (e.key === "s"){
s = 0;
e.preventDefault();
}
});
const loader = new THREE.OBJLoader();
// load a resource
loader.load(
// resource URL
"JulianHead_at4inches.obj",
// called when resource is loaded
object => {
const element = document.getElementById("loading");
element.parentNode.removeChild(element);
object.name = "Julian";
object.children = object.children.filter((f, i) => i === 4);
object.children[0].material = new THREE.MeshStandardMaterial({
color: "#ffe685",
metalness: 1,
roughness: .5
});
object.children[0].castShadow = true;
object.position.set(-1, 0, -6);
object.rotation.x = -.5 * Math.PI;
object.rotation.z = .05;
object.scale.set(2.5, 2.5, 2.5);
scene.add(object);
},
// called when loading is in progresses
xhr => {
document.querySelector(".pct").innerHTML = (xhr.loaded / xhr.total * 100).toFixed(1);
},
// called when loading has errors
error => {
console.log("An error happened.");
}
);
function julian(){
return scene.getObjectByName("Julian", true);
}
function animate(){
requestAnimationFrame(animate);
pointLight.intensity = controls.lightIntensity;
pointLight.distance = controls.lightDistance;
if (left && julian().position.x > -4.8){
julian().position.x -= 0.1;
}
if (right && julian().position.x < 2.3){
julian().position.x += 0.1;
}
if (up && julian().position.z > -15){
julian().position.z -= 0.1;
}
if (down && julian().position.z < 7){
julian().position.z += 0.1;
}
if (a && julian()){
julian().rotation.z -= 0.025;
}
if (s && julian()){
julian().rotation.z += 0.025;
}
let newLightColor = {};
Object.keys(lightPanel.material.color).forEach(channel => {
newLightColor[channel] = new THREE.Color(controls.lightColor)[channel] * controls.lightIntensity;
});
lightPanel.material.color = newLightColor;
stats.update();
renderer.render(scene, camera);
}
animate();
function size(){
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
}
size();
onresize = size;
</script>
</body>
</html>