2013-04-14 3 views
36

Я пытаюсь написать небольшую программу в Three.js, которая отображает две сферы, одну внутри другой. Радиус сферы 2 должен колебаться между 0,5 и 1,5, а радиус сферы 1 всегда равен 1,0. Каждая сфера прозрачна (непрозрачность: 0,5), чтобы можно было видеть меньшую сферу, содержащуюся в большей. Конечно, роли «меньшего» и «большего» изменения изменяются по мере того, как изменяется радиус сферы2.Прозрачные объекты в Threejs

Проблема в том, что Three.js делает прозрачным первый шар, который я определяю в своей программе, но не второй. Если я определяю первую сферу1, тогда она становится прозрачной, но затем сфера 2 полностью непрозрачна. Если я определяю первую сферу2, то это прозрачная. Порядок добавления их на сцену не играет никакой роли.

Включая ниже минимальную программу, которая показывает, что происходит (без анимации). В текущем состоянии видна только сфера1, и она не прозрачна. Если я определяю сферу 1 перед сферой 2, то сфера1 становится прозрачной, но сфера2 уже не прозрачна. Изменение радиуса сферы2 до 1,2 скроет сферу 1.

Есть ли способ сделать оба шара прозрачными?

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

ответ

111

Обе ваши сферы являются прозрачными, и остальные так. Что происходит, так это то, что меньшая сфера вообще не отображается.

Прозрачность в WebGL сложна. Вы можете решить эту проблему, чтобы узнать больше об этом.

Но вы наткнулись на проблему, связанную с тем, как three.js в частности обрабатывает прозрачность.

WebGLRenderer в three.js сортирует объекты по их расстоянию от камеры и визуализирует прозрачные объекты в порядке от самого дальнего до ближайшего. (Это очень важный момент: Это сортирует объектов на основе их позиции и делает объекты в отсортированном порядке.)

Таким образом, для двух прозрачных объектов для корректного отображения, объекта, который находится в задней части - - меньшая сфера в вашем случае - должна быть сделана первой. В противном случае он не будет отображаться вообще из-за буфера глубины.

Но в вашем случае у вас есть две сферы, находящиеся в одном и том же месте, и, следовательно, они равноудалены от камеры. В этом и заключается проблема, которую нужно сделать первыми; это бросок.

Таким образом, вам нужно разместить меньшую сферу дальше от камеры, чем большая сфера, чтобы сцена выглядела правильно.

Одним из решений является перемещение маленькой сферы назад немного.

Другим решением является установка renderer.sortObjects = false. Затем объекты будут отображаться в том порядке, в котором они будут добавлены в сцену. В этом случае обязательно добавьте меньшую сферу в первую сцену.

Третьим решением является установка material1.depthWrite = false и material2.depthWrite = false.

РЕДАКТИРОВАТЬ:

визуализируемых объектов, имеющих material.transparent = false (непрозрачные объекты) оказываются перед тем объекты, имеющие material.transparent = true (прозрачные объекты).

Итак, четвертое решение заключается в том, чтобы сделать меньшую сферу непрозрачной, поэтому она визуализируется в первую очередь.

Новая функция для R.71:

Существует теперь Object3D.renderOrder свойство. Внутри каждого класса объекта (непрозрачный или прозрачный) объекты отображаются в порядке, указанном object.renderOrder. Значение по умолчанию renderOrder: 0. Обратите внимание: renderOrder не наследуется дочерними объектами; вы должны установить его для каждого рендерируемого объекта.

Объекты с одинаковыми renderOrder (галстуки), сортируются по глубине, как описано выше.

Итак, пятое решение - установить renderOrder = 1 для большей сферы. Это, скорее всего, лучшее решение в вашем случае.

three.js r.71

+0

Это потрясающий ответ, спасибо чувак !!! –

+0

Так что спасибо –

+0

Что делает deepWrite? Каковы недостатки (perfs, новые ошибки, ...?)? –

5

Пара комментариев.

Первый. Если вы зададите вопрос, который ожидает, что люди просмотрят код, поместите его в jsfiddle. Если вы это сделаете, вы получите больше людей, заглядывающих. Это было сказано, вот немного измененная версия вашего кода в jsfiddle, пожалуйста, используйте его в качестве руководства для будущих вопросов. 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); 

Что я изменил в своем коде, чтобы установить sortObjects в ложь, а затем изменил порядок, что сферы были добавлены к сцене. Это было сделано из-за информации, в ближайшие 2 ссылок

WebGL transparent planes Transparent texture behavior

+1

Благодарим вас за то, что нашли время, чтобы проверить мой код и ответить. Установка 'rendered.sortObjects' в' false' делает конечный результат зависеть от порядка, который я добавляю в объекты, как объясняется в более позднем ответе WestLangley. Проблема в том, что я хочу оживить сцену, и для некоторых кадров было бы лучше иметь одну сферу, нарисованную сначала, а также некоторые другие рамки в другой сфере. – cefstat

Смежные вопросы