SQLの窓 イラストAC フリー素材

2013年03月03日

Three.js : カメラが発射する Ray(光) にヒットするオブジェクトを総取りする


ここから、実行ページを開きます( 但し、WebGL でのみ動作します )

カメラを使った当たり判定

要するに、マウスクリックに対する座標を使って、3D エリア内のオブジェクトとの当たり判定を行います。

画面に対して垂直に光線が進んで行き、その光線に触れたオブジェクトが最初に接した座標が一覧として取得できるようになっています。

※ ソース内では、intersects がそれです。

当たった位置に新しいオブジェクトを作成



この画像をテクスチャとして使って、球オブジェクトを作成して、画面内に複数のオブジェクトを追加して行きます。動作としてはクリックする毎に、その位置に表示されている立方体の手前の表面に球が埋め込まれて行くことになります。

このオブジェクトは、transparent: true が設定されており、PNG 画像が背景透過なので、画面内では口の中が奥まで見えて立方体の中に入った部分は見えたり見えなかったりします( 厳密な仕様は解りません )
※ 立方体側は、opacity: 0.5

画像は同一ドメインで

ご自分で再現する場合、ソースコードそのままでほぼ動作しますが、画像のみ同一ドメインである必要があります。WebGL での仕様だと思われますが、同一ドメインの画像を用意して下さい。( ここの画像をダウンロードして使って下さい )

ページ上の部分エリア用のクリック位置判定

Three.js の殆どのサンプルページはブラウザ上のページを全て使うので必要無いですが、実際の WEB ページでは、スクロールを考慮した DIV の位置よりクリックした位置を正確に計算する必要があります。その為、ちょっと面倒ですが以下のような処理を行っています
var parent = document.getElementById("three_area")
while (parent) {
	numx += parent.offsetLeft;
	numy += parent.offsetTop;
	parent = parent.offsetParent;
}
さらに、ブラウザによってスクロール距離の取得場所が違うので、現時点で以下のように書いています。( 将来的にはどうなるか解らないので注意 ) var top = document.documentElement.scrollTop + document.body.scrollTop
<script type="text/javascript" src="http://homepage2.nifty.com/lightbox/three/three.min56.js"></script>
<div id="three_area" style='width:600px;height:500px;'></div>

<script type="text/javascript">
if( !( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )() ) {
	document.write("<span style='font-weight:bold;font-size:30px;'>WebGLが使用できません</span>");
	document.getElementById("three_area").style.display = 'none';
}

var w = 600;
var h = 500;
var numx = 0;
var numy = 0;

var container;
var camera, scene, projector, renderer;

var texture,material;

init();
animate();

function init() {

	camera = new THREE.PerspectiveCamera( 70, w / h, 1, 10000 );
	camera.position.set( 0, 300, 500 );

	scene = new THREE.Scene();

	var geometry = new THREE.CubeGeometry( 20, 20, 20 );

	for ( var i = 0; i < 1000; i ++ ) {

		var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, transparent: true,opacity: 0.5 } ) );
		object.position.x = Math.random() * 800 - 400;
		object.position.y = Math.random() * 800 - 400;
		object.position.z = Math.random() * 800 - 400;

		object.scale.x = Math.random() * 2 + 1;
		object.scale.y = Math.random() * 2 + 1;
		object.scale.z = Math.random() * 2 + 1;

		object.rotation.x = Math.random() * 2 * Math.PI;
		object.rotation.y = Math.random() * 2 * Math.PI;
		object.rotation.z = Math.random() * 2 * Math.PI;

		scene.add( object );

	}

	// Ray 判定用のプロジェクター
	projector = new THREE.Projector();

	renderer = new THREE.WebGLRenderer();
	renderer.setSize( w, h );

	document.getElementById("three_area").appendChild( renderer.domElement );

	document.addEventListener( 'mousedown', onDocumentMouseDown, false );

	var image = new Image()
	image.onload = function () {

		texture = new THREE.Texture( this );
		texture.needsUpdate = true;
		material = new THREE.MeshBasicMaterial({map: texture, transparent: true});
	};
	image.src = "http://lightbox.matrix.jp/imgpatio/smile.png";

	var parent = document.getElementById("three_area")
	while (parent) {
		numx += parent.offsetLeft;
		numy += parent.offsetTop;
		parent = parent.offsetParent;
	}


}

// *********************************************
// クリックした位置の Ray 判定 
// *********************************************
function onDocumentMouseDown( event ) {

	var top = document.documentElement.scrollTop + document.body.scrollTop

	event.preventDefault();

	// 対象座標のベクタ( 0.5 は 1 でも良いはず。意味が解りやすい 0.5 が使われている )
	var vector = new THREE.Vector3( (( event.clientX - numx ) / w ) * 2 - 1, - ( (event.clientY - (numy - top) ) / h ) * 2 + 1, 0.5 );
	projector.unprojectVector( vector, camera );

	// Ray 判定の本体
	var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );

	// 対象となるオブジェクトの一覧の取得
	var intersects = raycaster.intersectObjects( scene.children );

	if ( intersects.length > 0 ) {

		var mesh = null;

		for( var i = 0; i < intersects.length; i++ ) {

			// テクスチャ付の球オブジェクト
			mesh = new THREE.Mesh(new THREE.SphereGeometry(15, 20, 20), material);
			mesh.position = intersects[ i ].point;
			mesh.rotation.y = Math.random() * Math.PI * 2;
			scene.add(mesh);

		}

	}
}

function animate() {

	requestAnimationFrame( animate );
	render();

}

var radius = 600;
var theta = 0;

function render() {

	theta += 0.1;

	camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
	camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
	camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
	camera.lookAt( scene.position );

	renderer.render( scene, camera );

}

</script>


【Three.jsの最新記事】
posted by at 2013-03-03 08:00 | Three.js | このブログの読者になる | 更新情報をチェックする