PROWAREtech
ThreeJS: A Survival Guide
This guide requires a basic understanding of JavaScript.
The THREE.js library used in the following examples is version 147 and may be downloaded here: THREEJS.zip. It is incompatible with Internet Explorer 11 (IE11).
This is a guide to the basics for getting started. For the THREE.js full documentation see the THREE.js site.
What is THREE.js
THREE.js is a WebGL plug-in based on JavaScript. It works straight from the browser and is used to power games, advertisements or any other GPU-powered apps. JavaScript is an interpreted language and as a result, THREE.js cannot perform as well as a language like C/C++ which is compiled and linked to a hardware platform.
The Basics of THREE.js
WebGL and THREE.js require at least one of the special <CANVAS>
HTML tags to be in the HTML document. The <CANVAS>
tag is used to draw graphics, on the fly, via JavaScript scripting.
THREE.js is based around a single scene which is a container with objects like a camera, planes, cubes, spheres, and much more... Every THREE.js application requires a renderer and an function that animates the scene, but lights are optional depending on whether or not the other objects in the scene reflect light (some objects do not reflect light and therefore do not need it to be seen). Every object has a mesh, which is simply a material associated with it.
Three dimensional means having or appearing to have length, breadth, and depth, so all objects on the scene have x, y and z positions as well as x, y and z rotations.
Here are all the steps, one by one, to creating a very basic THREE.js scene using JavaScript:
- Create the HTML document with at least one
<CANVAS>
tag (or create one dynamically). - Add the THREE.js JavaScript library to the HTML document before any code that requires it.
- Add a script to the HTML document.
- Create the scene container object which has a default background color of black.
- Set the scene background color (orange in the case of the following example).
- Create one or more objects with a surface material (mesh).
- Rotate/position the object(s) as needed for the scene (this can be done during the animation of the scene, or as the window is scrolled, which is covered near the end of this guide).
- Create a perspective camera with its field of view, aspect ratio, near and far frustum (how near and far the camera can see).
- Set the camera position in space and add it to the scene.
- Create the scene renderer passing it the canvas to use.
- Set the renderer's device pixel ratio which is available from the browser.
- Set the renderer's size which is based on the size of the canvas.
- Finally, create the animation function:
- Rotate and/or move any objects if so desired in the animation function.
- Call
renderer.render()
with the scene and camera as parameters. - Call the THREE.js
requestAnimationFrame()
function passing it the animation function as a parameter. - Call the animation function once to get it started.
Here is a full example of the above steps:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Basic Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
// NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var canvas = document.getElementsByTagName("canvas")[0];
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFF8000); // NOTE: color 0xFF8000 is the same as the CSS color #FF8000 which is simply orange in this case
var cubeSize = 2;
var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var materials = {}; // NOTE: use an object to store mesh objects
materials.white = new THREE.MeshBasicMaterial({ color: 0xFFFFFF });
materials.silver = new THREE.MeshBasicMaterial({ color: 0xCCCCCC });
materials.lightgray = new THREE.MeshBasicMaterial({ color: 0x999999 });
materials.gray = new THREE.MeshBasicMaterial({ color: 0x666666 });
materials.darkgray = new THREE.MeshBasicMaterial({ color: 0x333333 });
materials.black = new THREE.MeshBasicMaterial({ color: 0x000000 });
var cubeSides = [];
cubeSides.push(materials.white); // NOTE: right side of cube
cubeSides.push(materials.silver); // NOTE: left side of cube
cubeSides.push(materials.lightgray); // NOTE: top of cube
cubeSides.push(materials.gray); // NOTE: bottom of cube
cubeSides.push(materials.darkgray); // NOTE: front of the cube
cubeSides.push(materials.black); // NOTE: back of cube
var cube = new THREE.Mesh(cubeGeometry, cubeSides);
scene.add(cube);
var fieldOfView = 75; // NOTE: this is in degrees and is the vertical field of view
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
// NOTE: "near" and "far" define the frustum which is the region in the 3D space that is going to be projected to the screen
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 5; // NOTE: position the camera back from the scene some so as to "see" the objects
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: required animation function
function animate() {
// NOTE: move the cube around
cube.rotation.x += 0.001;
cube.rotation.y += 0.002;
cube.rotation.z += 0.003;
renderer.render(scene, camera); // NOTE: render the scene
requestAnimationFrame(animate); // NOTE: call the animate function when the next frame is ready, typically 60 per second
}
animate(); // NOTE: call animation function once to get it started
</script>
</body>
</html>
Reflective Materials and Lights
Besides the THREE.MeshBasicMaterial
which is non-reflective, there are several materials available with different reflective qualities. THREE.MeshStandardMaterial
is used for any surface. THREE.MeshLambertMaterial
is used to simulate surfaces without shine, like a natural, unvarnished wood surface. THREE.MeshPhongMaterial
is used to simulate shiny surfaces, like polished stone or wood with varnish. These last three surfaces require a light be added to the scene. THREE.MeshBasicMaterial
requires no light.
Lights are required when using materials with reflective qualities. There are several lights all with different qualities. THREE.AmbientLight
shines from every direction. THREE.DirectionalLight
is light like that of the sun, directional, and its rays are all parallel. It can cast shadows. THREE.PointLight
is like a lightbulb, which is also directional and can cast shadows, but for shadows the THREE.DirectionalLight
seems best. Lights can emit any color, not just white.
Here is an example using these lights and reflective materials (notice that there can be subtle differences of each light on the spheres):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Lights and Materials Example</title>
</head>
<body style="background-color:#FFFFFF; font-size:10px;">
<table style="width:300px;"><tr><th>BasicMaterial</th><th>LambertMaterial</th><th>PhongMaterial</th></tr></table>
<canvas style="display: inline-block; width: 300px; height: 300px;"></canvas>
<table style="display:inline-block;"><tr style="height: 100px;"><td>spheres</td></tr><tr style="height: 100px;"><td>cubes</td></tr><tr style="height: 100px;"><td>planes</td></tr></table>
<div>
<label><input type="radio" name="light" onclick="setLight(['ambient']);" />AmbientLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['directional']);" />DirectionalLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['point']);" />PointLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['hemisphere']);" />HemisphereLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['spot']);" />SpotLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['ambient','directional']);" checked />AmbientLight+DirectionalLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['ambient','point']);" />AmbientLight+PointLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['directional','hemisphere']);" />DirectionalLight+HemisphereLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['ambient','hemisphere']);" />AmbientLight+HemisphereLight</label><br />
<label><input type="radio" name="light" onclick="setLight(['ambient','spot']);" />AmbientLight+SpotLight</label><br />
<label><input type="radio" name="light" onclick="setLight([]);" />None</label><br />
</div>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0]; // NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFF8000); // NOTE: color 0xFF8000 is the same as the CSS color #FF8000 which is simply orange in this case
function createCube(size, material) {
var geometry = new THREE.BoxGeometry(size, size, size);
return new THREE.Mesh(geometry, material);
}
function createSphere(size, material) {
var geometry = new THREE.SphereGeometry(size);
return new THREE.Mesh(geometry, material);
}
function createPlane(size, material) {
var geometry = new THREE.PlaneGeometry(size, size);
return new THREE.Mesh(geometry, material);
}
var materials = {};
materials.basic = new THREE.MeshBasicMaterial({ color: 0x6699CC });
materials.lambert = new THREE.MeshLambertMaterial({ color: 0x6699CC });
materials.phong = new THREE.MeshPhongMaterial({ color: 0x6699CC, shininess: 20 });
var objects = {};
objects.basicCube = createCube(1, materials.basic);
objects.basicCube.position.x = -2;
scene.add(objects.basicCube);
objects.lambertCube = createCube(1, materials.lambert);
scene.add(objects.lambertCube);
objects.phongCube = createCube(1, materials.phong);
objects.phongCube.position.x = 2;
scene.add(objects.phongCube);
objects.basicSphere = createSphere(.67, materials.basic);
objects.basicSphere.position.x = -2;
objects.basicSphere.position.y = 2;
scene.add(objects.basicSphere);
objects.lambertSphere = createSphere(.67, materials.lambert);
objects.lambertSphere.position.y = 2;
scene.add(objects.lambertSphere);
objects.phongSphere = createSphere(.67, materials.phong);
objects.phongSphere.position.x = 2;
objects.phongSphere.position.y = 2;
scene.add(objects.phongSphere);
objects.basicPlane = createPlane(1, materials.basic);
objects.basicPlane.position.x = -2;
objects.basicPlane.position.y = -2;
scene.add(objects.basicPlane);
objects.lambertPlane = createPlane(1, materials.lambert);
objects.lambertPlane.position.y = -2;
scene.add(objects.lambertPlane);
objects.phongPlane = createPlane(1, materials.phong);
objects.phongPlane.position.x = 2;
objects.phongPlane.position.y = -2;
scene.add(objects.phongPlane);
for(var object in objects) {
objects[object].castShadow = objects[object].receiveShadow = true;
}
var back = createPlane(1000, new THREE.MeshLambertMaterial({ color: 0xCCCCCC }));
back.receiveShadow = true;
back.position.z = -3;
back.rotation.x = Math.PI * -0.142857143;
scene.add(back);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
lights.hemisphere = new THREE.HemisphereLight(0xffffff, 0x080820, 1);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
lights.point = new THREE.PointLight(0xffffff, 1);
lights.point.position.set(10, 25, 15);
lights.spot = new THREE.SpotLight(0xffffff);
lights.spot.position.set(10, 25, 15);
function setLight(lightNames) {
for(var light in lights) {
scene.remove(lights[light]);
}
for(var i = 0; i < lightNames.length; i++) {
scene.add(lights[lightNames[i]]);
}
}
setLight(['ambient','directional']);
var fieldOfView = 10; // NOTE: this is in degrees and is the vertical field of view
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 40; // NOTE: position the camera back from the scene some so as to "see" the objects
scene.add(camera);
var setShadow = function (light) {
light.castShadow = true;
light.shadow.mapSize.width = 1024;
light.shadow.mapSize.height = 1024;
light.shadow.camera.near = near;
light.shadow.camera.far = far;
};
setShadow(lights.directional);
setShadow(lights.point);
setShadow(lights.spot);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: animation function which rotates the cube
function animate() {
for(var object in objects) {
objects[object].rotation.x += 0.006;
objects[object].rotation.y += 0.009;
objects[object].rotation.z += 0.012;
}
renderer.render(scene, camera); // NOTE: render the scene
requestAnimationFrame(animate); // NOTE: call the animate function when the next frame is ready, typically 60 frames-per-second
}
animate(); // NOTE: call animation function once to get it started
</script>
</body>
</html>
Object Groups
Groups are designed to make it simple to create a single object out of several basic objects by grouping them together into one. They may still be controlled individually but they can be rotated and positioned as a whole by setting the group's rotation and position properties. Groups make it possible to create a city, for example, with all the individual objects contained within a city group including people, cars, houses, etc. which are also groups themselves.
In this example, a group is used to add all the objects together so that they may be rotated in space as one unit:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Grouping Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0]; // NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFF8000); // NOTE: color 0xFF8000 is the same as the CSS color #FF8000 which is simply orange in this case
var group = new THREE.Group(); // NOTE: create a group to add objects to
function createCube(size, material) {
var geometry = new THREE.BoxGeometry(size, size, size);
return new THREE.Mesh(geometry, material);
}
function createSphere(size, material) {
var geometry = new THREE.SphereGeometry(size);
return new THREE.Mesh(geometry, material);
}
function createTorus(size, material) {
var geometry = new THREE.TorusGeometry(size, size / 2, 16, 100);
return new THREE.Mesh(geometry, material);
}
var materials = {};
materials.lambert = new THREE.MeshLambertMaterial({ color: 0x6699CC });
materials.phong = new THREE.MeshPhongMaterial({ color: 0x6699CC });
var objects = {};
objects.lambertCube = createCube(1, materials.lambert);
objects.lambertCube.position.x = -1;
group.add(objects.lambertCube);
objects.phongCube = createCube(1, materials.phong);
objects.phongCube.position.x = 1;
group.add(objects.phongCube);
objects.lambertSphere = createSphere(.67, materials.lambert);
objects.lambertSphere.position.x = -1;
objects.lambertSphere.position.y = 2;
group.add(objects.lambertSphere);
objects.phongSphere = createSphere(.67, materials.phong);
objects.phongSphere.position.x = 1;
objects.phongSphere.position.y = 2;
group.add(objects.phongSphere);
objects.lambertPlane = createTorus(.5, materials.lambert);
objects.lambertPlane.position.x = -1;
objects.lambertPlane.position.y = -2;
group.add(objects.lambertPlane);
objects.phongPlane = createTorus(.5, materials.phong);
objects.phongPlane.position.x = 1;
objects.phongPlane.position.y = -2;
group.add(objects.phongPlane);
scene.add(group);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
function setLight(lightNames) {
for(var light in lights) {
scene.remove(lights[light]);
}
for(var i = 0; i < lightNames.length; i++) {
scene.add(lights[lightNames[i]]);
}
}
setLight(['ambient','directional']);
var fieldOfView = 10; // NOTE: this is in degrees and is the vertical field of view
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 40; // NOTE: position the camera back from the scene some so as to "see" the objects
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: animation function which rotates the cube
function animate() {
// NOTE: rotate each object
for(var object in objects) {
objects[object].rotation.x += 0.006;
objects[object].rotation.y += 0.009;
objects[object].rotation.z += 0.012;
}
// NOTE: rotate the whole group of objects
group.rotation.x += 0.01;
group.rotation.y += 0.02;
renderer.render(scene, camera); // NOTE: render the scene
requestAnimationFrame(animate); // NOTE: call the animate function when the next frame is ready, typically 60 frames-per-second
}
animate(); // NOTE: call animation function once to get it started
</script>
</body>
</html>
Using Textures and the TextureLoader
The THREE.TextureLoader
is used for loading textures which are then set to objects.
Settings the Scene Background
In this example, the scene background (scene.background
) is set using the texture loader.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Background Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
// NOTE: this is an image in base64 format; a url to an image can be used such as:
// var stars = "/images/stars.jpg";
var stars = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAABYQAABEMAAA1Kv/bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgBLAEsAwEiAAIRAQMRAf/EAK8AAAMBAQEBAAAAAAAAAAAAAAABAgMEBQYBAQEBAAAAAAAAAAAAAAAAAAABAhAAAgICAgEDAgcBAAIDAAAAAQIAERIDIQQTMSIFQTIQIDBAIxQGQlAVM0MkEQABAwMDAwEGBgICAgIDAAABABECITESQVEDYXEigZGhsTJCE/DB0VJiBBDx4SNyFIIzwiQFEgABAgYCAwAAAAAAAAAAAAAhMBEAIEBgATFBYRBQgf/aAAwDAQACEQMRAAAA+LRJSljEhuWoJJ0wZy65qasQjco0SFrNNBz0y5IArLayEkUkDqA6PZ+fZtOdC0z6Tl0gIuQvMaiaQ0yZcGq5M2j0eH3/AJvOs0LeAaL6uNS6qCqSEaENUAkFKoHU0ORDQwmpG0AmwTQmgdIKgYgQzfpPObszECYxPSDbBoG5Gk17ObTLNS0jWdsyJU2WOWgTAqKBFEhRLcjksHITcAVIUmgCiQDTN0udOUVyxpioVIKkaYaSCEOkDVQrQ0oglaKqGml5mxixFQ6FRAWJXJZS3mMpCwmkCcliDTNMQABQ5ErW2aEsHDDbJzKJljH6B5o2S0hsCiHL9Fj5MzUklzSFWkkINMctGkjHLkYSU5ay0IJ0KoFRQghipMJpBUNRoQBhriDHKoYgINJlhUhopBAjSKlWKkSJXS81FTZZAKqcOG4oAkc1SmmOplpm0c0lSGg11HI+nlGFEpsTHLNztWDnRIXTzhRoI+y57PkZ3zIL0MZblRSUl0gLsOLSWVDSqQRqlVBEWkypENxqvRy3nBpkWdGBSzUpPpebyJsqdaOdXREOZdctJVFwgqQqTLVZlRcCYVNDgZImg3yAReY2UZtA1SCloHp+TtLlPsefZzNgSIuXI00OpoqKkE5LSYnLAEKwFtkgZoZGsEgDlsSaKXRkJDIYCGDNMpailYqmgWkGjyUoUrEVJU3maZ0AqROksrN9BlBquctoSwZLGCAmibcCpyMSLJsS0gFfaY4+/wCZZ56olvJyVWbLRquHveGWVFTCqUOpYrhDpsgEK0GmTpYQ0Bobiik4L6+FjQAAJWyCpG0BrlrK+elYVIXKYJUVphUrm4sQg6rwM6zRWsoADbOVNUZ0nZtztANHp8MIZckuQqQByxuaJGjTIoE2J6ZKVFIJUSDLh1LLmrJaFc0IhUpII0McgOpQFSMaE+trxvp5omodjEDlsVNS76cjCdnZzVOopsJBFJCpoQKQqSBUCpoi5Y5GFqZWujEeZoZhtZiXC3JYmvQjzRqw0ijTPp55Y0qLMqAS0RBQJXuc82CVoigHNdJy9fN1S/T/ACP23xVmJ0c6uaoybEl2lQ2KgHu/TueLn+p8iz//2gAIAQIAAQUA/TJA/XMF3+BAP7KuPz01Tn91X6Nj9QfnqfT97X7SuP1hf5LNfszd/th+Tm/zn9SuPxuoPX9kb/LXP4c3+F/vr/d8/pnH84qe2//aAAgBAwABBQD9MAn9cQ1X4Akfsr5/P7bnH7q/0aP6h/PfH/kvr+sa/JXP7MVX7Y/k+n5x+pfP41D+rX5hX5bNfhxX4V++r93x+mL/ADtc91f/2gAIAQEAAQUAZyIHJgZ6zaFmmTUWNEgtlzk06unf2Nj5pNSbNpLCrBP8kBYwEhU2Cz5EjksMjAWgzoZYK7Q3lqzfYrwk3bGFjAzUx507Trg/0i6+v2e1s3tluwILNzCYTk5FwY5ECa2KECyLM17n1sSWBLBD6sAWzYLTUalgB9rufUlyxUkTR0+zu1vp2oofV4WFTTqEbj8OZwBzF9VUsPtKFJZjeqHVTcTTp2b2NiMAk19fdtWyYwxY+hPGzwl3RtJ4igEqGI+L+Hbt7O1110bBDiA1A/RlxnW+Q7GhVd2i6nIZGVVNkghiTbct6w42CADay6hBlnBiA3X73g6qhtjFQdTVVkKRGdjGIZ74hoEkka9Z8akqNTO7n55B8Tt25vyYLl1DdliRLuG1fKfQ5WIj4uWBnM5EZrXiEEG/Y/s2Ua9AcQHR9ZsQ2YAzBQTKNMoUk1ObHoSCVDo4d1FkS5k5FCD0sYqrNMTk3BnANkQHjpdTf3N5UgKcSrKoyJAsHf0Nmjqe2n9SSSQQcmmV/h6QUWew6sAcyGyAM5AZVyJIl0uLMpBM5M6XWXs7fl+ivS7IfEbh1lQ8EK11YFM2lgqKCQj4H0BB1zixLEXt9rW1L4zWXEPV7hUHX4zARNbBSuMYgtyZyAjay7BQwDQ+lMxskO2TDIH0OvadZfczgmDU9/RELtyVIAhIIogAiDG22qdKhybhdr+lHxvkQykE7tuPEFSzgKo3LWEKIBcQlZfBGwKSTExzRHYlWEIqbm1MyemtS8IEOTkGjfFCjQA9U2MkZcSBcyoKmxj43YFSDwJ/wzGKRKlKWLZCje1tDtGIMBIM1lSWV87MFE8k63dNm7ds3bHd3dlxJqgsCAp9LMoS1g9bXJiCLJgQlManNjsbV0FWOtGKbMTH+8TmNtdgAsCz/olgQJ9OK4IZSpFSoTZuCqokuq3xDwI76W04mGWLIAgZfFQhBw9ssxVLDV1VdW0bNDMCG/8Arb19Gtr4WBzjYnl9hoT6VwKuyYNhGrlYRUINmyvBjKRr4ipo2pZu5kYGZSNZl3OKUAksaIxNgRvEWrF/8/2Ono2/N9nR2d+wKjYtgwnFBTY5I4BqcwLbECcCBQRsBVlYqcrm9uuIoBjYhuKik2ibNrC6qoDQ1lb+R7fT2qwQKwZGNmVCS0XAC6n2ld7AHZm5AvjBgSxeimwszuzMOJ9pv+IAEa9T7UZGQ69L7tlO8KPiGZGUKZt1lCIrYGyBwR9zO4q5dS2ZSxMogKEMJLIccEXJ9qlNwCkgyxKIlreWn+o1hwam0qYDQgaoBMSFOxtmxreKSsb2zFhAFmU4JlETi7xFcEG2xo+p5INM7KTgSigE0DBVlUDniZtGRtcQqTZAo4GrGJhU1RBCkm2pB1vEMVgAxUiz4y7e5lFt/wAbNJRQxUisUUOQ5VSbKlAOwes7jxQEqcjiSTDna6WYKqsx1gaivGxCjArDYjdja+np9Xb2dlETzQqcvtY+hLuQaHtEINXzkYBzxCPcxUn6sAEYqGvlNuD+phZwKM1M2tmVDARQyqzAzH8NjBohfL1jlb2NrZ7yOjfu62xyS9/xsSSFZ3siL64tWxzsNGZAwFRKYMdbBbucsOJ7iBp2NrBNG6Bpq9tAwLwmtmfxlnqwQJzOQwUwAkZHG+SefUhfayGqIOOvwvQZXZD9QxENFepr+O2Hu9bpaJRsDkuSyqSbUHJm2Or63piOr2tvXn0XWXHc6u/4/t42E15FdYI/zH+d6/Yb/S/B9fpuyQiiEybdt1bJXAVTAoBbHO4jKCOJr7PVToBire7x5oqsFCuRPWHKrJnkYqzlmx1gKFLIwWCiVYoQoE7XR7GhWys+vV3DR2u73N3f7SLZuI/Pw/8AoG6LfK/Lv22JygZkJYsT6jWCnFu+soLMujZqyBfFEz/koCGIIh4gcq6kEFgUENGW6xUuGXMipffscnmCoCRADY3NgGpvQK655gjevXTbt09jV1hr2HVq0HbsX3D6NtLRFlIWrEjggxgwHGO0kbUwwN+MKSeDKs/RQSajKuXIhYmdjq7euxsKGIjCiKtixMJBigT0i/cDiNeryt8h8H2On1SMYoN0QLxB1MoFW7BpiSpIM4o/bmfDu1ouxPvKMBzbAZBTSoWBOMbdsbSDQg4GSlaGO1G1sOILCNbzmektbDVFK5b/AAIVcrN3abavxPQ6nZ1d/Tq071xY7EwLLgwDNDaSjF+42DjcyPgDEHgEtxZJCh9npACUbJolCWMbi4wm5r7LLquyJ/yAWNWfcBZr6VUM4nJKbdiBy2XJlC4DRxUk4UuzCD04uj42YlqAhIAIIChjEUM3Mq1x9vM4DYLmWhC0SpmvXt7O/saNvX3WzAgqQSIaEalJsmjXU6p7LhhjYETWzA+rKRGswGb+xs2u0pcbpbMsYMCCAtaNW7s7+ymxexQMuCjGOR1UzcAbV2o5ApSlCehfdsc7iH2nghzrgViVd9ZCljWJbhk27NcGxkVHImvY+qPhbOzk8QK18W7NXjvWBlDqNYGmIhAA4riH3v7sNZCkZrD4yIVIamops8fqMGJxezZPSXRkGnU7W7p72YNGxDH1UrY5PoSB4/aEIqZKAN+4MOAjuPwFEXUZiZ4/Y/3es9on063Vft7NyuHVC7D037ju2GgC7OzbOF17TP4/GApPw6fEvp3+LNULkgmAUfSY2wjsAKjBVZSgGM+lZvv07OvuIgM5EE4EBMz2Ys+zLJjEGx27nxXd6er/ADny+v4/Z8r3v7fYsYqWtj7iTOQLIiHUBo6+3bOAF2OoyOLHIkknmBSSfcOJUHIdWUFaA9FOLMcYWN3rMHENEEYsC+JFTjFwbWwdbsjdr5HsdlQ5svwykMEtLbEkGXLNL7ps1vp2IFy09bbum3aX1+kNUuvJE2trBx8ZFA2BoOkbd/Z3bjgWFgJOJ1epu7nY+R+O29HsMEChTYtWYtVG6HibY4U3RqLRh4ORqgIuNgBthBClmDCqJQKCLRWeDZs16fuNGhUP2tiWsY80xyUgg6wh2MqqzcTicRCEfbsfZADK4QMWa8+yNOpvbbWH2OHcmpYJbboHVNCExSRH6O7Vpfbs7O7ITZsbc2LIqsdbJsdNjMzNyYux1AJwYArwTxjsweCwTlV1BMvYSs2a21NRAxca/q3JRSmkgUwxnj2+DgldWx9jIyn75bBTiR7CBjMjjdkI+47NR07GoTiMBCYLoVY4mKkUQArEgmWQWqsjOp1Nvc3MpU+hZg2oEmF/YAXI9SSdjbth1m1PkfxMYoWmDKSSWswpsClQTkSFYgk3KdnJJnuUe/x1YNwktDRYQOQSPbsRtTG7bCMSQ+xngZlnNYgi1adHqN3ex8n0H+P7eouHWilT6Njl/wAN60tFf4yMW4JZ22GqWiCeCRSEe3kkkS+GRtbkmEIqC50Ono7J6Hxfc+W7fX6IfexUQzgD2zgwCWxh8ZZPHkjsjF8316cuz3eu3U7EVmWbPaaGBHLspYsPJsZ9hIJAPurjgrzHZmagGAH4EHx+g92DDM/QNU1btmprJigs+hdLbfoy0MjjiQaEHsDhb06tm/Yw2LB7CSSaNg5F6TZi2DUCCAT7SALIigAkmEZOMIeAilipaBVcOeeaIYgqyxsTMtPhFg9jZrfSEZoajNpbXYM079epVC5UMaYjbtV21MA/zHZ+O7DtlBdhTSZLOybOTeMgTW2koACwb3uVP4UJVT7pVTFVIELEmhKjKqgs5JXjBp1ujt7O3uf49tPxm/Q+phrdjk/j5UAFypIZjZikKFUn8AgOpaJKe7XoJnd64/seBZs8fg2Yz2w+O/ZX8eP8VHx3/HW3w47PDmPFj/FXso+LL2UfHSf1fJ/Hj0K/sfKf3/8A1m3w5dbyxvDZxrX/APJ/HQwg8WIwi4wePH+CDxzrfd1f6mHyn/r/AO5/+PP/2gAIAQICBj8ATNBvyaJ0jbgVfhcyM/qyoFMZf5UCV7KGpzHUf//aAAgBAwIGPwBMUYomvNuVxI9h5w32oMrWUdziO4//2gAIAQEBBj8AOrE1P6I/E/kjqKOXrXbuqE1vVCpHcv3ZUJpWiABqHcu7/wCl4uxtEnyHdk7nsKexXK+zwcZ5uWYIHGIkyYDLKPWiMXLFgWLg9HUxAHwgZSAf6Q7+qLX0qaLxcPaLv709fH5jcBULiNWtRZxkco3Zwz2qhGT4bA6tQ+iiZmQEn9Y28XUuUfIZYgksbbDote6oS5LImrB92J2UTMmMCTgblxdScsJUlqQNwiBLIaGzvahUYAsahpFhR7kpye0ao3FetOiJcsLn3K5bRGrlNAmUfpe7dQFCYJlGN3oBI6L7HBDEFsnD2sHRlIkNaPdPV3u3Rk0Q5JLRALp2pZ9igBaP53RfxJoQAwHoNESC4AeRtqg5o9W2XiXu/StFliJAO8ZBxUNVMLlenoVHkhKXHIODyAnIk9rbKpcm+t91g7CXlTUaWTO7aixUmIxFXZrrEEgSbKIsWs6BY4k0O7KnvWQDSFtnB0CJMzLJnkbn/S20dH6QWeMQwONk1W07qc+PjlOMImUzES8QKVQlyRMIEPAkMJB28d1KEoE8pkDDkMmEB9Qx1ddLv0XJ9zkjxHji4cuZkj5YiLuS6oR5AEgadFX2aotZAu5f5fzdAeqc1FdW0VA4FzsvIDyFPXVDPIRJGUo3x+qhomzOOTZOWZE1BGoRfjzBhixkY4z0m4Fuixo71YdLdkePirKMZSq0fGPlK6ce1EPlGQYEWMg3uCJhF4RvMkM23VEWeh690Rsda2Qp8U3+0/CJGBDNyNk//wAUYckRnR+jh/at031WiLV6uiz4hn2GlVxxnOHGJTxjyTk0BR691LhiY8koyIyhJwQKM3vdPQiyDEmW1gvF+j0V66BAxLkksY2psVy8fHzz4o80THlYnyBuDumlyMICUoAmgLaDqoGAzPIcYRBebj+IWZicSfGRbSjMg/al2TTHk/ymlbIbj4o7XIH+Bi5p5O1+it5afoiBKlj69FS/6ofgp3rk7eikNzTtouf+vGHGRzxjlKcBKUTEv/1y+l1jGAeQNBowcs/RCYDByHdzI3Hjp3UTFyW8n36dFQvDJ2LXbVPR3YNprRCtQwBFGAtZPZy51DpzvQrf8WVC/uQeu6nygxjLiI8ZEZHKnjE3ZSsARRw7g/BS5JGBxAlLP6gGHtUf6keLiMRyOZ4+XyuyMpREQf2hkTSgTs7aoEX3RJ1qUBIlh8o/RfD0QBt01dUpKOtX7oAhxSnayBe5sNOqqXO5qmHc9xshMgSYuYyrEnYhGUmMiahqMdR2/wAfBMICIYAmpLi5B0dXsqpqM6kIyExUZCxB7p21b8BOKELroeiaQYkbvQ1FlUeiOv5spSYmIDyI2FEWvcIqN8SAQ9yCmBeJLkJro7bhE2FwEA+MpAUcCkrP0RDtE0kAb9EXpSul0xDEX37JnJyYd9kXpoQnLGho7EIBvIG/TsjiLBydhZ/esZFn106HsjEHIXfq2i3fRVcDZP6OUY0Y6kWZR/r/ANcA8kwSAZCNIjI1kw0XTTuqh3DEO3vCllHJwRFiQxNj1QiwGL1apff8kNzYLg/tcpD/ANnLCILyGBZyO6dq/wD5bonUlyU9tm0RjIYnYhmQb6bN7UxAoGHtdMVShtRNI4gh3Z/cvKOB1jb3IeIkBdw+jISiMWpv8UDEYsACHcHc+qFhVXugIl4mrO/o9EQQKtiDUjomar3PRGX0xbq2W5RJqdOqZ/ao8RlGORAykWA7hT/rx5uPmMDWfHLIGiLUJBGXc/mojhlKUpVnkAMdg+u6NXa5Cs7Vs9k9ADZeT1FNa9ei5JzhHkiBiBIkYylaUQNaIkWHVtLoEMSC9n+KJIcGj7FGMwRK0oEUHXunNhp07r80RfqhKEzHkgDGMgziJGJHZljS7u/S3dEjeo6IiNAfh1Uubk4uSXHTLlIqAOpUomUgXeAbxP8A5L2sEQSwNa7790XgJkggA0Ap81CKhAyLh6xtQaP1RIDAmgJct3VlUfMKHooy5ImUX84xIiSP4llIEGLUETcd1QGRIPVm7IZCpq+4KdzLr6Lpf1UpEMSHIDAOLWTAuagAPqNF+qJF2IGjOjE6m2zVpsuyAx8gDKQ6CpKt1pdCES50HXoEwDh9LVsq316Mo3LCvQ9EJbijp5VfTVA7jTff/hQhSUgJAnFiHP7n8qD0XiHID023VqaItR3DCzG693pqnoz2eu3tQlJ/txkRRrmpZEG4LFYfcmYaRcs3Z112WO9ySmHyu5YaohnNyeyb2qNLUI0PVO9SQwFQ3foi17nshOMiJCxFCJK5JJc/jdR5HYSBEW2FDayYlwLdAoPMwH1TAcx9l0Iwi5NIgX7MjFiCLjZM36qP2omMREA5MSZfUXF0YgPIvqwZEAxoMjkWogLn39k5qd9aJ2dqtsUxRJLM2IOr6P0WNyDRqhX+a72QkJkGFhej6LEgiQoQdF09/omifmDEsxreKkIPLCJlI/wF/RZRicSWBA8XOj7oAilQmLmnsJTaOsbB6pmcOKb9E9gCerdEWNHoSmYAXoKoMKn5fVcceLj+xGMBHlJJnnMfNO1H2FlRD9XTvrVfi6EeR8P4s76XRizyh4kBjbQMnGtzuBd0BKkHfs6p+GUeXiJjKMgYl2LivtU+TlJlyzJMidX3X3JzMuQ1lI1NKX7LEhiwf1D6Ltf9VU2uy5Z/sMWIYXP7TX2eq3q7/wCBI0iSxY1ojEB4kvEmhbTt/hyGGwUYxGgqS5/4XvX3PpyxNQ7s/wAt1ViCDXUdVXTansUuDj5JR4pmMuTj+mUofLIjcLNtS8nuToIpuOImT4xEw9ZBqdarFi+TY/U+3dE0HowRa5FRq/RWdr0+KiJF8YiMX0ANgvIFr0u2l0KgVpIfovIgkFybuepCeo2/4VbdEEGFr/8AKiHDasK31UpcZJ4wWE2a9n2PRbDUqpptqjrSr79GT3eh2ZV0fu6AGtBo7qIiCGpIE/UPm9E+iYVHTfX/ABCMePDkhE5zB/8Asc0cdrosCWD123XVO1FQg2oK/wCl9v7YM8nHJqzNj+f+BJmifGm8a91erXOh6MnNCfRYZNH5u5tRtUXjLMGwIqFkRjIfKdjumBqLMdd3T6P691uL/jqg1SNVrEH4JwTegbTdGJk0TVtyKBU/5UYXjFzjZ3rcVRr227on2BULsx/AVbbbp9tR7FPjAaMpAlxZrNt1VR8wo+x1QDuToN0ytU1JLVHRdb1UZFqkgMfL1G2ydcs5THDOIj9vjAJEzY+Wm9UGNRW7BxZl1unFCECLixv01Q0DO5oyJJcm+iJa9msEa4gPU77IZGjMO3RMQ0hfRCJjUFzoey/63YiI82+Y/MXFgDZGJIONAQXHoVGf9mA5OON4nVSnxw+1F7CrBUOcCAaddO4TMMnbF62T2qWfXf2Kv4C8nDUY+4KnkeiAbV3tTugaGjU/P/AiSxJqTRu6IjUDddfciXY0YGxcppxYsHFkJRPkCG3dEy+bSzeqj/6ucYmERy/cIJM7yxb6dkGBMsrCgIZ77oiJeJsRtejoaF6kmitVAxJyFQjGIMixkdKRDk1VLSv2VQejr8mQyi9Xqv6//q/1h/Xlx8YE5CZlnL9weyBEnoXi1v1R4zIFrH6d9UHHV9T1T7qrksz60Us9jiP5aKwkBRxr/gbjalEBM4RepIMmHUIg0b3/AO02jo6aVUhEBiDFvmobsow5AeSL0iZMXJ/d+qadoAxDAJ7HROCC1XB1WIgKESM/qrQDsidQpShFxExEpaDItH3qUZDExOJ2B2Q4+KJlM/JEXLB0ZVk1ZSvQm5WZGMXAB6moQlAsdDqFIykIkBw7nKT2DboPKMs4iTxLgPoeo2T6pwHa+QcP1Qi9Lrff/aBe5DmW/XosJCMsHaUQwk51Oo2V1VVIIjURPU1ZAPQCg0D6IgBwz9gpfckR4vFhllLQFPJ5M0RWkRdlEgkyrkCKDZlGD45EBzQP1U4gCMoyMSIF40ocTV0ARi3zEXPtRJruRuiT5Sen7eqL7gke9VApUOL9D0WGH/bnl93o3yYpzV6s+htVfLb1CxjCMQNnc06p2d/lLp77vZAEUFe79VU6d/wUJGxLDdx0Wcy8zeX+lKuuRf3o0BEgY1uOoRANLbP6I4ihBfrEGqc21aqcVoRXbREyfI1AA167KvtRHtH+JRlHyoHNMd6dUC4AP4siW9W31UGBz+vJmfTH0WLiLa6EjrVE0HQUZAsDiRSViBoVMwGAJ8YvYbI8kXPHEgOaVOiiJSxBo92G7BNEOzuRv/wqkAHXopREjiPlLMS1qaIUYabkIVpF8X0H4KGVCQ47FATeMWNYtlZGDXZwd02mW+qJI3ruXWgi7ObjqhI6hw+osmIYnehqiHYC5Pf3oF6D4PsuU8uY5gI/YEWxf6sya2syhMRyBcEStkO3d0CSz26hDK2v+0cX+2bEtk3wdHVzf4IscSN6C26NASWapeLdFCUpA5h4iJdu6Bt2oqgkg1kCzA9FiC25Nox3LKXHE+M2egfxNOoRMiSSak1KNCeR3/jiBtd3UR/XE4wMY55kHz+ogR+l7BAGRFWLCmPruiwAeLHUMe6bsfUKtHqT+ZUQTWPy9B2TkxgDHKJkWBbQdUwo/wArlvaUOQckC5HgD5CmsWUfEgyDgnatWTZRlYiUTQgh1KoAbxia+9auocJY8fGSYgfyuvt8cM54ylGJB8sQ5Y7pm8o1qK03CyxDZPg3i+zWZfKfKketdFXS4DaIb1fV1kSTQPIuaWDlEbhh+voj203W8d2TnyBDFRJ8m316KldwEZA+IYMbkbrEjuLIgDxB8auwV6WdRkZeUj8oowG6JiPEmj1QJ7rMRe+QehBVaPcsuq1AnV/3AIda+xGUGcvECQEqSFaS+KiIEVGQiCS0v2yfVMwu5OvZYxJ8jQbtugavVyhEyo+Vd1W223ZRIiIlgC2rfUhgSZA+P4KBoHqS6OMcReIqfegeKH2oAAMZZEkDyLnfZCNgKBrt1Q5eOdeI+MgaA9OhRkbkufVWF7pi5IPiHoBsyEYgykaACpJ7BFqNRh+a0bUOa+xZNQVJGndZEjI3iI4xiBbFk+h+OyZqPRvYpRI1DSr4gGtApAAvEOW0HVkJSoJPj1YsUXqUHYYhhS/TunrYv6d0BcRD00dS5QHhAgTO2VnRFK1r02WQBAJpL8tk8hS7EOFrs/ZDY+pCedTIFhdi7eSH1EkAHsvl0qBvvVAEgUo/RUTa2VR6HojYY3c37I9LrE22Q2fuyNakm/uKay+4WLlmJqVmPlk+MgGdqFhovgFll5Z2Ytizu/eilEGj0LXQlCRhIWkCxB6Ef4c13G40R7hy9/Rf/uf2OTg0fjhnRRP9b+xPljIPAz4zx5De6s51BTEA0LV/RRjMNGAYiIZgE7Ft+iDWBdzVw+qJj4vJwBSr0U4cgbkBIkDWuvqgX8HIB0e5C5YQPh/YgePliQ4lE1HvQrX8MhgXciOJIBzlsNe6n/XmQOWAaWP8xUewrxYsK+l1WQjDWRsKIASiJDXN7ocvPOEwGIESC5RlwSgAfpMgKogSj7QEzhxqCgIsej3WPDx/b4csowJM5RpXy2VPVPKVtBf0UMqRLEkX6siIvi/iSz46PoqbMhlETBoQSxP/AIkIh7CvVT4T/WjLnlOJj/YyLxiAXjjY90J8Zxk9CDZ6K9Mvl67spxPGJTlIGPISXiBeLCnkgRcu4uA5ozbdVHEEADUu5OvRWoPzQFwfl/4TaoQFGudZVGMa7aKUpk/cJrJ6kn5ieqgci5JzDfKHo29FEGTRkak1YbsFK0gxAy6/VHYp/Vj73WUe7XpsUKAg6A+4qHLzQwjyxy45GmYsDHe1E5JLWNuqYrj5zX7chIW+lS/scxeU5Fh+0Epg7kUbV1SjaIVYA/7Q4414yWcipBNyndiLMnLACjqp8XBkAx+Kch6uTu+69eylymUWgQDxmTTk71A2GqrUKMOPjaV5TckyPbRUHT2pi1tU1dx0WJ1LlXdvgu120Wr5KcsgMTq7lyzhO1Tcvd/0XxTkXsOiiaSxsDWNtkxsxIs791GI+YEl7OOyq7oFm6oSs9Y9t1EO2Wpt09qxNADUdVtS6MYOIm2TbVO1UBOciAMQ9WGwWRsKPqVV26IkUFnRkGDD401UeNomEXYNfKtTq2i8e3kyNUCA0SaPUgJq5PR2xAKEOGR5eNgDM0ylq3RQlyQj9vl8uPkLGRY4kAioZrKXKIvCPzSDUegy76KUBMeMTJ9CwdHQbgUrug9G6oMBxgRESIhsmDZHqdSsyxESMgb321RzcAiRhiGFbU0CqHB/FFiz6EW96JF29G1UCaODiaaUT/Ts/RTDw/aTH5TS6mJHyLYUJ18kzOJVB7Flidfy6lPQNcWfsgI1cfgFd9E+9BVkBezt16JoPIOR32Zfn0QehYDb2qEZsTyRE4gEGh7IbaHqLonSmSxpQ6IPvU3YdlUkswGzCgTs4FwibOXiL+/oiSWa25X6oIHEZVYv8e2ibjDWABIvrLRcHPyGJjzxziRISetaOqhpah6dEWuXAG69HcG6dxLrsfzQyItkKu8dKjdbPr+iiwYgAFyZae78lX5d1QNZEG5toyv19Vi0XyfJvLf2KceOYmIfULSY/NHog2laa+xE2AYnoTZMPQo09AXA9VkQ8RdSItGpqHboNVFhjMOTMF3e3ZQ4TL/r4yZQBAoZXrfRWpvr27f4B9q/m99ABsiS/XdtWRiWeh8SDGocfLRFrG4TUIu4GpFnQxIIhB6ARbvu2/8Al8WYAalz+TpwWr7PVBxSgYbanuvt/wBc5xA8uWozfyFDbG1Lqlo1URLQN70Zz5IR5Q8vMsKaDqU3D8t2vZCBIDmsm36osQYkOGL0KMSxa5BcH1CAFdgoyoMg4avRNUDrutnpuz0dE/62QatCSBcNumyHzfK3vdTjGMZGYMRkKxq/j1ThxSx/JkGoaOBYndD2KEcoxyIDl2j1LKUSXGwoKIsHi4D6jssyQSb70pZUHl9LM3XJ+ioPIH0Yrrog5ADl3H6XXjHECu/tK5eLCEvumJM5ReccS/hLR9U5LSpjt6o0dnMjpimcuS7af7TBiTp2RYts6dr+91djGoD77BHRm61USDS/qrV3TsQPjIfkqDc70RESz/FeZc0cguhUv8Boy2F1+SofWyiIPIEDJhrqF5AuAwbU7l7KM+N4ckQxkLuXr0TDRVDDZZVZ7qZhFgbgOWD791dyzgvZeJdwMh1Vaapw9AT7NUMpYg/Ua/BUpqyc+1ZSOIbxB+prseiArW3qsZeQibilNU2RMAalmOO7PspCJJgSwfYIsdKBroMMT9RJJ9UI8PH5yIAjGwke6nx88TDlifIGmuyMdCRSnor3u3wPVdNlSofsmiRMaGwbr2RNzuE+i5g7R4eKfMSLAR0QIgAwMZSLyBJt2KDULOd3RkPliQ6pqmIIN/RDowA2QFgaSOl0JTxeMRBgAA0RiPlZ+6FcpEMbu6oCeQO+wAHxQAauyumre2ndGtHsiSbWouLh4wDyTkIQiSAHJ3KnEgDlhIiUYBogxuzIuGGjV8tkaU0DmieRZhrqdkZFoglyI0AfYL7TgCdBKdBHUF9FHQipkDX8Bfb5gxho9gfL807h3+XbqjGT12T0JDUKfVReReAEY6NEVAp3QbklyxNBKQ8gNjdPEmlQde6lLxmZ0ykHIJDkgHVACpbuyljIxJiYSI1ibhAAOSQBtVGEg8gTEVsQauizUpSxU8CY/ciYS6xNwpRjQSi04nUO4WPiQTlWINWb2dEcJEZhpDoVLB8bRe5HVkJSJLARBJcgAW7JiPXVFg+DVFQK+yqL0I0UYmT4hgNqkr7kiBF8Qd5M6LMGDh3qRoOpUi7UyGXiZB28d0zC+/RFotMkudG6IggiYLdEAwBHzF7qns/5QLR4waACkad3Tk0letabonLAiJYgPVmx9d0AKFqC9CpEAhmABYnrtqndCMniB0rXoUZYsARE99l93FuMnHICj3ZABqW0JdAM5Lj1F0QYmJiKggv705NVycn9ivHxQlKMD9UzSIUv5e46KH9j+vM8fNBzCYFtHCc3NZPuboDjL5b6equ5ohl5ARZjSu1NEzO9PVVLnofaFkGcmlahtwr+Rp/4sb+v+A0XlGT5E0I0BCzEvIREHYfK2KLFnoR0TBmLg5CnlSrotuwARJvoU+ot0URJyIhg6Z63dizM6NXeqD2FGNH6d07O+ho3+OOIZi4kIg+Ii92REoCDXgKCgWMdd6BhW6BHbsspQhA4RiIwjjEsPmbc76pmuz/6Wc5mUzeRJJoKF1LEy82yyYmTVv3UWAlmSBDIVIGz+9GVfuOMR9OLe13VRTUL+1yf35csuU8ZwlQvL+RLr/rfHYsmjfY0snviA6P3HkAHABd3tULdwjEScAOOraBObUtsjxwY8eTxlIDKzXCpUXRxkCCGdiL3odkcgTKjR+k7vqi4+X5tDTX2oPVrDRYx8IEgAlmDnVlyf1pyBlxyxJFjj+SpVUJG3ey2IojT/gbqmxrp3XiaxBO1NVlkcrPri2LP2omyYndm9US9tNa7IRjU6Blw8nPxmMeSOUHjQhPPi4pEiQEZRvQlS5jHjB5Hk8Yszo3BpiB7yfyRFARd9hshiXOhD+0J/wAVTj0PZA66n9VW5+aVy2oANPVT+w0jxjKVRE4v/K6IkwqSP3CQoixuCCmYMC7tU6MnOgYDoP0VQHNCsfchGIeR06p21v8Akhvrsj8EHYmXW3fqg4IcUBcLUSsXt0ZUIDu7qopUSFqFMKg1dE0mSGc1vr3RBMmriaHSj+qGuvsRIJDeo/BR/jUCVCpB6S+bqnI2V/qv0RloSz6ImjsQHLMf1QmDUHRccJ8kjHjiIwBkSA2zp3rutjd1iQ0jcd66dEZOCxFAa922TfSDbqUSzE/K1gm0FnTKMMh5S1oATRyVLjJGUSRlEuC2sSLhRzBMSagUJ9UZMRwxpPlAMoxe2TKHEeKPH9kF5D55Ammctei27KlSQG26rO4BYsQ4o9lIARkJxI8oiRGWsXsdkBQylSQasWLhpfyRDinX4Iiz3+KiebL7QIzECMsdcX1Q+7yGYjEQj/GMaRi2wTgUGj+0og3egtbUlP7VUb2NVxcMAHkYwi7RBqy5f6/MBHk4iRIOCL2cXKiz5VM3Zr0b80LBzqbd11FpDQjUFO4oakak1dP+2pKyyGWVte+yMMmiS5iLOLFMdTe5T73GoRyJAALa10CpUb6FYA0Qr42EtHQyk3kA7E03C+rEmhFSUcgIli5+qWR1dRniHAFCKFtwb9Vft1KGLn9z0r0ZHRh490OOIMpP4xAcvqsYcxEeV/u8QeIBiaCWh3TktoNT0CxxqTTunNY9CgDe6OIIibB3I9Uf3A0B7XXfdBySagxb5du6IZiLg6FATmYxJ8pgOw/8VLAvCJMQSwJI9t1RbI1b80JcZEwG8mat7XRlJzGMpNZwZFy+qDelPeomQABDA9AV4jMitATbspbuaANdQ4eMfcwY8stJzuQOgssm8cnxYs98d2RAuCWIRlGAh/CLt6Oj1uESR5uKigAHRDhlwAf2o8hJ/sAlzA/QY2NdUQRX81joDfuixqC4G5XH/wD1f6XMOXi+7FzCk+Hl+aInDSttFLk5ZGfLySMpSNTIkueyiGcwfyEi526U6KXLyF5zOUiwAJNyAEJkeMnxkGILX+KeURJgRjLQkUNNlHk45YTiXjIFsT0UpEvIkkk6ndWvdSEZMJRxl1Du3uUo4guxyaoPfqhL92ur7N0VAwujRzd9q7KJgJOI/wDbKVXl2FhoENDpoqlwDWta3VLGjpiBsC+yMQSIyIyB6aoFmANa1Kx5I4kioJFMqx9ycxIkLHRjrXdZAeJNZC40YnR0MTU6LyvqdVLmhzCMshD7YJHJKJD5U+ndXYs7HUd1UeYPcM3vWeMvtv8AOxwfvZGlbuT7ghxQi/K+MYxZzImwOqIIYjQqMQAAwDmjm7yKMdJM47VAQIv9Qag7L5i70BGiLu/0tv16IxiTjKpANwP3N7U/ykAENuocXGIzlyEFwMSCaYklmUoSIM4FjiXFKHHdAAVF5AuOjKgalv8AaA19zIihe5/RUq1/VVLA3N/crPp2UQ2Ey5MpFokXi1FWz/K9S4TR8joBVFiQ6B1ZqqoIyjbc710KzqdDJmrs6PHxM4iZyMrMLkowNQCxPW108hqXi6iMAJRcGUfqH8urp6yEbjYLES8S0jFy0jo43CERU2YByncljSV7aqUqEvlZgX2CEDLxBybVwMXe9kQPlpbXssMj9t/lensWNGd31RJniRWIY1PomkKkO5t5WITkd9PgqSZ7hkJmJEXaJ63QxeR9Km5UQfpt61ugxYioxui9SbSs3VNCLGf0xoCg2go1EARcCUQbGvwLKc4xbjnIRyYMCPPEbIl2I0/RPKuVQf8ASc3pX9qtiNKuA11bRm6LKNCLG7DYJ9rjZYmkmBbUOHQNggQZG77M9GUamQAaug29EMjSIEYjYRsmiTEEMWpfRUsaB7JwQCNDd1Inxk4pdxshwwIi9PIsGGiPBLEmIpiXHtQnCWM4vLIXpsPyV8ZA9ot/hwbU9UWewYa2T9fyRazlF5MQzD9z9bBl9wyucYh6017IZ0BAJGtde6pYm5UTymUhEADUiOjIScVcAa0TURDB30eiFqk96fktzqyINHuAonHRmd3Io/RXpT8BGHJFpC4e3sXlc+iBBJkbjRm+IKMWG9tlyDm/sR4MOOc8pPiTG0Q2pX/q/wBOMZ82JkASIvGA1JU+D+xyw/rT4xJzN2Mo/S8XqVUPMH5iXBGgZClfen+YkV/jWiGh33TAVu/5I9LeiqaSc9CR2UXkTCVSQPLrHZNPL7Zf5SAXFr0ugYHGQudXQlzmUouM92fTqhw5fbeWMTyN4xJ8c2ouT+v9yHIeMkGfHLKB6g6/4Mo0kBRtEYhm8TaxA/JPlq/V0Sx1deAIcB3a+rNojy4RjlWPGHx9jr7s6k3kKP7FkHxdnN907NE3D0ZOaK1Rp+qJQlKrtGJIABamiaWlCr1dqrqaHqEC1ifJ7voy+IXi7A5evRSnCGMQQ4BJYnqdymb1RaxDEL7nHOUCKAxliezjotTk9DvqgAzyoH/VAcszx8TEymBkRSgAG5TNXrogXHkHYFyO6Y1iKtsSh/IA1pQ0dUPqhNsoOYkFg5ZFpO52NlHi445TkfGqIZsZOwsD8v5JiCC1NKqtyUxYaE9QnEREMzRDBEFwwFYs9QN0+Jxyv0RcPL8VWEi0HDkV9ikIm9H3iUKd3o6AA09d0MrH8VWFXdq0WMASZFgHckqQkSCB4tbLq6DEFxXodkIAgE0INBvUoDpTZRjxgy5iWIFRJzQx2TNiYgRIBcFvmJWOnuRILxiznQOvIEE2dExcAMwNSd6oRHH/ANoJJm9CDoyJFHBDdDov6wjADk444zIFJsaE9QKFECgHoKn9ycMGAZYwiYyizG5lfIk6dgt5aFTgeMcn3IGJzbxl+6Lfmh90kQqHgQS4FGel0f3DTRkC1Zux7XUTGEICMcTgC0v5HI3QJ0u1HC4v/Q4DwQhxxjIEvlPU+qBkT+0E7DT0TRJr70JCxcA7tt/hpTBEQ4iLgmMT5dD7k3VkR8uTnpuuaPKMZmIPDIaTH0kbSHsQBOI1N09noQT+aJAYEkt8B6KtdkASG31XVHoqs7eqkJeVKYmjlCRqBdSPy5UaNAx06IkCjMaWK76KJjLIyDyDEYl7V+IReRLhi+tqJwKC57pg+Ir6qMOEGbs5pQ6i64uWLZ1JANHlVGEosQagmpTRiSSaAVWNMSbUenW+qHJCYyLxlECwPxcImMXIeUgBQRGvbdCQuDQaeqJIAJL0t2ZAuX3REg5sK29icOwcltAhW9eiMyegHV9UaiAYExPkC1m11dEZCRGoqD2Qb5Tv1XIY1j4gekYrGuLu7VsumdHbP5at/F/ei3y0fGzto619yOL46KLblnb3rq9bOhd3qpO71/FUL69/VQ+22X2w/wBp3+5rnlq37aKX2/kozO1v5VUvmypjs2rqV8Wp30Xjl/KzPoqZe5G/uXlk71fsuHHL/wCv/uxZ/uNL91Nv9qGTWOz+vXbouP7bvS7fN+i4MvkabM7XHvVXdy7N7+qn/wCq/wB/DkyxbL7OPnfojg+Ons6oZOzeL/tX/TlkxbfHqqP1stWVc3etmx/2hv8Ai6DP0Qu7l+zIs9qPuhfrZSwvgcrfL9SLu+rLlb+D7PjF7L6ny9Hf4Mv/2Q==";
// NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var canvas = document.getElementsByTagName("canvas")[0];
// NOTE: create the texture loader
var loader = new THREE.TextureLoader();
var scene = new THREE.Scene();
// NOTE: set the background to a JPEG image of stars
// could also be something like scene.background = loader.load("/images/stars.jpg");
scene.background = loader.load(stars);
var cubeSize = 2;
var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var materials = {}; // NOTE: use an object to store mesh objects
materials.blue = new THREE.MeshPhongMaterial({ color: 0x6699CC });
var cube = new THREE.Mesh(cubeGeometry, materials.blue);
scene.add(cube);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
scene.add(lights.ambient);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 75; // NOTE: this is in degrees and is the vertical field of view
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
// NOTE: "near" and "far" define the frustum which is the region in the 3D space that is going to be projected to the screen
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 5; // NOTE: position the camera back from the scene some so as to "see" the objects
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: required animation function
function animate() {
// NOTE: move the cube around
cube.rotation.x += 0.001;
cube.rotation.y += 0.002;
cube.rotation.z += 0.003;
renderer.render(scene, camera); // NOTE: render the scene
requestAnimationFrame(animate); // NOTE: call the animate function when the next frame is ready, typically 60 per second
}
animate(); // NOTE: call animation function once to get it started
</script>
</body>
</html>
Adding Textures to Objects
Textures can also be applied to object surfaces by "mapping" them to the mesh material.
In this example, a texture is mapped to the sides of the cube:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Add Texture to Objects Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
// NOTE: these images are in base64 format; a url to an image can be used such as:
// var stars = "/images/stars.jpg";
var stars = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAAAhMAAAM0AAAFv//bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgAZABkAwEiAAIRAQMRAf/EAJQAAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYBAQEBAAAAAAAAAAAAAAAAAAABAhAAAgICAgIBBAIDAAAAAAAAAQIAESEDEgQQMRMgUEEUIgUyIyURAAEDAgUCBQQCAwAAAAAAAAEAEQIhMUFRYRIDEHGBkbEiE6HB0TJCFFJiBBIAAwEAAAAAAAAAAAAAAAAAAFAxQf/aAAwDAQACEQMRAAAA/ExC3A1vn2l5XNsMh15w2qXMLEsKFixDWSnql8qrEUXIstM17Tw1BZpYhLnWRYN5pZ6PNU7cevMZADVnoPMQvXnSSCtYLOmFBFhdTJLKE7clA1N5TLqMZFUJoT6vywzBb6RPcNZ//9oACAECAAEFAPqv7SPc/9oACAEDAAEFAPqr7SfU/9oACAEBAAEFAM0bhsRWpS2V07X1lsalV3NwmXNHZ3ddvka+bBSZnwCaPb3/AK6qXZ9To8o0QKnDV8gIBjAgj3AARdE3BBUUkTiKo+AYaMErl4FEgkE6iNfjrt0x18crIlkzINChc9kDLKblGG6UFjbUYilyysjeCpBKsrdX+p7vZ6xQgkEHMQMzw2fFkmyIrcCGIYsWOjvdnTpbUw1GqOTPyTnBlkSrliuLKAICRNe3bpZmLMBcx4GDxZwBPcIm3S+prxBcyScG5Yl5bvbQuZVS8PQArxYZfUdUVJwPL2ORK63+NibGZmexmDjyBy2vYqg1PyKmbqe4bHhdmKuBbA9cWXWP5Pw/2n/EzEbjZgq/6D9T9h+FmCLU6vw/J/zP3f/aAAgBAgIGPwBhlP/aAAgBAwIGPwBhsP/aAAgBAQEGPwAnBfXwVe47KQYEnHJsk1iKKXNGBPHBt0sA69SgJEQif5F2Hki1Rngg1Q1ehnwzPHIhnjditznfd8XRgCdpLkdk/wBOjumxu+K/q7j8O7fsw3WdCMQ8pFgMypccxtlAkSBwa6on8FFpPSos2iZbfk9n+bH0TsJDKv2Tm6Y4UQZaol2bA41QIdxitxrm+fTROOycGuVXTdK11TgdCRSjnoxtpdOMaKPI4aRIABrTMJj5aov9FyjmiTzFvhIoAdVo6/CcpyH0RLCtlQeCceS1Re4N3os02j2QCAFSS20XTP4Yo+iERWUqAO1fFGEg0hQjo+aG0uSAaZ5IxIIkCxGIK5v+ng4zLh4ADySyTIh3wpUKqpIRlcElrVunerus2oVVUTIiUXNYtJ6JwWILjuiT7pSLmRNSSp8HHySjx8n7xBoe6HM0hxzkYiRFDtr90wxq7Ik0PUlr+S1yVOxZaIghycXwQowNjn26HWihMgSEfdGE6xL4spSIA3VZs8kat1cC2ClMCgrIiwdZMMVVh04xzWmBMbSJHbL7ouK0YvZNYIOTogPABMQzXHR2HZPdc3Fwj4eDnYy4okt7bXqitU1yfMIRBJlj+E5rp0AAYj9j0iRPdKT7ot+pelem2j2Z8cley24O7J2BoQ0tcaIua+bpxTJk+PRjggCWD1N2COOShKUTGPJWEsCBSiYh3qmavSlNOlFW5VE3x8ZMQGkf9a4FiTiq3wTuKea7KHISJCTtF3I2nEYLczObCwW3VUs4uq+C9V7ba3X5Xutoub+z8O34eTZ8ztubBsVJrv7Wey1oy16R+b9dF/L492j3X//Z";
var starDust = "data:image/jpeg;base64,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";
var canvas = document.getElementsByTagName("canvas")[0];
// NOTE: create the texture loader
var loader = new THREE.TextureLoader();
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFFFFFF); // NOTE: make background white
var cubeSize = 2;
var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var materials = {}; // NOTE: use an object to store mesh objects
materials.stars = new THREE.MeshPhongMaterial({ map: loader.load(stars) }); // NOTE: load the stars image and map it
materials.starDust = new THREE.MeshPhongMaterial({ map: loader.load(starDust) }); // NOTE: load the star dust image and map it
var sides = [];
sides.push(materials.stars);
sides.push(materials.stars);
sides.push(materials.stars);
sides.push(materials.stars);
sides.push(materials.starDust); // NOTE: front of cube
sides.push(materials.starDust); // NOTE: back of cube
var cube = new THREE.Mesh(cubeGeometry, sides);
scene.add(cube);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
scene.add(lights.ambient);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 75;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 5;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
function animate() {
// NOTE: move the cube around
cube.rotation.x += 0.001;
cube.rotation.y += 0.002;
cube.rotation.z += 0.003;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Displacement Maps
A texture can have a displacement map. A displacement map is a grayscale image that can be used to alter geometry. The lighter areas have greater displacement. The darker areas less so. Each pixel's value in the displacement map image is used to change the position of the vertices of the mesh.
These are the displacement maps use in the following example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Two Planes with Displacement Map Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
// NOTE: these images are in base64 format, a url to an image can be used such as:
// var stars = "/images/stars.jpg";
var stars = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAAAhMAAAM0AAAFv//bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgAZABkAwEiAAIRAQMRAf/EAJQAAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYBAQEBAAAAAAAAAAAAAAAAAAABAhAAAgICAgIBBAIDAAAAAAAAAQIAESEDEgQQMRMgUEEUIgUyIyURAAEDAgUCBQQCAwAAAAAAAAEAEQIhMUFRYRIDEHGBkbEiE6HB0TJCFFJiBBIAAwEAAAAAAAAAAAAAAAAAAFAxQf/aAAwDAQACEQMRAAAA/ExC3A1vn2l5XNsMh15w2qXMLEsKFixDWSnql8qrEUXIstM17Tw1BZpYhLnWRYN5pZ6PNU7cevMZADVnoPMQvXnSSCtYLOmFBFhdTJLKE7clA1N5TLqMZFUJoT6vywzBb6RPcNZ//9oACAECAAEFAPqv7SPc/9oACAEDAAEFAPqr7SfU/9oACAEBAAEFAM0bhsRWpS2V07X1lsalV3NwmXNHZ3ddvka+bBSZnwCaPb3/AK6qXZ9To8o0QKnDV8gIBjAgj3AARdE3BBUUkTiKo+AYaMErl4FEgkE6iNfjrt0x18crIlkzINChc9kDLKblGG6UFjbUYilyysjeCpBKsrdX+p7vZ6xQgkEHMQMzw2fFkmyIrcCGIYsWOjvdnTpbUw1GqOTPyTnBlkSrliuLKAICRNe3bpZmLMBcx4GDxZwBPcIm3S+prxBcyScG5Yl5bvbQuZVS8PQArxYZfUdUVJwPL2ORK63+NibGZmexmDjyBy2vYqg1PyKmbqe4bHhdmKuBbA9cWXWP5Pw/2n/EzEbjZgq/6D9T9h+FmCLU6vw/J/zP3f/aAAgBAgIGPwBhlP/aAAgBAwIGPwBhsP/aAAgBAQEGPwAnBfXwVe47KQYEnHJsk1iKKXNGBPHBt0sA69SgJEQif5F2Hki1Rngg1Q1ehnwzPHIhnjditznfd8XRgCdpLkdk/wBOjumxu+K/q7j8O7fsw3WdCMQ8pFgMypccxtlAkSBwa6on8FFpPSos2iZbfk9n+bH0TsJDKv2Tm6Y4UQZaol2bA41QIdxitxrm+fTROOycGuVXTdK11TgdCRSjnoxtpdOMaKPI4aRIABrTMJj5aov9FyjmiTzFvhIoAdVo6/CcpyH0RLCtlQeCceS1Re4N3os02j2QCAFSS20XTP4Yo+iERWUqAO1fFGEg0hQjo+aG0uSAaZ5IxIIkCxGIK5v+ng4zLh4ADySyTIh3wpUKqpIRlcElrVunerus2oVVUTIiUXNYtJ6JwWILjuiT7pSLmRNSSp8HHySjx8n7xBoe6HM0hxzkYiRFDtr90wxq7Ik0PUlr+S1yVOxZaIghycXwQowNjn26HWihMgSEfdGE6xL4spSIA3VZs8kat1cC2ClMCgrIiwdZMMVVh04xzWmBMbSJHbL7ouK0YvZNYIOTogPABMQzXHR2HZPdc3Fwj4eDnYy4okt7bXqitU1yfMIRBJlj+E5rp0AAYj9j0iRPdKT7ot+pelem2j2Z8cley24O7J2BoQ0tcaIua+bpxTJk+PRjggCWD1N2COOShKUTGPJWEsCBSiYh3qmavSlNOlFW5VE3x8ZMQGkf9a4FiTiq3wTuKea7KHISJCTtF3I2nEYLczObCwW3VUs4uq+C9V7ba3X5Xutoub+z8O34eTZ8ztubBsVJrv7Wey1oy16R+b9dF/L492j3X//Z";
var starDust = "data:image/jpeg;base64,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";
// NOTE: these displacement maps are just the original image converted to grayscale, the lighter areas will displace more than the darker areas
var starsDisplacementMap = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAAAfkAAALvAAAE5f/bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgAZABkAwEiAAIRAQMRAf/EAJEAAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYBAQAAAAAAAAAAAAAAAAAAAAAQAAICAgICAQQDAAAAAAAAAAECABEhAxASMQRBUBMUBSIjJREAAQMCBgAEBAQHAAAAAAAAAQARAhAhMUFRYRIDcYEiEyChsTKR0UIUweHxUmIjBBIBAAAAAAAAAAAAAAAAAAAAUP/aAAwDAQACEQMRAAAA/DxC3Iu+fU53NLIHTEK0MxRAVAsDWRXc86hKFgFM16zx1CpoiCywA1mh24Drz3gQCDVnc4SwdMUiCrkN4KAQ1IADrzCUs1CNjOQUFD6XzgzAvYPYD//aAAgBAgABBQD6j//aAAgBAwABBQD6j//aAAgBAQABBQDNG5mKf4k5XVsZC0RQzGE8at+3S3c32aieQcfkbvsBSzNrZHlQgVOmvuCAYQQR5gGPENwQQWJWK4uHMEq+BRIJEKEJxpb1Rpxd1xkH4E+ayQblHgCzZqKpYsrK3FEEqwb1v1nu+z6xU2QQYgLNDfFkyzAesBIJYsdPuexq1NrYaz4OTwTnm8URBLIiPs1MxJIHI89WYCeYRNmp9bXiZmTDgzEvLe5sC5lVLw1AY4wVjKgWdT28izSN0a7HHkZgq4yOFBqfIqfPB4V8VcAsDx1Kp5br/YfHB62YKv8ASfi/fbrZggqev9rv/n/mf//aAAgBAgIGPwBH/9oACAEDAgY/AEf/2gAIAQEBBj8AdfPyoQzk56JlLsjEmEG5SyD0AkREH9Rdh+CtffJBtKc+qZhJmcY3XJzyxfdGL+l3I8Pg+br9vyPtcuXHLlg6EYh5EsFKEg0okiQOTYqydBi9r7Jlx5+j+5j9E7Pst0xypujdv4oHPVOb+NbJwb6Z/BaltKMcNsU4Qm4YkgAG9tRXsHbEntLe0QWAL50/Kjm6NsVYKy3V6MgEALklmGKb5Z0AFycPHzRjIMRYijoMXJvb6JiCJCxGYK7v+np6jLp6AD2yyD0b+dLECWIJLb4p3v8AAyIlFzcMXsnFiLhEm8pFyTip9PX2Sj19n3xBsW1Q7WIhImIkRYt/VW8USbVelvNqG19UNDhvQ72UJMCI+qMZXiX2RLY3ZHKrt5IyAsMdLrwrAduEgJBiCeMkXF8jomWKATENqKOw8Fqu3r6v9XT3sZdcSW9OGN6tmgAXOf5JzQAC4xNIkSeRflFvtOV6cc8Fjgmyd07A2Zii5v8AVWtVtEHLB7nFlqoylEgTvE5EC1kzO6ZqW+FuECQAxP8AjfwL5rdPZeChNxISf0u5DHMZJ2ZzgMAuO6tg4V/Knpw+dPVhsu39x7PH2ezh7ztyazcc9FJsXs2i3yW9I+79uy/V7fLZ8V//2Q==";
var starDustDisplacementMap = "data:image/jpeg;base64,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";
var canvas = document.getElementsByTagName("canvas")[0];
// NOTE: create the texture loader
var loader = new THREE.TextureLoader();
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFFFFFF); // NOTE: make background white
var planeSize = 4;
var materials = {}; // NOTE: use an object to store mesh objects
materials.stars = new THREE.MeshPhongMaterial({
map: loader.load(stars), // NOTE: load the stars image
displacementMap: loader.load(starsDisplacementMap), // NOTE: load the displacementMap
displacementScale: 1, // NOTE: this changes the amount of the displacement, starts at 0
side: THREE.DoubleSide // NOTE: make both sides visible
});
materials.starDust = new THREE.MeshPhongMaterial({
map: loader.load(starDust), // NOTE: load the star dust image
displacementMap: loader.load(starDustDisplacementMap), // NOTE: load the displacementMap
displacementScale: 1, // NOTE: this changes the amount of the displacement, starts at 0
side: THREE.DoubleSide // NOTE: make both sides visible
});
materials.plane = new THREE.MeshPhongMaterial({ color: 0x6699CC });
var planeBufferGeometry = new THREE.PlaneBufferGeometry(planeSize, planeSize, 100, 100); // NOTE: create a plane buffer, 100 vertices
var planeBuffer1 = new THREE.Mesh(planeBufferGeometry, materials.stars);
planeBuffer1.rotation.x = -Math.PI / 2.5;
scene.add(planeBuffer1);
var planeBuffer2 = new THREE.Mesh(planeBufferGeometry, materials.starDust);
planeBuffer2.rotation.x = -Math.PI / 2.5;
planeBuffer2.position.y = -1;
scene.add(planeBuffer2);
var lights = {};
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 75;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 5;
camera.position.y = -1;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
function animate() {
// NOTE: move the objects around
planeBuffer1.rotation.z += 0.003;
planeBuffer2.rotation.z = planeBuffer1.rotation.z;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Alpha Maps
A texture can have an alpha map, which is a grayscale texture that controls the opacity across the surface. Black is fully transparent while white is fully opaque.
This is the alpha map used in the following example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Plane with Alpha Map and Displacement Map Example</title>
</head>
<body style="background-color:#FFFFFF;">
<canvas style="display: block; width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
// NOTE: these are images in base64 format, a url to an image can be used such as:
// var stars = "/images/stars.jpg";
var stars = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAAAhMAAAM0AAAFv//bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgAZABkAwEiAAIRAQMRAf/EAJQAAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYBAQEBAAAAAAAAAAAAAAAAAAABAhAAAgICAgIBBAIDAAAAAAAAAQIAESEDEgQQMRMgUEEUIgUyIyURAAEDAgUCBQQCAwAAAAAAAAEAEQIhMUFRYRIDEHGBkbEiE6HB0TJCFFJiBBIAAwEAAAAAAAAAAAAAAAAAAFAxQf/aAAwDAQACEQMRAAAA/ExC3A1vn2l5XNsMh15w2qXMLEsKFixDWSnql8qrEUXIstM17Tw1BZpYhLnWRYN5pZ6PNU7cevMZADVnoPMQvXnSSCtYLOmFBFhdTJLKE7clA1N5TLqMZFUJoT6vywzBb6RPcNZ//9oACAECAAEFAPqv7SPc/9oACAEDAAEFAPqr7SfU/9oACAEBAAEFAM0bhsRWpS2V07X1lsalV3NwmXNHZ3ddvka+bBSZnwCaPb3/AK6qXZ9To8o0QKnDV8gIBjAgj3AARdE3BBUUkTiKo+AYaMErl4FEgkE6iNfjrt0x18crIlkzINChc9kDLKblGG6UFjbUYilyysjeCpBKsrdX+p7vZ6xQgkEHMQMzw2fFkmyIrcCGIYsWOjvdnTpbUw1GqOTPyTnBlkSrliuLKAICRNe3bpZmLMBcx4GDxZwBPcIm3S+prxBcyScG5Yl5bvbQuZVS8PQArxYZfUdUVJwPL2ORK63+NibGZmexmDjyBy2vYqg1PyKmbqe4bHhdmKuBbA9cWXWP5Pw/2n/EzEbjZgq/6D9T9h+FmCLU6vw/J/zP3f/aAAgBAgIGPwBhlP/aAAgBAwIGPwBhsP/aAAgBAQEGPwAnBfXwVe47KQYEnHJsk1iKKXNGBPHBt0sA69SgJEQif5F2Hki1Rngg1Q1ehnwzPHIhnjditznfd8XRgCdpLkdk/wBOjumxu+K/q7j8O7fsw3WdCMQ8pFgMypccxtlAkSBwa6on8FFpPSos2iZbfk9n+bH0TsJDKv2Tm6Y4UQZaol2bA41QIdxitxrm+fTROOycGuVXTdK11TgdCRSjnoxtpdOMaKPI4aRIABrTMJj5aov9FyjmiTzFvhIoAdVo6/CcpyH0RLCtlQeCceS1Re4N3os02j2QCAFSS20XTP4Yo+iERWUqAO1fFGEg0hQjo+aG0uSAaZ5IxIIkCxGIK5v+ng4zLh4ADySyTIh3wpUKqpIRlcElrVunerus2oVVUTIiUXNYtJ6JwWILjuiT7pSLmRNSSp8HHySjx8n7xBoe6HM0hxzkYiRFDtr90wxq7Ik0PUlr+S1yVOxZaIghycXwQowNjn26HWihMgSEfdGE6xL4spSIA3VZs8kat1cC2ClMCgrIiwdZMMVVh04xzWmBMbSJHbL7ouK0YvZNYIOTogPABMQzXHR2HZPdc3Fwj4eDnYy4okt7bXqitU1yfMIRBJlj+E5rp0AAYj9j0iRPdKT7ot+pelem2j2Z8cley24O7J2BoQ0tcaIua+bpxTJk+PRjggCWD1N2COOShKUTGPJWEsCBSiYh3qmavSlNOlFW5VE3x8ZMQGkf9a4FiTiq3wTuKea7KHISJCTtF3I2nEYLczObCwW3VUs4uq+C9V7ba3X5Xutoub+z8O34eTZ8ztubBsVJrv7Wey1oy16R+b9dF/L492j3X//Z";
var starsDisplacementMap = "data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAAeAAD/7gAhQWRvYmUAZMAAAAABAwAQAwIDBgAAAfkAAALvAAAE5f/bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMvL0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzArLicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8IAEQgAZABkAwEiAAIRAQMRAf/EAJEAAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYBAQAAAAAAAAAAAAAAAAAAAAAQAAICAgICAQQDAAAAAAAAAAECABEhAxASMQRBUBMUBSIjJREAAQMCBgAEBAQHAAAAAAAAAQARAhAhMUFRYRIDcYEiEyChsTKR0UIUweHxUmIjBBIBAAAAAAAAAAAAAAAAAAAAUP/aAAwDAQACEQMRAAAA/DxC3Iu+fU53NLIHTEK0MxRAVAsDWRXc86hKFgFM16zx1CpoiCywA1mh24Drz3gQCDVnc4SwdMUiCrkN4KAQ1IADrzCUs1CNjOQUFD6XzgzAvYPYD//aAAgBAgABBQD6j//aAAgBAwABBQD6j//aAAgBAQABBQDNG5mKf4k5XVsZC0RQzGE8at+3S3c32aieQcfkbvsBSzNrZHlQgVOmvuCAYQQR5gGPENwQQWJWK4uHMEq+BRIJEKEJxpb1Rpxd1xkH4E+ayQblHgCzZqKpYsrK3FEEqwb1v1nu+z6xU2QQYgLNDfFkyzAesBIJYsdPuexq1NrYaz4OTwTnm8URBLIiPs1MxJIHI89WYCeYRNmp9bXiZmTDgzEvLe5sC5lVLw1AY4wVjKgWdT28izSN0a7HHkZgq4yOFBqfIqfPB4V8VcAsDx1Kp5br/YfHB62YKv8ASfi/fbrZggqev9rv/n/mf//aAAgBAgIGPwBH/9oACAEDAgY/AEf/2gAIAQEBBj8AdfPyoQzk56JlLsjEmEG5SyD0AkREH9Rdh+CtffJBtKc+qZhJmcY3XJzyxfdGL+l3I8Pg+br9vyPtcuXHLlg6EYh5EsFKEg0okiQOTYqydBi9r7Jlx5+j+5j9E7Pst0xypujdv4oHPVOb+NbJwb6Z/BaltKMcNsU4Qm4YkgAG9tRXsHbEntLe0QWAL50/Kjm6NsVYKy3V6MgEALklmGKb5Z0AFycPHzRjIMRYijoMXJvb6JiCJCxGYK7v+np6jLp6AD2yyD0b+dLECWIJLb4p3v8AAyIlFzcMXsnFiLhEm8pFyTip9PX2Sj19n3xBsW1Q7WIhImIkRYt/VW8USbVelvNqG19UNDhvQ72UJMCI+qMZXiX2RLY3ZHKrt5IyAsMdLrwrAduEgJBiCeMkXF8jomWKATENqKOw8Fqu3r6v9XT3sZdcSW9OGN6tmgAXOf5JzQAC4xNIkSeRflFvtOV6cc8Fjgmyd07A2Zii5v8AVWtVtEHLB7nFlqoylEgTvE5EC1kzO6ZqW+FuECQAxP8AjfwL5rdPZeChNxISf0u5DHMZJ2ZzgMAuO6tg4V/Knpw+dPVhsu39x7PH2ezh7ztyazcc9FJsXs2i3yW9I+79uy/V7fLZ8V//2Q==";
var alphaMap = "data:image/jpeg;base64,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";
var canvas = document.getElementsByTagName("canvas")[0];
// NOTE: create the texture loader
var loader = new THREE.TextureLoader();
var scene = new THREE.Scene();
scene.background = new THREE.Color(0x6699CC); // NOTE: make the background blue
var planeSize = 4;
var materials = {}; // NOTE: use an object to store mesh objects
materials.stars = new THREE.MeshPhongMaterial({
map: loader.load(stars), // NOTE: load the stars image
displacementMap: loader.load(starsDisplacementMap), // NOTE: load the displacementMap (not required for alpha maps)
displacementScale: 1, // NOTE: this changes the amount of the displacement, starts at 0 (not required for alpha maps)
alphaMap: loader.load(alphaMap), // NOTE: load the alphaMap
transparent: true, // NOTE: must specify this
side: THREE.DoubleSide // NOTE: make both sides visible
});
var planeBufferGeometry = new THREE.PlaneBufferGeometry(planeSize, planeSize, 100, 100); // NOTE: create a plane buffer, 100 vertices
var planeBuffer = new THREE.Mesh(planeBufferGeometry, materials.stars);
planeBuffer.rotation.x = -Math.PI / 2.5;
scene.add(planeBuffer);
var lights = {};
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 75;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 5;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
function animate() {
// NOTE: move the object around
planeBuffer.rotation.z += 0.003;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
</script>
</body>
</html>
Moving Objects as the User Scrolls
To move an object around as the user scrolls, the parent element of the canvas must have a CSS height
value higher than the browser window, such as 400vh
which is 4 times the window height, and the canvas must have a position
of sticky
or fixed
with a value set for its CSS top
property, usually set to 0
.
A basic example for moving an object during the window scroll event is adding a constant to the rotation of the mesh object as in this example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Basic Scroll Example</title>
</head>
<body style="margin: 0; padding: 0;background-color:#FFFFFF; min-height: 400vh;">
<canvas style="display: block; position: fixed; top: 0; left: calc(50% - 150px); width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0]; // NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFFFFFF); // NOTE: make background white like the HTML page
function createCube(size, material) {
var geometry = new THREE.BoxGeometry(size, size, size);
return new THREE.Mesh(geometry, material);
}
var materials = {}; // NOTE: use an object to store mesh objects (object materials)
materials.lambert = new THREE.MeshLambertMaterial({ color: 0x6699CC });
var objects = {};
objects.lambertCube = createCube(3, materials.lambert);
scene.add(objects.lambertCube);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
scene.add(lights.ambient);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 10;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 40;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: animation function which only updates the canvas and does not modify any objects
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("scroll", function () {
// NOTE: rotate the objects in the scene a fixed amount and forward direction only
for(var object in objects) {
objects[object].rotation.x += 0.009;
objects[object].rotation.y += 0.018;
objects[object].rotation.z += 0.036;
}
});
</script>
</body>
</html>
Using Scroll Position to Set Objects
A better example uses the position of scroll to set the rotation as in this example:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Rotate Element on Scroll Example</title>
</head>
<body style="margin: 0; padding: 0;background-color:#FFFFFF; min-height: 400vh;">
<canvas style="display: block; position: fixed; top: 0; left: calc(50% - 150px); width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0]; // NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFFFFFF); // NOTE: make background white like the HTML page
function createCube(size, material) {
var geometry = new THREE.BoxGeometry(size, size, size);
return new THREE.Mesh(geometry, material);
}
var materials = {}; // NOTE: use an object to store mesh objects (object materials)
materials.lambert = new THREE.MeshLambertMaterial({ color: 0x6699CC });
var objects = {};
objects.lambertCube = createCube(3, materials.lambert);
scene.add(objects.lambertCube);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
scene.add(lights.ambient);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 10;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
camera.position.z = 40;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: animation function which only updates the canvas and does not modify any objects
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("scroll", function () {
// NOTE: get the position of the scroll value (0 < position < 1)
var position = window.scrollY / (canvas.parentElement.offsetHeight - window.innerHeight);
// NOTE: rotate the objects in the scene
for(var object in objects) {
objects[object].rotation.x = position * 2;
objects[object].rotation.y = position * 4;
objects[object].rotation.z = position * 8;
}
});
</script>
</body>
</html>
Using Scroll Position to Set Camera Position
In this example the camera is moved forward as the user scrolls:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>THREE.js - Zoom on Scroll Example</title>
</head>
<body style="margin: 0; padding: 0;background-color:#FFFFFF; min-height: 400vh;">
<canvas style="display: block; position: fixed; top: 0; left: calc(50% - 150px); width: 300px; height: 300px;"></canvas>
<script src="/js/three.min.js"></script>
<script type="text/javascript">
var canvas = document.getElementsByTagName("canvas")[0]; // NOTE: get the canvas element - this can be created with document.createElement("canvas") and added with document.body.appendChild(createdCanvasObject);
var scene = new THREE.Scene();
scene.background = new THREE.Color(0xFFFFFF); // NOTE: make background white like the HTML page
function createCube(size, material) {
var geometry = new THREE.BoxGeometry(size, size, size);
return new THREE.Mesh(geometry, material);
}
var materials = {}; // NOTE: use an object to store mesh objects (object materials)
materials.lambert = new THREE.MeshLambertMaterial({ color: 0x6699CC });
var objects = {};
var cubeSize = 3;
objects.lambertCube = createCube(cubeSize, materials.lambert);
scene.add(objects.lambertCube);
var lights = {};
lights.ambient = new THREE.AmbientLight(0xffffff, .5);
scene.add(lights.ambient);
lights.directional = new THREE.DirectionalLight(0xffffff, 1);
lights.directional.position.set(10, 25, 15);
scene.add(lights.directional);
var fieldOfView = 10;
var aspectRatio = canvas.offsetWidth / canvas.offsetHeight;
var near = 1;
var far = 10000;
var camera = new THREE.PerspectiveCamera(fieldOfView, aspectRatio, near, far);
var cameraPositionZ = 50;
camera.position.z = cameraPositionZ;
scene.add(camera);
var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.offsetWidth, canvas.offsetHeight);
// NOTE: animation function which only updates the canvas and does not modify any objects
function animate() {
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
window.addEventListener("scroll", function () {
// NOTE: get the position of the scroll value (0 < position < 1)
var position = window.scrollY / (canvas.parentElement.offsetHeight - window.innerHeight);
// NOTE: rotate the objects in the scene
for(var object in objects) {
objects[object].rotation.x = position * (Math.PI / 2);
objects[object].rotation.y = position * Math.PI;
objects[object].rotation.z = position * (Math.PI * 1.5);
}
// NOTE: this will zoom the camera into the scene
// cubeSize is added to make the camera not go beyond the cube as it zooms
camera.position.z = cameraPositionZ - position * cameraPositionZ + cubeSize;
});
</script>
</body>
</html>
Other Things to Consider
- Because almost everything is in radians,
Math.PI
is a very close friend. Use it when rotating objects. - How to increase performance.
- How to create, cast and receive shadows.
- How to make the background transparent.
- How to set the background image to cover the scene without stretching it.
- How to create transparency using Portable Network Graphics (PNG) images.
- How to make a mesh object appear to follow the mouse cursor by moving the camera.
- How to make mesh objects appear to watch the moving mouse cursor.
- How to set camera z-position based on object's size.
- Raytracer: How to access individual objects on the canvas.
-
If the canvas is to be resized when the browser window is resized then the canvas CSS width and height properties must be set and then the camera aspect ratio must be updated, as done in this code snippet:
window.addEventListener("resize", function () { var width = window.innerWidth, height = window.innerHeight; canvas.style.width = width + "px"; // NOTE: set the canvas width canvas.style.height = height + "px"; // NOTE: set the canvas height camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); });