2013-04-14 11 views
36

Próbuję napisać mały program w Three.js, który wyświetla dwie sfery, jedną w drugiej. Promień sfery2 ma oscylować między 0,5 a 1,5, podczas gdy promień sfery 1 wynosi zawsze 1,0. Każda sfera jest przezroczysta (nieprzezroczystość: 0,5), dzięki czemu możliwe byłoby zobaczenie mniejszej kuli zawartej w większej. Oczywiście role "mniejszych" i "większych" zmieniają się, gdy zmienia się promień sfery2.Przezroczyste obiekty w Threejs

Problem polega na tym, że Three.js czyni przezroczystą pierwszą sferę, którą definiuję w moim programie, ale nie drugą. Jeśli zdefiniuję pierwszą sferę1, wówczas stanie się przezroczysta, ale wtedy sfera2 jest całkowicie nieprzezroczysta. Jeśli zdefiniuję pierwszą sferę2, wówczas jest to przezroczysta. Kolejność dodawania ich do sceny nie odgrywa żadnej roli.

Zamieszczam poniżej minimalny program, który pokazuje, co się dzieje (bez animacji). W obecnym stanie widoczna jest tylko sfera 1 i nie jest ona przezroczysta. Jeśli zdefiniuję sferę 1 przed sferą2, wówczas sfera 1 stanie się przezroczysta, ale sfera2 nie będzie już przezroczysta. Zmiana promienia kuli2 na 1.2 spowoduje ukrycie sfery 1.

Czy jest jakiś sposób, aby uczynić obie kule przejrzystymi?

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

Odpowiedz

111

Obie twoje kulki są przejrzyste, a więc pozostają. Dzieje się tak, że mniejsza kula w ogóle nie jest renderowana.

Przejrzystość w WebGL jest trudna. Możesz google rozwiązać problem, aby dowiedzieć się więcej.

Ale natknąłeś się na problem związany z tym, jak trzy.js w szczególnościobsługuje przezroczystość.

Obiekt 01.sortuje obiekty na podstawie ich odległości od kamery i renderuje przezroczyste obiekty w kolejności od najdalej do najbliższej. (Jest to ważny punkt: sortuje To obiektów podstawie ich pozycji i renderuje obiektów w posortowanych.)

Tak dla dwóch obiektów przezroczystych do renderowania poprawnie, obiekt, który jest w odwrocie - - mniejsza kula w twoim przypadku - musi być wykonana jako pierwsza. W przeciwnym razie nie będzie renderowany w ogóle z powodu bufora głębi.

Ale w twoim przypadku masz dwie kulki, które znajdują się w tym samym miejscu i dlatego są w równej odległości od kamery. Na tym polega problem - który należy najpierw wyrazić; to jest toss-up.

Więc trzeba umieścić mniejszą kulę dalej od aparatu niż w większej kuli, aby scena do renderowania poprawnie.

Jednym z rozwiązań jest niewielkie przesunięcie mniejszej kuli.

Innym rozwiązaniem jest ustawienie renderer.sortObjects = false. Następnie obiekty będą renderowane w kolejności, w jakiej zostały dodane do sceny. W takim przypadku pamiętaj, aby najpierw dodać mniejszą kulę do sceny.

Trzecim rozwiązaniem jest ustawienie material1.depthWrite = false i material2.depthWrite = false.

Edycja:

Renderable przedmiotów o material.transparent = false nieprzezroczyste (obiekty) są prezentowane przed przedmiotów o material.transparent = true (przezroczyste obiektów).

Więc czwarty rozwiązaniem jest, aby mniejszy sfera OPAQUE więc staje się pierwszym.

Nowa funkcja dla r.71:

Istnieje obecnie własność Object3D.renderOrder. W obrębie każdej klasy obiektu (nieprzezroczystej lub przezroczystej) obiekty są renderowane w kolejności określonej przez object.renderOrder. Domyślna wartość renderOrder to 0. Zauważ, że renderOrder nie jest dziedziczone przez obiekty podrzędne; musisz ustawić go dla każdego renderowalnego obiektu.

obiekty o takich samych renderOrder (podkłady), są klasyfikowane przez głębokość, jak opisano powyżej.

Więc piąty rozwiązaniem jest ustawienie renderOrder = 1 dla większej kuli. Jest to prawdopodobnie najlepsze rozwiązanie w twoim przypadku.

three.js r.71

+0

To jest niesamowita odpowiedź, dzięki koleś !!! –

+0

Bardzo dobrze dziękuję –

+0

Co dokładnie robi deepWrite? Jakie są wady (perfki, nowe błędy, ...?)? –

5

Kilka komentarzy.

Pierwsza. Jeśli masz zamiar zadać pytanie, które oczekuje, że ludzie sprawdzą kod, umieść go w jsfiddle. Jeśli to zrobisz, zobaczysz, że więcej osób rzuca okiem. Jak już wspomniano, oto nieco zmodyfikowana wersja twojego kodu w jsfiddle, użyj go jako wskazówki do przyszłych pytań. jsfiddle example

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

Co mam zmienić w kodzie jest ustawienie sortObjects false, a następnie zmieniono kolejność że kule zostały dodane do sceny. Stało się tak z powodu informacji w ciągu najbliższych 2 linki

WebGL transparent planes Transparent texture behavior

+1

Dziękujemy za poświęcenie czasu na sprawdzenie kodu i odpowiedź. Ustawienie 'rendered.sortObjects' na' false' powoduje, że końcowy wynik zależy od kolejności dodawania obiektów do sceny, jak wyjaśniono w późniejszej odpowiedzi WestLangley. Problem polega na tym, że chcę animować scenę, a dla niektórych klatek lepiej byłoby, gdyby jedna sfera była najpierw nakreślona, ​​a inne inne. – cefstat