web-dev-qa-db-fra.com

Objets transparents dans Three Js

J'essaie d'écrire un petit programme dans Three.js qui affiche deux sphères, l'une à l'intérieur de l'autre. Le rayon de la sphère2 est supposé osciller entre 0,5 et 1,5, tandis que le rayon de la sphère1 est toujours égal à 1,0. Chaque sphère est transparente (opacité: 0,5) de manière à pouvoir voir la plus petite sphère contenue dans la plus grande. Bien sûr, les rôles de "plus petit" et "plus grand" changent lorsque le rayon de sphère2 varie.

Le problème maintenant est que Three.js rend transparente la première sphère que je définis dans mon programme mais pas la seconde. Si je définis d'abord sphère1, elle devient transparente mais sphère2 est complètement opaque. Si je définis en premier sphère2 alors ceci est le transparent. L'ordre de leur ajout à la scène ne joue aucun rôle.

J'inclus ci-dessous un programme minimal qui montre ce qui se passe (sans l'animation). Dans son état actuel, seule la sphère1 est visible et elle n'est pas transparente. Si je définis sphère1 avant sphère2, sphère1 devient transparente mais sphère2 n'est plus transparente. Changer le rayon de sphere2 en 1.2 cachera alors sphere1.

Est-il possible de rendre les deux sphères transparentes?

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);
57
cefstat

Vos deux sphères sont transparentes et le restent. Ce qui se passe, c'est que la plus petite sphère n'est pas rendue du tout.

La transparence dans WebGL est délicate. Vous pouvez google la question pour en savoir plus à ce sujet.

Mais vous êtes tombé sur un problème lié à la manière dont trois.js en particulier gère la transparence.

WebGLRenderer in three.js trie les objets en fonction de leur distance par rapport à la caméra et rend les objets transparents par ordre croissant. (Ceci est un point important: il trie objets en fonction de leur position, et rend objets dans l'ordre de tri.)

Ainsi, pour que deux objets transparents soient correctement restitués, l’objet situé à l’arrière - la plus petite sphère de votre cas - doit être affiché en premier. Sinon, il ne sera pas rendu du tout, à cause du tampon de profondeur.

Mais dans votre cas, vous avez deux sphères qui se trouvent au même endroit et sont donc à égale distance de la caméra. C’est le problème - lequel doit être rendu en premier; c'est un coup.

Vous devez donc placer la plus petite sphère plus loin de la caméra que la plus grande afin que la scène soit correctement rendue.

Une solution consiste à reculer un peu la petite sphère.

Une autre solution consiste à définir renderer.sortObjects = false. Ensuite, les objets seront rendus dans l'ordre dans lequel ils ont été ajoutés à la scène. Dans ce cas, veillez à ajouter la plus petite sphère à la scène en premier.

Une troisième solution consiste à définir material1.depthWrite = false et material2.depthWrite = false.

MODIFIER:

Objets rendus ayant material.transparent = false (objets opaques) sont rendus avant les objets ayant material.transparent = true (objets transparents).

Donc, une quatrième solution consiste à rendre la sphère plus petite opaque afin qu’elle soit rendue en premier.

Nouvelle fonctionnalité pour r.71:

Il y a maintenant un Object3D.renderOrder propriété. Dans chaque classe d'objet (opaque ou transparente), les objets sont rendus dans l'ordre spécifié par object.renderOrder. La valeur par défaut de renderOrder est 0. Notez que renderOrder n'est pas hérité par les objets enfants; vous devez le définir pour chaque objet rendu.

Les objets avec le même renderOrder (liens) sont triés par profondeur, comme décrit ci-dessus.

Une cinquième solution consiste donc à définir renderOrder = 1 pour la plus grande sphère. C'est probablement la meilleure solution dans votre cas.

trois.js r.71

144
WestLangley

Quelques commentaires.

Première. Si vous posez une question qui implique que les utilisateurs révisent le code, mettez-la dans jsfiddle. Si vous le faites, vous aurez plus de gens à jeter un coup d'oeil. Ceci étant dit, voici une version légèrement modifiée de votre code dans jsfiddle, veuillez l’utiliser comme guide pour les questions futures. exemple 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);

Ce que j'ai changé dans votre code est de définir false sur sortObjects, puis de modifier l'ordre dans lequel les sphères ont été ajoutées à la scène. Cela a été fait en raison de l'information dans les 2 prochains liens

plans transparents WebGLcomportement de la texture transparente

7

Pour ce que ça vaut, je ne pouvais pas résoudre le même problème en utilisant les méthodes ci-dessus mais j'ai constaté que:

scene = new THREE.Scene();
group = new THREE.Group();
scene.add( group );

dans mon init (), puis en ajoutant le maillage frontal à la scène, mais le maillage arrière au groupe a résolu le problème. c'est à dire.:

var materialfront = new THREE.MeshPhongMaterial({ 
opacity:1, map:texture });
materialfront.transparent = true ;     
materialfront.side = THREE.FrontSide ; 
frontthing = new THREE.Mesh( geometry, materialfront );
frontthing.renderOrder = 2;
scene.add(frontthing);

puis

var texture2 = texture.clone();
texture2.needsUpdate = true;
var materialBack = new THREE.MeshPhongMaterial({
opacity:0.1, map: texture2})
materialBack.transparent = true ;
materialBack.side = THREE.BackSide;
backthing = new THREE.Mesh( geometryback, materialBack );
backthing.renderOrder = 1;
group.add(backthing);

Ma carte matérielle était une texture .png transparente.

Je ne peux pas expliquer pourquoi d'autres méthodes suggérées n'ont pas fonctionné pour moi, mais j'espère que ce qui précède pourrait aider quelqu'un dans une situation similaire.

0
Craig Wallace