Skip to content

Instantly share code, notes, and snippets.

@rotoglup
Created March 5, 2020 14:52
Show Gist options
  • Save rotoglup/815f704e765df35fce38c5c50f9af006 to your computer and use it in GitHub Desktop.
Save rotoglup/815f704e765df35fce38c5c50f9af006 to your computer and use it in GitHub Desktop.
Once more, I'm struggling to wrap my head around the threejs API, here are my notes.
> **IMPORTANT** base on threejs `r114` (march 2020)
API topics covered :
* Scene and Object3D, hierarchy, transforms
* Maths, linear algebra
* DirectionalLight, as I'm currently trying to understand how to attach one of them to a Camera...
Handy links :
* source code: https://github.com/mrdoob/three.js
* docs: https://threejs.org/docs
* examples: https://threejs.org/examples
Scene and Object3D
==============================================================================
The `Scene` represents a fairly typical *Scene graph* (or maybe more a *Scene tree*)
composed of `Object3D` instances.
The `Scene` itself is a kind of `Object3D`.
Each `Object3D` had an `id: number`, a `name: string` and an `uuid: string`.
## Hierarchy
`Object3D` have a *parent/children* hierarchical relationship, manipulated through the use of :
* `someParent.add(someChild)`, or `attach` to preserve the child world position in the process
* `someParent.remove(someChild)`
## Coordinate systems and Transforms
Coordinate systems are right-handed systems, with `Y` pointing up.
1. `Object3D` transform is *defined* by the following PRS values, relative to parent :
* `position`: `Vector3`
* `quaternion`: `Quaternion`, which is implicitly mirrored in `rotation` as Euler angles
* `scale`: `Vector3`
2. `Object3D` additionally store its transform through :
* `matrix`: the transform from *local* to *parent* space
* computed from PRS values
* `matrixWorld`: the transform from *local* to *world*
> NOTE - inverse matrices are not stored, and are computed when needed (for exemple, in `worldToLocal` method)
3. These transforms are updated by the `updateMatrix` and `updateWorldMatrix` methods :
* updates can propagate through parents/children depending on arguments
* updates can be skipped depending on flags :
* `matrixAutoUpdate` can be disabled for an object, or generally
* `matrixWorldNeedsUpdate` is a 'dirty flag'
4. By default, the scene general matrix update propagation is done by the renderer :
* `if (scene.autoUpdate === true) scene.updateMatrixWorld();`
* which updates, by default, all `matrix` and dirty `matrixWorld` values, for all the objects in the scene hierarchy
Maths
==============================================================================
The linear algebra types are :
* `Vector3` storing either positions or directions
* `Matrix3`
* `Matrix4`
> My understanding is that math methods never allocate a result value. I suppose for performance reasons related to GC.
Scalar values are stored in plain javascript numbers (so mostly `float64` values)
Matrices are *stored* in linear arrays, it seems in a *column-major order*: the values for a column are next to each other in the array.
* NOTE - `Matrix.set(...)` takes values in a *row-major* format, to be able to format code is a human-readable math-like way
* on the other hand, `Matrix.fromArray` takes values in a *column-major* way
## Matrix inverse
Matrix inverse is calculated using the *determinant* of the matrix.
An exception is optionally thrown when not inversible (`det === 0`).
## GLSL equivalences
Vector3
* `v1.dot(v2)` <=> `dot(v1,v2)`
* `v1.cross(v2)` <=> `v1 = cross(v1,v2)`
* `v1.crossVectors(v1,v2)` <=> `v = cross(v1,v2)`
* `v3.applyMatrix3(M)` <=> `v3 = M * v3`
* `v3.applyMatrix4(M)` <=> `vec4 tmp = M * vec4(v3, 1.0); v3 = v4.xyz / v4.w`
Matrix3/Matrix4
* `M1.multiply(M2)` <=> `M1 = M1 * M2`
* `M1.premultiply(M2)` <=> `M1 = M2 * M1`
* `M.multiplyMatrices(M1, M2)` <=> `M = M1 * M2`
DirectionalLight
==============================================================================
`DirectionalLight` orientation is defined by :
* `position`: `Vector3`
* `target`: `Object3D`
1. There's no API to query the actual light direction :
* It seems only calculated in `WebGLLights.js` to feed the `direction` GLSL uniform
* The calculation extracts world positions from `light.worldMatrix` and `light.target.worldMatrix`
2. By default, the `target` object is not part of the `scene` :
* its position is `[0,0,0]`
* its `worldMatrix` must be explicity updated if the position moves - *or* the target should be added to the scene hierarchy
3. This `target` behaviour is not the same as the one used in Cameras
* there's an old proposal to change the behaviour : [Github issue, Remove target from DirectionalLight and SpotLight. #5079](https://github.com/mrdoob/three.js/issues/5079)
* there's also an old PR that is related, so work is stalled for now : [Make Light targets optional #14658](https://github.com/mrdoob/three.js/pull/14658)
## DirectionalLightHelper
Is used to draw a helper object representing the light.
> NOTE - It should be added to the `scene` not as a child of another object.
>
> * It seems that the underlying geometry is updated only using *world coordinates* taken from the light, so it breaks if the helper *parent* is not the world.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment