2013-04-14 13 views
36

Ich versuche, ein kleines Programm in Three.js zu schreiben, das zwei Kugeln zeigt, eine ineinander. Der Radius von sphere2 soll zwischen 0.5 und 1.5 schwingen, während der Radius von sphere1 immer 1.0 ist. Jede Kugel ist transparent (Opazität: 0,5), so dass es möglich ist, die kleinere Kugel zu sehen, die in der größeren enthalten ist. Natürlich ändern sich die Rollen von "kleiner" und "größer", wenn der Radius der Kugel 2 variiert.Transparente Objekte in Threejs

Das Problem ist jetzt, dass Three.js die erste Sphäre transparent macht, die ich in meinem Programm definiere, aber nicht die zweite. Wenn ich die erste Sphäre1 definiere, wird sie transparent, aber dann ist Sphäre2 vollständig undurchsichtig. Wenn ich die erste Sphäre2 definiere, dann ist dies die transparente Sphäre. Die Reihenfolge, in der sie zur Szene hinzugefügt werden, spielt keine Rolle.

Ich schließe unten ein minimales Programm ein, das zeigt, was vor sich geht (ohne die Animation). In seinem aktuellen Zustand ist nur sphere1 sichtbar und nicht transparent. Wenn ich sphere1 vor sphere2 definiere, wird sphere1 transparent, aber sphere2 ist nicht mehr transparent. Wenn Sie den Radius von sphere2 auf 1,2 ändern, wird sphere1 ausgeblendet.

Gibt es eine Möglichkeit, beide Kugeln transparent zu machen?

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

Antwort

111

Sowohl Ihre Kugeln sind transparent und werden noch so. Was passiert, ist, dass die kleinere Kugel überhaupt nicht wiedergegeben wird.

Transparenz in WebGL ist schwierig. Sie können das Problem googlen, um mehr darüber zu erfahren.

Aber Sie sind auf ein Problem gestoßen in Bezug auf wie three.js insbesondere behandelt Transparenz.

Die WebGLRenderer in three.js sortiert Objekte basierend auf ihrer Entfernung von der Kamera und rendert transparente Objekte in der Reihenfolge von am weitesten zum nächsten. (Dies ist ein wichtiger Punkt: Es sortiert Objekte auf der Grundlage ihrer Position und macht Objekte in der sortierten Reihenfolge.)

So für zwei transparente Objekte korrekt zu machen, das Objekt, das auf der Rückseite ist - - die kleinere Kugel in Ihrem Fall - muss zuerst gerendert werden. Andernfalls wird es aufgrund des Tiefenpuffers überhaupt nicht gerendert.

Aber in Ihrem Fall haben Sie zwei Kugeln, die an der gleichen Stelle sind und daher von der Kamera gleich weit entfernt sind. Das ist das Problem - welches man zuerst rendern soll; es ist ein Toss-Up.

Sie müssen also die kleinere Kugel weiter weg von der Kamera als die größere Kugel platzieren, damit die Szene korrekt gerendert wird.

Eine Lösung besteht darin, die kleinere Kugel etwas zurück zu bewegen.

Eine andere Lösung ist renderer.sortObjects = false zu setzen. Dann werden die Objekte in der Reihenfolge wiedergegeben, in der sie der Szene hinzugefügt wurden. Stellen Sie in diesem Fall sicher, dass Sie der Szene zuerst die kleinere Kugel hinzufügen. Eine dritte Lösung besteht darin, material1.depthWrite = false und material2.depthWrite = false zu setzen.

EDIT:

Render Objekte material.transparent = false (undurchsichtige Objekte), die wiedergegeben werden, bevor Objekte mit material.transparent = true (transparenten Objekten).

Eine vierte Lösung besteht also darin, die kleinere Sphäre undurchsichtig zu machen, so dass sie zuerst gerendert wird.

Neue Funktion für r.71:

Es gibt jetzt eine Object3D.renderOrder Eigenschaft. Innerhalb jeder Objektklasse (undurchsichtig oder transparent) werden Objekte in der durch object.renderOrder angegebenen Reihenfolge gerendert. Der Standardwert von renderOrder ist 0. Beachten Sie, dass renderOrder nicht von untergeordneten Objekten geerbt wird. Sie müssen es für jedes renderbare Objekt festlegen.

Objekte mit dem gleichen renderOrder (Bindungen), sind nach Tiefe sortiert, wie oben beschrieben.

Also eine fünfte Lösung ist renderOrder = 1 für die größere Kugel einzustellen. Dies ist wahrscheinlich die beste Lösung in Ihrem Fall.

three.js r.71

+0

Das ist eine tolle Antwort, danke Alter !!! –

+0

Also gut danke –

+0

Was genau tut depthWrite? Was sind die Nachteile (Perfs, neue Bugs, ...?)? –

5

Ein paar Kommentare.

Zuerst. Wenn du eine Frage stellen willst, die Leute erwarten, Code zu überprüfen, lege sie in jsfiddle ab. Wenn Sie das tun, werden Sie mehr Leute dazu bringen, einen Blick zu werfen. Nachdem dies gesagt wurde, hier ist eine leicht modifizierte Version Ihres Codes in jsfiddle, bitte verwenden Sie es als Leitfaden für zukünftige Fragen. 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); 

Was ich in Ihrem Code geändert haben, ist sortObjects auf false gesetzt und verändert dann die Reihenfolge, dass die Kugeln in die Szene aufgenommen wurden. Dies wurde aufgrund der Informationen, die in den nächsten 2 Links getan

WebGL transparent planes Transparent texture behavior

+1

Vielen Dank, dass Sie sich die Zeit genommen haben, meinen Code zu überprüfen und zu antworten. Setzen Sie 'rendered.sortObjects' auf' false', hängt das Endergebnis von der Reihenfolge ab, in der ich die Objekte der Szene hinzufüge, wie in der späteren Antwort von WestLangley erklärt. Das Problem ist, dass ich die Szene animieren möchte und für einige Frames wäre es besser, eine Sphäre zuerst und für andere Frames die andere Sphäre zu zeichnen. – cefstat