Three.jsで2つの球を表示する小さなプログラムを作成しようとしています。 sphere1の半径は常に1.0ですが、sphere2の半径は0.5〜1.5の間で振動することになっています。各球体は透明(不透明度:0.5)であるため、大きな球体に含まれる小さな球体を見ることができます。もちろん、「小さい」と「大きい」の役割は、sphere2の半径が変わると変わります。
現在の問題は、Three.jsが、プログラムで定義する最初の球を透明にしますが、2番目の球ではありません。最初にsphere1を定義すると、透明になりますが、sphere2は完全に不透明になります。最初にsphere2を定義すると、これは透明なものです。それらをシーンに追加する順序は何の役割も果たしません。
何が起こっているかを示す最小限のプログラムを以下に示します(アニメーションなし)。現在の状態では、sphere1のみが表示され、透明ではありません。 sphere2の前にsphere1を定義すると、sphere1は透明になりますが、sphere2は透明ではなくなります。 sphere2の半径を1.2に変更すると、sphere1が非表示になります。
両方の球を透明にする方法はありますか?
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);
両方の球体は透明であり、そのまま残ります。起こっているのは、小さな球体がまったくレンダリングされていないことです。
WebGLの透明性には注意が必要です。問題をグーグルで検索して、詳細を調べることができます。
しかし、特にthree.jsが透明度を処理する方法に関連する問題に出くわしました。
Three.jsのWebGLRenderer
は、カメラからの距離に基づいてオブジェクトを並べ替え、透明なオブジェクトを最も遠いものから最も近いものの順にレンダリングします。 (これは重要なポイントです:オブジェクトを位置に基づいてソートし、ソートされた順序でオブジェクトをレンダリングします。)
そのため、2つの透明なオブジェクトを正しくレンダリングするには、後ろにあるオブジェクト(この場合は小さい球体)を最初にレンダリングする必要があります。それ以外の場合、深度バッファにより、まったくレンダリングされません。
しかし、あなたの場合、同じ場所にある2つの球体があるため、カメラから等距離にあります。それが問題です。最初にレンダリングするのはどれですか。それは投げです。
そのため、シーンを正しくレンダリングするには、小さい球体を大きい球体よりもカメラから遠ざけて配置する必要があります。
1つの解決策は、小さな球体を少し戻すことです。
別の解決策は、renderer.sortObjects = false
を設定することです。その後、オブジェクトはシーンに追加された順序でレンダリングされます。その場合は、最初に小さい球を必ずシーンに追加してください。
3番目の解決策は、material1.depthWrite = false
およびmaterial2.depthWrite = false
を設定することです。
編集:
material.transparent = false
(不透明なオブジェクト)を持つレンダリング可能なオブジェクトは、material.transparent = true
(透明なオブジェクト)を持つオブジェクトの前にレンダリングされます。
したがって、4番目の解決策は、小さい球を不透明にして、最初にレンダリングすることです。
R.71の新機能:
現在、Object3D.renderOrder
プロパティがあります。オブジェクトの各クラス(不透明または透明)内で、オブジェクトはobject.renderOrder
で指定された順序でレンダリングされます。 renderOrder
のデフォルト値は0
です。 renderOrder
は子オブジェクトに継承されないことに注意してください。レンダリング可能なオブジェクトごとに設定する必要があります。
同じrenderOrder
(タイ)を持つオブジェクトは、上記のように深さでソートされます。
5番目の解決策は、より大きな球体にrenderOrder = 1
を設定することです。これはおそらくあなたの場合の最良の解決策です。
three.js r.71
いくつかのコメント。
最初。人々がコードをレビューすることを期待する質問をするつもりなら、jsfiddleに入れてください。そうすれば、より多くの人が覗くようになります。そうは言っても、これはjsfiddleのコードのわずかに変更されたバージョンです。今後の質問のガイドとして使用してください。 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);
コードで変更したのは、sortObjectsをfalseに設定してから、球体がシーンに追加される順序を変更することです。これは、次の2つのリンクの情報のために行われました
上記の方法を使用して同じ問題を解決することはできませんでしたが、次のことがわかっています。
scene = new THREE.Scene();
group = new THREE.Group();
scene.add( group );
私のinit()でシーンにフロントサイドメッシュを追加しましたが、グループのバックサイドメッシュは問題を解決しました。すなわち:
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);
それから
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);
私のマテリアルマップは、透明な.pngテクスチャでした。
他の提案された方法が私にとってうまくいかなかった理由を説明することはできませんが、上記が似たような立場の人を助けるかもしれないと思います。