Three.js — Trasformazioni: position, rotation, scale, quaternion

Gianluca Lomarco
7 min readMay 22, 2023

--

In un articolo precedente abbiamo mosso i primi passi con three.js e abbiamo creato una scena base con un cubo al centro che ruota su se stesso.

Oggi vediamo come muovere, ruotare e scalare gli oggetti nello spazio 3D, operazioni indispensabili quando volgiamo creare scene complesse con molti oggetti posizionati accuratamente tra di loro.

Cos'è una trasformazione

Quando si parla di trasformazioni si fa riferimento a tutta una serie di operazioni che hanno come obiettivo quello di modificare le caratteristiche degli oggetti in termini di posizione, rotazione e dimensione che questi hanno nello spazio.

Dobbiamo precisare che gli oggetti che possiamo creare con three.js ricadono in una categoria di oggetti chiamati "corpi rigidi", ovvero oggetti non deformabili, per cui, la distanza relativa tra i vertici di uno stesso oggetto non cambia.

Quindi con le trasformazioni non deformiamo gli oggetti, ma semplicemente li muoviamo / ruotiamo in modo "rigido".

Un esempio

Se trasliamo un cubo lungo l'asse X di 5 unità, tutti gli 8 vertici del cubo trasleranno delle stesse 5 unità lungo l'asse X. Se impostiamo una rotazione al cubo di un certo angolo 𝛼, tutti i suoi 8 vertici ruoteranno dello stesso angolo attorno all'asse usato come riferimento.

Object3D

In three.js esiste una classe chiamata Object3D che contiene tutte le proprietà e i metodi per trasformare i nostri oggetti. Tali metodi e proprietà vengono ereditate da tutte le classi che estendono la classe Object3D, come per esempio le classi Mesh, le classi per le Camera e anche le classi per le Luci.

Nella documentazione è possibile vedere ogni classe quale classe sta estendendo all'inizio delle singole pagine, come puoi vedere nello screenshot qui sotto.

La classe Object3D ci mette a disposizione 4 proprietà di trasformazione per muovere, ruotare e scalare i nostri oggeti.

  • position
  • rotation
  • scale
  • quaternion

Muovere gli oggetti

Tramite la proprietà position possiamo muovere i nostri oggetti nello spazio 3D impostando le coordinate desiderate corrispondenti ai 3 assi di riferimento dello spazio XYZ. La proprietà position non è altro che un vettore 3D ed è un istanza della classe Vector3 che al suo interno contiene le proprietà x, y e z.

Ci sono diversi modi per modificare questi 3 valori.

Il modo più diretto per farlo è assegnando alle proprietà un nuovo valore:

mesh.position.x = 5
mesh.position.y = -2

In alternativa possiamo sfruttare i metodi che mi mette a disposizione la classe Vector3, come per esempio: set(), setX(),setY(), setZ(), copy(),

//imposta x = 5, y = -2, z = 3
mesh.position.set(5,-2,3)

//imposta x = 5, y = -2, z = 3
mesh.position.setX(5)
mesh.position.setX(-2)
mesh.position.setX(3)

//copiare i valori da un altro vettore
mesh.position.copy( meshB.position )

Ci sono tanti altri metodi che i vettori ci mettono a disposizione per normalizzare i vettori e per fare operazioni tra vettori come somme, differenze, calcolare distanze, angoli e per calcolare prodotto scalare e prodotto vettoriale tra vettori.

Axes helper

Per aiutarsi a muovere gli oggetti nello spazio può tornare utile inserire nella scena un AxesHelper (link alla documentazione) in questo modo.

const axesHelper = new THREE.AxesHelper(2)
scene.add(axesHelper)

Questo elemento ci permette di visualizzare nello spazio 3D la posizione e la direzione dei tre assi XYZ, ognuno con un colore diverso; verde l'asse Y, rosso l'asse X, infine blue l'asse Z. È possibile personalizzare la lunghezza e il colore delle linee che identificano gli assi.

Scalare gli oggetti

Anche la proprietà scale di Object3d è un vettore, ed è possibile modificarlo esattamente come abbiamo fatto con la position. Di default il vettore scale ha le coordinate x, y e z uguali a 1, il che significa che l'oggetto non è stato scalato.

Impostando un valore di 0.5, l'oggetto risulterà più piccolo della metà, mentre con un valore di 2, l'oggetto diventerà grande il doppio.

// oggetto risulterà la metà
mesh.scale.set(0.5,0.5,0.5)

// risulterà il doppio
mesh.scale.set(2,2,2)

È possibile anche scalare l'oggetto lungo i 3 assi in maniera disomogenea, assegnando valori differenti alle coordinate x, y e z.

mesh.scale.set(1,0.5,2)

Per quanto sia possibile utilizzare valori negativi per la proprietà del vettore scale, questo potrebbe portare ad alcuni bug, per cui è sconsigliabile scendere sotto lo zero.

Ruotare gli oggetti

La proprietà rotation contiente anch'essa le componenti x, y e z, ma invece di essere un vettore, rappresenta un angolo di Eulero instanza della classe Euler di three,js. La rotazione in uno spazio 3D avviene attorno ad un asse, a differenza delle rotazioni 2D che invece avvengono attorno ad un punto.

Quindi le proprietà x, y e z di rotation sono rispettivamente le 3 rotazioni intorno ai 3 rispettivi assi X, Y e Z e sono espresse in radianti.

Per esprimere gli angoli in radianti è spesso comodo riferirsi alla costante π (Pi greco) che equivale ad un angolo di 180 gradi.

  • 2π => 360
  • π => 180
  • π / 2 => 90
  • π / 3 => 60

In JavaScript possiamo recuperare il valore di π dall'oggetto Math.PI che possiamo poi moltiplicare per ottenere l'angolo desiderato prima di assegnarlo al nostro oggetto.

mesh.rotation.x = Math.PI * 0.3
maeh.position.y = Math.PI * 0.3

Se può sembrare facile, questo sistema ha un grande svantaggio che può portarci ad avere comportamenti strani. Infatti ruotare un oggetto attorno ad uno dei suoi assi fa ruotare di conseguenza anche l'orientamento degli altri assi.

Questo succede perché ogni oggetto 3D ha un suo sistema di riferimento locale XYZ, separato dal quello globale della nostra scena, posizionato al centro di esso, e le rotazioni sono riferite a questo sistema di riferimento.

Esse inoltre inoltre vengono applicate nel seguente ordine: prima la rotazione attorno all'asse X, poi quella attorno all'asse Y, infine la rotazione sull'asse Z (ma è anche possibile modificare l'ordine). Questo può portare al alcune problematiche come il Gimbal lock.

Quaternioni

Per risolvere questo problema si è passati ad un altro meccanismo per gestire la rotazione di un oggetto 3D mediante l'utilizzo dei quaternioni e quindi tramite la proprietà quaternion.

Un quaternione è un modo matematico con cui è possibile esprimere la rotazione di un oggetto 3D nello spazio. Ricade nella teoria dei numeri complessi. Dato che non sono un matematico non mi dilungo nella spiegazione di cosa sia e come funzioni, la potete trovare a questo link.

In ogni caso three.js ci permette di gestire la rotazione sia con la proprietà rotation che con la proprietà quaternion e si occupa di mantenerle aggiornate qualora una delle due venisse modificata.

Orientare gli oggetti

Ruotare gli oggetti in alcuni casi può sembrare un compito assai arduo, ma three.js ci mette a disposizione dei metodi con cui possiamo fare alcune operazioni molto comuni.

Infatti molto spesso abbiamo la necessità di orientare un oggetto affinchè guardi verso un punto ben preciso. Per esempio se vogliamo dire alla camera di guardare verso un punto dello spazio.

Gli Object3D hanno un metodo lookAt() al quale possiamo passare un vettore 3D o le 3 le coordinate del punto verso il quale vogliamo orientarli ed il gioco è fatto.

// passando un vettore 3D
camera.lookAt( new THREE.Vector3(2,3,0.5) )

//passando le singole coordinate x, y, z
camera.lookAt( 2,3,0.5 )

Esistono altri metodi che ci permettono di ruotare gli oggetti 3D lungo assi inclinati riferiti al sistema di riferimento globale, oppure a partire da matrici 4x4. Trovi tutti i metodi degli Object3D nella documentazione a questo link.

Combinare le trasformazioni

Le trasformazioni che applichiamo con position, rotation/ quaternion e scale, possono essere combinate tra di loro e , indifferentemente dall'ordine, queste produrranno lo stesso risultato.

Raggruppare gli oggetti

Spesso vogliamo applicare le stesse trasformazioni ad un gruppo di oggetti contemporaneamente.

Immaginiamo di avere un tavolo, con intorno le sedie. Inoltre sul tavolo abbiamo posizionato piatti, stoviglie e una serie di decorazioni. Se vogliamo a questo punto spostare questi oggetti in un certo punto all'interno di una stanza diventa comodo raggruppare tutti gli oggetti e applicare la traslazione al gruppo invece che a tutti i singoli oggetti.

Per fare questo possiamo creare un gruppo grazie alla classe Group e aggiungere tutti gli oggetti come figli di questo gruppo con il metodo add(), in questo modo.

// creo gli ogetti del gruppo
const table = new THREE.Mesh(tableGeometry,tableMaterial)
const chair = new THREE.Mesh(chairGeometry,chairMaterial)
const glass = new THREE.Mesh(glassGeometry,glassMaterial)

// creo il gruppo
const group = new THREE.Group()

// aggiungo gli ogetti al gruppo
group.add(table, chair, glass)

// sposto la sedia e il bicchiere rispetto al centro del gruppo
chair.position.x = 3
glass.position.y = 0.9

// sposto il gruppo alla coordinata x = 3, y = 5, z = 5
group.position.set( 3, 5, 5)

// ruoto tutto il gruppo do 90 gradi attorno all'asse Y
// del sistema locale del gruppo
group.rotation.y = MAth.PI * 0.5

Conclusioni

Arrivati a questo punti dovremmo essere in grado di spostare i nostri oggetti in qualunque punto dello spazio e a ruotarli a piacimento. Manipolare le proprietà position, rotation e scale è il punto di partenza per poter creare animazioni 3D pazzesche nelle nostre pagine web.

Nel prossimo articolo parleremo appunto di Animazioni!

--

--

Gianluca Lomarco

I am a creative developer working with webGL and fullstack Main teacher at Boolean Academy. Now I’am starting a new experience as content creator in YouTube