BaeBox

Picking(raycasting) 본문

개발 관련

Picking(raycasting)

배모씨. 2020. 6. 21. 18:16
반응형

 

picking 방법인 raycating

Picking : 사용자가 터치하거나 클릭한 객체를 파악하는 프로세스. 

내가 고른 객체 데이터를 가져오는 방법이라고 생각하자.

요런 식이다.

처음보면 저걸 어떻게하나 어려워 보이는데, Three.js에서 기본적으로 제공해주는 RayCasting 기능을 사용하면 굉장히 쉽게 구현이 가능하다. 

아래 코드는 공식 문서에서 긁어왔다. 

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

function onMouseMove( event ) {

	// calculate mouse position in normalized device coordinates
	// (-1 to +1) for both components

	mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
	mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

}

function render() {

	// update the picking ray with the camera and mouse position
	raycaster.setFromCamera( mouse, camera );

	// calculate objects intersecting the picking ray
	var intersects = raycaster.intersectObjects( scene.children );

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

		intersects[ i ].object.material.color.set( 0xff0000 );

	}

	renderer.render( scene, camera );

}

window.addEventListener( 'mousemove', onMouseMove, false );

window.requestAnimationFrame(render);

위에는 귀여운 예제였다.

이제 아래를 보자.

허허...

하나의 mesh(object)에 texture로 중앙이 빵꾸난 png 텍스쳐를 입혔다고 생각해보자. 

근데 그렇다고 가운데 텅 빈 부분을 관통해서 뒤에 있는 놈이 선택이 될까?

블로그에서도 이 부분을 강조한다.

중앙이 텅 빈 텍스처를 씌웠건 말았건, 빛이 가리키는 것은 제일 앞에 위치한 Mesh(Object)이다. 

자바스크립트는 얘가 텅 비었는지 아닌지 구분을 못한다.

 

그렇다면 중앙을 텅 빈놈을 관통해 뒤에 있는 친구를 선택하고 싶다면 어떻게 해야 하느냐.

CPU를 기반으로하면 각 mesh가 뚫려있는지 구분이 불가능하므로, GPU를 기반으로하여 picking을 한다. 

대강 요런짓을 한다.

모든 선택은 구분이 가능한 picingScene에서 하고, 그 결과를 Scene으로 보낸다.

Scene을 복제한 pickingScene을 새로 생성한 다음, pickingScene과 내용물의 색상을 모두 검은색(0)으로 설정한다. 

이 때, pickingScene의 내용물이 되는 Mesh들의 emissive(외부로 보이는 색)의 값을 유니크한 값으로 설정해준다.

그리고 아래 코드의 pick 함수가, id 값을 받아오는데 이 값은 위에서 유니크하게 설정한 emissive 값이 된다.  

이제 그걸 기반으로 Mesh 속성을 조정하면 된다. 

  class GPUPickHelper {
    constructor() {
      // create a 1x1 pixel render target
      this.pickingTexture = new THREE.WebGLRenderTarget(1, 1);
      this.pixelBuffer = new Uint8Array(4);
      this.pickedObject = null;
      this.pickedObjectSavedColor = 0;
    }
    pick(cssPosition, scene, camera, time) {
      const {pickingTexture, pixelBuffer} = this;

      // restore the color if there is a picked object
      if (this.pickedObject) {
        this.pickedObject.material.emissive.setHex(this.pickedObjectSavedColor);
        this.pickedObject = undefined;
      }

      // set the view offset to represent just a single pixel under the mouse
      const pixelRatio = renderer.getPixelRatio();
      camera.setViewOffset(
          renderer.getContext().drawingBufferWidth,   // full width
          renderer.getContext().drawingBufferHeight,  // full top
          cssPosition.x * pixelRatio | 0,             // rect x
          cssPosition.y * pixelRatio | 0,             // rect y
          1,                                          // rect width
          1,                                          // rect height
      );
      // render the scene
      renderer.setRenderTarget(pickingTexture);
      renderer.render(scene, camera);
      renderer.setRenderTarget(null);
      // clear the view offset so rendering returns to normal
      camera.clearViewOffset();
      //read the pixel
      renderer.readRenderTargetPixels(
          pickingTexture,
          0,   // x
          0,   // y
          1,   // width
          1,   // height
          pixelBuffer);

      const id =
          (pixelBuffer[0] << 16) |
          (pixelBuffer[1] <<  8) |
          (pixelBuffer[2]      );

      const intersectedObject = idToObject[id];
      if (intersectedObject) {
        // pick the first object. It's the closest one
        this.pickedObject = intersectedObject;
        // save its color
        this.pickedObjectSavedColor = this.pickedObject.material.emissive.getHex();
        // set its emissive color to flashing red/yellow
        this.pickedObject.material.emissive.setHex((time * 8) % 2 > 1 ? 0xFFFF00 : 0xFF0000);
      }
    }
  }

 

끗.


 

아래 공식문서에서 다양한 예제를 제공하고, 그 아래엔 threejs 블로그인데, picking을 쉽게 하기 위한 클래스 및 다양한 방법을 소개한다. 

https://threejs.org/docs/index.html#api/en/core/Raycaster

 

three.js docs

 

threejs.org

https://threejsfundamentals.org/threejs/lessons/threejs-indexed-textures.html

 

Three.js Indexed Textures for Picking and Color

Using Indexed Textures for Picking and Color

threejsfundamentals.org

https://threejsfundamentals.org/threejs/lessons/threejs-picking.html

 

Three.js Picking

Selecting Objects with the Mouse in Three.js

threejsfundamentals.org

https://threejsfundamentals.org/threejs/lessons/threejs-align-html-elements-to-3d.html

 

Three.js Aligning HTML Elements to 3D

How to line up an HTML Element to match a point in 3D space

threejsfundamentals.org

 

반응형

'개발 관련' 카테고리의 다른 글

WebGL  (0) 2020.07.01
Globe( 지구본 )  (0) 2020.06.21
Web developer roadmap 2020  (0) 2020.06.21
Git README.md 작성법  (0) 2020.04.25
QuickSort(퀵정렬) with JavaScript  (0) 2020.04.21
Comments