WebGL

Basics

Core WebGL concepts.

Tags

WebGL

Core Concepts

interactive editor at mrdoob github threejs editor

Animation

Appears smooth at at least 60 FPS

Film requires only 24 FPS

Z Depth

0 close to camera 1 far from camera

Triangulation

All objects are drawn with triangles

Baking

Stores information such as colour on the object in order to avoid calculations

Cartesian Coordinate System

point

(x,y,z) gives position vector

(x,y,z) gives direction

x parallel to bottom of screen

y parallel to side of screen

z into and out of screen

Vectors

Normalize A Vector

length = sqrt(x^2 + y^2 + z^2)

(x/length,y/length,z/length)

length = sqrt(3^2 + -4^2 + 0^2)
length = sqrt(25)
length = 5

normalized = (3/5,-4/5,0/5)
normalized = (0.6,-0.8,0)

Angle Between Vectors

Use Dot Product between them to obtain the cos

vector A * vector B = AxBx + AyBy + AzBz

Use acos to obtain the angle in degrees

acos(cos)

A = (0.0,-0.60,0.80)
B = (0.80,-0.36,0.48)

A*B = (0.0*0.80) + (-0.60*-0.36) + (0.80*0.48)
A*B = (0.0) + (0.216) + (0.384)
A*B = 0.6

cos theta = 0.6

acos(0.6) = 53.13 deg

Axis of Rotation

Use Cross Product of two vectors.

length(A x B) = sin θ * length(A) * length(B)

A x B = (AyBz-AzBy,AzBx-AxBz,AxBy-AyBx)

var axisOfRotation = new THREE.Vector3();
axisOfRotation.crossVectors(someObjectAxis,new THREE.Vector(0,1,0));
if (axisOfRotation.length() == 0) {
  axisOfRotation.set(1,0,0); // choose based on direction of rotation
}
axisOfRotation.normalize();

Matrices

Transformations are carried out via a [4x4] Transform Matrix.

new coordinate = matrix * coordinate D = N * C

Dot Product of vector C with matrix N give new vector D.

In this case points and vectors have a fourth parameter to distinguish them.

1 represents a point , 0 represents a vector

When transforming points and vectors the fourth parameter should result in 0 or 1.

If not 0 or 1 then calculation was done incorrectly.

Identity Matrix

1000
0100
0010
0001

Multiplying any vector by the identity matrix results in the same vector.

var mtx = new THREE.Matrix4(); makes an identity matrix

mtx.identity(); resets matrix to the identity matrix , useful after transforms have been applied

Translation Matrix

100Tx
010Ty
001Tz
0001

Place x,y,z coordinates from a vector into Tx,Ty,Tz.

For multiple vectors add their respective x,y,z coordinates and place them into the corresponding Tx,Ty,Tz.

var mtx = new THREE.Matrix4 (
  1,0,0,10, // Tx
  0,1,0,15, // Ty
  0,0,1,-5, // Tz
  0,0,0,1);

Matrix translation using built in operation.

mtx.makeTranslation(x,y,z);
someObject.matrix = mtx; 
someObject.matrixAutoUpdate = false; // overrides default order of Pos,Rot,Sca and uses this matrix instead

Rotation Matrix

cos θ-sin θ00
sin θcos θ00
0010
0001

Scale Matrix

Sx000
0Sy00
00Sz0
0001

Uniformly scaling a matrix maintains the normal direction but the normal needs to be renormalized.

Non uniformly scaling a matrix changes the direction of the normal. To realign the normal use transpose and inverse.

Transpose Matrix

Returns a reoriented matrix with the main diagonal unchanged.

Matrix M

AEIM
BFJN
CGKO
DHL1

Becomes M^T

ABCD
EFGH
IJKL
MNO1

Inverse Matrix

Undoes a matrix operation.

M * M^-1 = I

Translation Matrix M

100Tx
010Ty
001Tz
0001

Becomes

I

100-Tx
010-Ty
001-Tz
0001

Rotation Matrix M

cos θ-sin θ00
sin θcos θ00
0010
0001

Becomes

I

cos θsin θ00
-sin θcos θ00
0010
0001

Scale Matrix M

Sx000
0Sy00
00Sz0
0001

Becomes

I

1/Sx000
01/Sy00
001/Sz0
0001

matrix4.getInverse();

Mirror Matrix

Mx000
0My00
00Mz0
0001

Where one of Mx,My,Mz is -1 and the others are 1.

.scale.z = -1 constructs a mirror matrix , choose any axis

These are difficult to use since they alter directions and normals.

Searches for any mirror matrices in the code.

if (mtx.determinant()<0) {
  console.log("mirror found");
}

Matrix Zones

N11N21N31N41
N12N22N32N42
N13N23N33N43
0001

Linear Transforms occur in N11:N33

Translations occur in N41,N42,N43

Projective transforms occur in 0,0,0,1

Matrix Decomposition

mtx.decompose(T,R,S)

Returns T and S as vectors

Returns R as a quaternion

Shaders

Vertex Shader

Fragment Shader

Colours

THREE.MeshLamberMaterial() declares the material

.color.setRGB() sets the colour , values are between 0 and 1

var someMaterial = new THREE.MeshLambertMaterial();
someMaterial.color.setRGB(0.5,0.5,0.5);

Lighting

Emissive Emits colour , so not affected by other colours

Ambient Used to light areas not receiving light directly as they should

Diffuse Flat mat finish

Specular Shininess

Object Surface Colour

emissive + ambient + (diffuse + specular)

Shading - calculated from surface normals , lighting , eye position

Interpolation - calculated from two normals and filling in smaller normals in between them

Look into tile based transparency for when you need to render transparent objects inside other transparent objects.

Directional Light

Light source is infinitely far away but traces a line through position given towards the origin.

THREE.DirectionalLight(); colour,intensity

.position.set(); x,y,z (this is the position that the light ray will pass through)

Point Light

Emits light in all directions. Max distance is the distance at which the light intensity is zero.

colour,intesity,max distance

Ambient Lighting

Since light in the model does not bounce similar to real light, ambient light is added to mimic the result of bouncing light.

THREE.AmbientLight() colour,intensity

Spot Light

Focused light that displays a cone and casts shadows.

Hemisphere Light

Light coming from all directions.

Normals facing upwards receive one colour.

Normals facing downwards receive a different colour.

Any normals in between these angles receive a blend of the two colours.

Fog

Not really a light but works nicely with lights.

Uses such as fog of war , making ray light shadows as in through clouds , or simply obfuscating the limits of the scene.

fog linear fall off

FoxExp2 exponential fall off , more realistic

Lighting Materials

.MeshBasicMaterial does not require a light source to be seen , all other materials do

.MeshLambertMaterial gives a matte finish

.MeshPhongMaterial gives shiny finish

.MeshBlinnMaterial gives very shiny finish

Textures

Materials applied to an object that affect its lighting such as reflection and absorption.

2D images are applied to an objects surface as a material.

var someTexture = new THREE.ImageUtils.loadTexture(); png gif image

var someMaterial = new THREE.MeshBasicMaterial({map: someTexture});applies image to object surface

Texels

Positions on a surface are texels instead of pixels.

For a square the corners are 0,0 (bottom,left) 0,1 (top,left) 1,1 (top,right) 1,0 (bottom,right)

Any position within the square has a value between 0 and 1 for x and y.

Wrapping

RepeatWrapping , MirroredRepeatWrapping , ClampToEdgeWrapping

someTexture.wrapS = someTexture.wrapT = THREE.ClampToEdgeWrapping;

Transform

Repeat

Repeating a texture pattern.

var someTexture = new THREE.Texture();
someTexture.repeat.set();
someTexture.offset.set();

Scale

Scaling a texture pattern can lead to pixelation artifacts.

MipMap is a method for scaling such that texels and pixels have a near 1:1 ratio.

To make use of mipmapping make sure the texture has dimensions divisible by 2. Such as 64x64.

someTexture.minFilter = THREE.NearestFilter;
someTexture.minFilter = THREE.LinearFilter;
someTexture.minFilter = THREE.LinearMipMapLinearFilter;

Transparency

.transparent = true true , false

.opacity = 0.5 0 to 1

var geometry = new THREE.SphereGeometry(2,12,10); // radius,verteces
var material1 = new THREE.MeshLambertMaterial({color:0xBB0000});
material1.transparent = true;
material1.opacity = 0.6;

Particles

For making clouds , lens flares.

Particles can render whole scenes without triangulation.

To help illusion of 3d space make the same side always face the viewer.

var someTexture = THREE.ImageUtils.loadTexture("some.png");
var someMaterial = new THREE.ParticleBasicMaterial ({
  size:35, // pixels
  sizeAttenuation:false,
  map:disk,
  transparent,true
});
someMaterial.color.setHSL(0.9,0.2,0.5);

var particles = new THREE.ParticleSystem(geometry,material);
particles.sortParticles = true;
scene.add(particles);

var someGeometry = new THREE.Geometry();
for (var i=0;i<8000;i++) {
  var vertex = new THREE.Vector3();
  do {
    vertex.x = 2000 * Math.random() - 1000;
    vertex.y = 2000 * Math.random() - 1000;
    vertex.z = 2000 * Math.random() - 1000;
  } while (vertex.length() > 1000);
}

Displacement Mapping

Adds height value to each vertex in order to add bumps to object surface.

Normal Mapping

Allows for light to interact with the displacement mapping.

Light Mapping

Allows for shadows from other objects to be displayed on themselves.

Best for static objects.

SkyBox Mapping

The viewer is placed in the middle of a cube. Each wall of the cube has a scene attached such that when all are viewed there are no seams visible between them and the illusion is that of a continuous panorama.

Cube map where

Reflection Mapping

Material applied to objects so that it reflects the environment map on the material’s surface.

Material map is similar to the skybox but it is used for the reflections only.

Refraction Mapping

Light passing through an object changes direction.

If index of refraction of 2nd material is greater than 1st material the angle get smaller.

If index of refraction of 2nd material is smaller than 1st material the angle get larger.

Glossy Reflection Mapping

Gives a matted semi reflective surface such as a metal object.

Achieved by blurring the environmental map.

Diffuse Reflection Mapping

Gives a further matted reflection for farther objects and makes brighter points such as a sun more realistically prominent.

Anisotropic Material

Light reflects in different ways depending on the orientation of the object.

Such as velvet and brushed aluminum.

Isotropic Material

Light reflects uniformly regardless of the orientation of the object.

Such as gold and glass.

Shaders

Cameras

Camera looks towards - z axis.

THREE.OrbitControls(someCamera);

Projections

Camera Objects

THREE.OrthographicCamera(); left , right , top , bottom , near , far

THREE.PerspectiveCamera(); field of view (deg) , aspect ratio , near , far

try to set near as far away from the camera eye as possible to avoid z fighting

decreasing FOV makes object larger

decreasing FOV and zooming in or increasing FOV and zooming out creates a surreal cinematic effect

Making changes to these parameters after they have been constructed requires an update call

camera.updateProjectionMatrix() this is rarely needed since the cameras parameters are usually set and unchanged

Target

Point camera view is set to as the camera moves and rotates around world.

cameraControls.target.set(); x,y,z

Movements

Effects

Depth of Field - gives a blur effect to distant objects and sharp focus to near objects

Geometries

.BoxGeometry()

.SphereGeometry() radius , triangle width , triangle height

Transforms

Rendered in the order scale,rotation,position

.scale.z scales object along given axis (x,y,z) , passed numbers less than 1 shrink object

.rotation.z rotates object around given axis (x,y,z) in radians

.position.z changes position (x,y,z) of object as determined by its center

Use Object3D to change the order of applied transformations.

// make an object (someName) and apply the transform you want to happen first
var someName = new THREE.Mesh ( new THREE.CubeGeometry(100,5,5),someMaterial);
block.position.x = 40;
// make new object with Object3D
var otherName = new THREE.Object3D();
otherName.add(someName);
// apply second transform to second object
otherName.rotation.y = -50 * Math.PI/180;
// finish scene
scene.add(otherName);

Animation

User Interaction

Add an event listener for mouse,keyboard,touchscreen and set a function based on the event.

document.addEventListener('mousedown',onDocumentMouseDown,false);
function onDocumentMouseDown(event) {
  event.preventDefault();
  // code block
};

Pick An Object

document.addEventListener('mousedown',onDocumentMouseDown,false);
function onDocumentMouseDown(event) {
  event.preventDefault();
  var mouseVector = new THREE.Vector3(
    2*(event.clientX/canvasWidth) - 1,
    1-2*(event.clientY/canvasHeight)
  );
  var projector = new THREE.Projector();
  var raycaster = projector.pickingRay(mouseVector.clone(),camera);
};
var intersects = raycaster.intersectObjects(objects);
if(intersects.length > 0) {
  intersects[0].object.material.color.setRGB(
  Math.random(), Math.random(), Math.random()
  );
  var sphere = new THREE.Mesh(sphereGeom,sphereMaterial);
  sphere.position = intersects[0].point;
  scene.add(sphere);
}

Rendering

renderer.render(scene,camera);
function animate() {
  window.requestAnimationFrame(animate);
  render();
}
function render() {
  var delta = clock.getDelta(); // returns ms since getDelta was called
  cameraControls.update(delta);
  renderer.render(scene,camera)
}

Keyframe

Tween between one frame and the next.

To avoid abrupt changes between positions use a spline curve or an ease.

Skinning

Giving weights to a joint in a bone skeleton model to add realism to its movement.

Skeletal Animation

Morph Targets

Motion Capture

Simulation

Set Up A Scene

Loading In A Model

look at tutorial 7 from three js sonar systems

Examples

Basic Set Up

// set up scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000); // field of view degrees,aspect ratio,near,far
var light = new THREE.PointLight(0xFFFFFF,1,500); // colour,intensity,distance
var renderer = new THREE.WebGLRenderer({antialias:true});

// set up camera
camera.position.z = 5;

// set up light
light.position.set(10,0,25); // x,y,z

// set up canvas parameters
renderer.setClearColor("#502020"); // canvas background colour
renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2);
var canvasContainer = document.querySelector('#canvasContainer');
canvasContainer.appendChild(renderer.domElement); // attaches canvas to passed location

// set up canvas to respond to resizing of window
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2); // sets canvas size on window resize /2
  camera.aspect = window.innerWidth/window.innerHeight; // sets camera aspect on window resize
  camera.updateProjectionMatrix();
})

// rendering of scene and objects
var render = function() {
  requestAnimationFrame(render);  // avoids distortion of objects during resizing of canvas
  //animation(); // calls animations
  renderer.render(scene,camera);
}

// 1st object sphere
var geometry = new THREE.SphereGeometry(1,8,8); // radius,verteces
var material = new THREE.MeshLambertMaterial({color:0xBB0000});
var mesh1 = new THREE.Mesh(geometry,material);

// 2nd object cube
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshLambertMaterial({color:0xFFCC00});
var mesh = new THREE.Mesh(geometry,material);

// 1st object transformations 
//mesh.rotation.set(45,0,0); // x,y,z in degrees
mesh1.position.set(-2,0,0); // x,y,z moves along axis

// animation sequence to occur upon load if called
var animation = function() {
  mesh.rotation.x += 0.02;
  mesh.rotation.y += 0.01;
}

// add objects to scene
scene.add(light);
scene.add(mesh);
scene.add(mesh1);

// last step
render();

// gs animation
//this.tl = new TimelineMax().delay(.3); // delays start of animation by 3ms after page load
this.tl = new TimelineMax({paused:true}); // pauses animation so it does not begin at load
this.tl.to(this.mesh.scale,1,{x:2,ease:Expo.easeOut});
this.tl.to(this.mesh.scale,.5,{x:.5,ease:Expo.easeOut});
this.tl.to(this.mesh.position,.5,{x:2,ease:Expo.easeOut});
this.tl.to(this.mesh.rotation,.5,{y:Math.PI*.5,ease:Expo.easeOut},"=-1.5");

document.body.addEventListener('click',() => {
  this.tl.play(); // plays animation when anywhere on page is clicked
})

Click On Object To Animate It

// set up scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000); // field of view degrees,aspect ratio,near,far
var light = new THREE.PointLight(0xFFFFFF,1,500); // colour,intensity,distance
var renderer = new THREE.WebGLRenderer({antialias:true});

// set up camera
camera.position.z = 5;

// set up light
light.position.set(10,0,25); // x,y,z

// set up canvas parameters
renderer.setClearColor("#502020"); // canvas background colour
renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2);
var canvasContainer = document.querySelector('#canvasContainer');
canvasContainer.appendChild(renderer.domElement); // attaches canvas to passed location

// set up canvas to respond to resizing of window
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2); // sets canvas size on window resize /2
  camera.aspect = window.innerWidth/window.innerHeight; // sets camera aspect on window resize
  camera.updateProjectionMatrix();
})

// rendering of scene and objects
var render = function() {
  requestAnimationFrame(render);  // avoids distortion of objects during resizing of canvas
  renderer.render(scene,camera);
}

// raycaster to interact with canvas objects , needed for mouse events
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

// object cube
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshLambertMaterial({color:0xFFCC00});
var mesh = new THREE.Mesh(geometry,material);

// object cube 2
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshLambertMaterial({color:0xFFCC00});
var mesh2 = new THREE.Mesh(geometry,material);
mesh2.position.y = 2;

// add objects to scene
scene.add(light);
scene.add(mesh);
scene.add(mesh2);

// mouse event function
function onMouseMove(event) {
  event.preventDefault();
  mouse.x = (event.clientX/window.innerWidth) * 2 - 1;
  mouse.y = - (event.clientY/window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse,camera);
  var intersects = raycaster.intersectObjects(scene.children,true);
  for (var i=0; i<intersects.length; i++) {
    // gs animation
    this.tl = new TimelineMax();
    this.tl.to(intersects[i].object.scale,1,{x:2,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.scale,.5,{x:.5,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.position,.5,{x:2,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.rotation,.5,{y:Math.PI*.5,ease:Expo.easeOut},"=-1.5");
  }
}

// last step
render();

// detects click event
window.addEventListener('click',onMouseMove); // mousemove detects mouse hover , click detects mouse click

Array Of Objects That Animate On Click

// set up scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000); // field of view degrees,aspect ratio,near,far
var light = new THREE.PointLight(0xFFFFFF,1,1000); // colour,intensity,distance
var light2 = new THREE.PointLight(0xFFFFFF,1.5,1000); // colour,intensity,distance
var renderer = new THREE.WebGLRenderer({antialias:true});

// set up camera
camera.position.z = 5;

// set up light
light.position.set(0,0,0); // x,y,z
light2.position.set(0,0,25);

// set up canvas parameters
renderer.setClearColor("#502020"); // canvas background colour
renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2);
var canvasContainer = document.querySelector('#canvasContainer');
canvasContainer.appendChild(renderer.domElement); // attaches canvas to passed location

// set up canvas to respond to resizing of window
window.addEventListener('resize', () => {
  renderer.setSize(window.innerWidth/1.2,window.innerHeight/1.2); // sets canvas size on window resize /2
  camera.aspect = window.innerWidth/window.innerHeight; // sets camera aspect on window resize
  camera.updateProjectionMatrix();
})

// rendering of scene and objects
var render = function() {
  requestAnimationFrame(render);  // avoids distortion of objects during resizing of canvas
  renderer.render(scene,camera);
}

// raycaster to interact with canvas objects , needed for mouse events
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

// object cube
var geometry = new THREE.BoxGeometry(1,1,1);
var material = new THREE.MeshLambertMaterial({color:0xFFCC00});

meshX = -10;
for(var i=0; i<15; i++) {
  var mesh = new THREE.Mesh(geometry,material);
  mesh.position.x = (Math.random() - 0.5) * 10;
  mesh.position.y = (Math.random() - 0.5) * 10;
  mesh.position.z = (Math.random() - 0.5) * 10;
  scene.add(mesh);
  meshX += 1;
}
// add objects to scene
scene.add(light);
scene.add(light2);

// mouse event function
function onMouseMove(event) {
  event.preventDefault();
  mouse.x = (event.clientX/window.innerWidth) * 2 - 1;
  mouse.y = - (event.clientY/window.innerHeight) * 2 + 1;
  raycaster.setFromCamera(mouse,camera);
  var intersects = raycaster.intersectObjects(scene.children,true);
  for (var i=0; i<intersects.length; i++) {
    // gs animation
    this.tl = new TimelineMax();
    this.tl.to(intersects[i].object.scale,1,{x:2,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.scale,.5,{x:.5,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.position,.5,{x:2,ease:Expo.easeOut});
    this.tl.to(intersects[i].object.rotation,.5,{y:Math.PI*.5,ease:Expo.easeOut},"=-1.5");
  }
}

// last step
render();

// detects click event
window.addEventListener('click',onMouseMove); // mousemove detects mouse hover , click detects mouse click

Rotating Sphere With Lines

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(25,window.innerWidth/window.innerHeight,0.1,1000);
var renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);

var geometry = new THREE.SphereGeometry(1);
var edges = new THREE.EdgesGeometry(geometry);
var line = new THREE.LineSegments(edges,new THREE.LineBasicMaterial({color:0xffffff}));
var material = new THREE.MeshBasicMaterial({color: 0x00ff00});
var sphere = new THREE.Mesh(geometry,material);
scene.add(sphere);
scene.add(line);

material.color.setRGB(.95,.5,.5); // overides color set above

camera.position.z = 5;

function animate() {
  requestAnimationFrame(animate);
  sphere.rotation.x += 0.01;
  sphere.rotation.y += 0.01;
  line.rotation.x += 0.01;
  line.rotation.y += 0.01;
  renderer.render(scene,camera);
}

animate();

notes navigation

Current URL: /notes/09WebGL/00-webgl-basics/

total notes 36