web-dev-qa-db-ja.com

マウスクリック座標からWebGL3D空間でオブジェクトを取得する方法

WebGLでボードゲームを作成しています。ボードは回転/ズームできます。キャンバス要素(x、y)のクリックを3D空間(x、y、z)の関連するポイントに変換する方法が必要です。最終的な結果は、ユーザーに最も近いオブジェクトに接触するポイントを含む(x、y、z)座標を知りたいということです。たとえば、ユーザーがピースをクリックすると、そのピースとゲームボードの両方を通過する3D空間を光線が通過することを想像しますが、そのピースの(x、y、z)座標が必要です。触れた。

これは非常に一般的な問題であるに違いないと思いますが、グーグルで解決策を見つけることができないようです。 3D空間の現在のビューを2Dに投影して、2D空間の各ポイントを3D空間の関連するポイントにマッピングできるようにする方法が必要です。ボード上のスペースにマウスを合わせて、スポットの色を変えてもらいたいです。

18
seibelj

画面座標をカメラ位置から3Dワールドにキャストされた光線に変換する非投影関数を探しています。次に、光線/三角形の交差テストを実行して、光線と交差するカメラに最も近い三角形を見つける必要があります。

jax/camera.js#L568 -で利用可能な非投影の例がありますが、それでも光線/三角形の交差を実装する必要があります。 jax/triangle.js#L11 に実装しています。

ただし、「ピッキング」と呼ばれる、より単純で(通常は)より高速な代替手段があります。オブジェクト全体(たとえば、チェスの駒)を選択する場合、およびマウスが実際にクリックした場所を気にしない場合は、これを使用します。これを行うWebGLの方法は、シーン全体をさまざまな青の色合い(青がキーで、赤と緑がシーン内のオブジェクトの一意のIDに使用されます)でテクスチャにレンダリングしてから、ピクセルを読み取ります。そのテクスチャ。 RGBをオブジェクトのIDにデコードすると、クリックされたオブジェクトが得られます。繰り返しますが、これを実装しました。 jax/world.js#L82 で入手できます。 (146、162、175行も参照してください。)

どちらのアプローチにも長所と短所があり(後で説明します ここ およびその後のコメントの一部で)、どちらのアプローチがニーズに最も適しているかを理解する必要があります。巨大なシーンではピッキングが遅くなりますが、純粋なJSでの投影解除は非常に遅いため(JS自体はそれほど高速ではないため)、両方を試してみることをお勧めします。

参考までに、GLUプロジェクトとプロジェクト解除コードを確認することもできます。これらのコードは大まかに基づいています。 http://www.opengl.org/wiki/GluProject_and_gluUnProject_code

25

これは作業デモです

function onMouseUp(event) {

    event.preventDefault();        
    x_pos = (event.clientX / window.innerWidth) * 2 - 1;
    y_pos = -(event.clientY / window.innerHeight) * 2 + 1;
    z_pos = 0.5;

    var vector = new THREE.Vector3( x_pos , y_pos , z_pos );

    var projector = new THREE.Projector();
    projector.unprojectVector(vector, camera);
    var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
    var intersects = raycaster.intersectObjects(intersectObjects);

    if (intersects.length > 0) {

        xp = intersects[0].point.x.toFixed(2);
        yp = intersects[0].point.y.toFixed(2);
        zp = intersects[0].point.z.toFixed(2);  
        destination = new THREE.Vector3( xp , yp , zp );

        radians =  Math.atan2( ( driller.position.x - xp) , (driller.position.z - zp));
        radians += 90 * (Math.PI / 180);
        console.log(radians);

        var tween = new TWEEN.Tween(driller.rotation).to({ y : radians },200).easing(TWEEN.Easing.Linear.None).start();

    }

weissner-doors.de/drone/

2

私は現在この問題に取り組んでいます-私が取っているアプローチは

  1. オブジェクトをレンダリングして、それぞれ固有の色のバッファを選択します
  2. バッファピクセルを読み取り、選択したオブジェクトにマップし直します
  3. 選択したオブジェクトをレンダリングして、各ピクセルの色をZ深度の関数でバッファリングします
  4. バッファピクセルを読み取り、Z深度にマップし直します
  5. オブジェクトを選択し、選択座標のZを近似しました
2
xeolabs

スレッドの1つからカルト。 (x、y、z)についてはわかりませんが、canvas(x,y)を使用して取得できます

getBoundingClientRect()

function getCanvasCoord(){
  var mx = event.clientX;
  var my = event.clientY;
  var canvas = document.getElementById('canvasId');
  var rect = canvas.getBoundingClientRect();// check if your browser supports this
  mx = mx - rect.left;
  my = my - rect.top;
  return {x: mx , y: my};
}
0
Naruto