Oggetti trasparenti in Threejs

Sto cercando di scrivere un piccolo programma in Three.js che visualizza due sfere, una dentro l’altra. Il raggio di sfera2 dovrebbe oscillare tra 0,5 e 1,5 mentre il raggio di sfera1 è sempre 1,0. Ogni sfera è trasparente (opacità: 0,5) in modo che sia ansible vedere la sfera più piccola contenuta in quella più grande. Ovviamente i ruoli di “più piccolo” e “più grande” cambiano al variare del raggio di sfera2.

Il problema ora è che Three.js rende trasparente la prima sfera che definisco nel mio programma ma non la seconda. Se definisco prima sphere1, allora diventa trasparente ma poi sphere2 è completamente opaco. Se definisco la prima sfera2, questa è quella trasparente. L’ordine di aggiungerli alla scena non ha alcun ruolo.

Includo sotto un programma minimale che mostra cosa sta succedendo (senza l’animazione). Nel suo stato attuale solo sfera1 è visibile e non è trasparente. Se definisco la sfera1 prima della sfera2, la sfera1 diventa trasparente ma la sfera2 non è più trasparente. Cambiando il raggio di sphere2 a 1.2 si nasconderà quindi sphere1.

C’è un modo per rendere trasparenti entrambe le sfere?

var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 3); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); var ambient = new THREE.AmbientLight( 0x555555 ); scene.add(ambient); var light = new THREE.DirectionalLight( 0xffffff ); light.position = camera.position; scene.add(light); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Definition 2 var geometry2 = new THREE.SphereGeometry(0.8,32,24); var material2 = new THREE.MeshLambertMaterial({color: 0x0000ff, transparent: true, opacity: 0.5}); var sphere2 = new THREE.Mesh(geometry2, material2); // Definition 1 var geometry1 = new THREE.SphereGeometry(1.0,32,24); var material1 = new THREE.MeshLambertMaterial({color: 0x00ff00, transparent: true, opacity: 0.5}); var sphere1 = new THREE.Mesh(geometry1, material1); scene.add(sphere1); scene.add(sphere2); renderer.render(scene, camera); 

Entrambe le tue sfere sono trasparenti e rimangono tali. Quello che sta succedendo è che la sfera più piccola non viene affatto resa.

La trasparenza in WebGL è complicata. Puoi trovare il problema su google per saperne di più.

Ma ti sei imbattuto in un problema relativo al modo in cui three.js in particolare gestisce la trasparenza.

Il WebGLRenderer in three.js ordina gli oggetti in base alla loro distanza dalla telecamera e rende gli oggetti trasparenti in ordine dal più lontano al più vicino. (Questo è un punto importante: ordina gli oggetti in base alla loro posizione e esegue il rendering degli oggetti nell’ordine ordinato).

Quindi, per eseguire correttamente il rendering di due oggetti trasparenti, l’object che si trova nella parte posteriore, ovvero la sfera più piccola nel tuo caso, deve essere visualizzato per primo. Altrimenti, non sarà reso affatto, a causa del buffer di profondità.

Ma nel tuo caso, hai due sfere che si trovano nella stessa posizione, e quindi sono equidistanti dalla fotocamera. Questo è il problema: quale rendere per primo; è un problema.

Quindi è necessario posizionare la sfera più piccola più lontano dalla fotocamera rispetto alla sfera più grande affinché la scena possa essere visualizzata correttamente.

Una soluzione è spostare leggermente indietro la sfera più piccola.

Un’altra soluzione è impostare renderer.sortObjects = false . Quindi gli oggetti verranno visualizzati nell’ordine in cui vengono aggiunti alla scena. In tal caso, assicurati di aggiungere prima la sfera più piccola alla scena.

Una terza soluzione è impostare material1.depthWrite = false e material2.depthWrite = false .

MODIFICARE:

Gli oggetti renderizzabili aventi material.transparent = false (oggetti opachi) vengono renderizzati prima degli oggetti che hanno material.transparent = true (oggetti trasparenti).

Quindi una quarta soluzione è rendere la sfera più piccola opaca in modo che sia resa per prima.

Nuova funzionalità per r.71:

Ora Object3D.renderOrder una proprietà Object3D.renderOrder . All’interno di ogni class di oggetti (opaca o trasparente), gli oggetti sono resi nell’ordine specificato da object.renderOrder . Il valore predefinito di renderOrder è 0 . Si noti che renderOrder non è ereditato dagli oggetti figlio; devi impostarlo per ogni object renderizzabile.

Gli oggetti con lo stesso renderOrder (cravatte) sono ordinati per profondità, come descritto sopra.

Quindi una quinta soluzione è impostare renderOrder = 1 per la sfera più grande. Questa è probabilmente la soluzione migliore nel tuo caso.

three.js r.71

Un paio di commenti.

Primo. Se hai intenzione di fare una domanda che si aspetta che la gente riveda il codice, inseriscilo in jsfiddle. Se lo fai, avrai più persone che sbirciano. Detto questo, ecco una versione leggermente modificata del tuo codice in jsfiddle, per favore usalo come guida per le domande future. esempio di jsfiddle

 var scene = new THREE.Scene(); var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.set(0, 0, 3); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); var ambient = new THREE.AmbientLight( 0x555555 ); scene.add(ambient); var light = new THREE.DirectionalLight( 0xffffff ); light.position = camera.position; scene.add(light); var renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); renderer.sortObjects = false; // Definition 2 var geometry2 = new THREE.SphereGeometry(0.8,32,24); var material2 = new THREE.MeshLambertMaterial({color: 0x0000ff, transparent: true, opacity: 0.5}); var sphere2 = new THREE.Mesh(geometry2, material2); // Definition 1 var geometry1 = new THREE.SphereGeometry(1.0,32,24); var material1 = new THREE.MeshLambertMaterial({color: 0xff0000, transparent: true, opacity: 0.5}); var sphere1 = new THREE.Mesh(geometry1, material1); scene.add(sphere2); scene.add(sphere1); renderer.render(scene, camera); 

Quello che ho modificato nel codice è impostare sortObjects su false e quindi modificare l’ordine in cui le sfere sono state aggiunte alla scena. Questo è stato fatto a causa delle informazioni nei prossimi 2 collegamenti

Piani trasparenti WebGL Comportamento della trama trasparente