원하는 매쉬에 대한 에니메이션 ZoomIn 함수

먼저 사용하는 함수는 다음과 같습니다. (에니메이션을 위하 GSAP 라이브러리를 사용하였음)

_zoomFit(object3D, viewMode, bFront, viewAngle) {
    const box = new THREE.Box3().setFromObject(object3D);
    const sizeBox = box.getSize(new THREE.Vector3()).length();
    const centerBox = box.getCenter(new THREE.Vector3());
    
    const offset = new THREE.Vector3(viewMode==="X"?1:0, viewMode==="Y"?1:0, viewMode==="Z"?1:0);
    if(!bFront) offset.negate();
    offset.applyAxisAngle(new THREE.Vector3(1,0,0), THREE.Math.degToRad(viewAngle));

    const newPosition = new THREE.Vector3().copy(centerBox).add(offset);
    const halfSizeModel = sizeBox * 0.5;
    const halfFov = THREE.Math.degToRad(this._camera.fov * .5);
    const distance = halfSizeModel / Math.tan(halfFov);
    const direction = (new THREE.Vector3()).subVectors(newPosition, centerBox).normalize();
    newPosition.copy(direction.multiplyScalar(distance).add(centerBox));

    const oldPosition = this._camera.position.clone();
    gsap.to(this._camera.position, { duration: 0.5, x: newPosition.x, y: newPosition.y, z: newPosition.z});

    // camera.lookAt(centerBox.x, centerBox.y, centerBox.z); // OrbitControls를 사용하지 않은 경우
    // this._controls.target.copy(centerBox); // OrbitControls를 사용하고 있는 경우
    gsap.to(this._controls.target, { duration: 0.5,  
        x: centerBox.x, y: centerBox.y, z: centerBox.z});        
}

사용은 줌인 대상을 마우스로 클릭해 선택한다고 할때.. 먼저 RayCaster 객체를 하나 정의하구요.

_setupPicking() {
    const raycaster = new THREE.Raycaster();
    this._divContainer.addEventListener("click", this._onClick.bind(this));
    this._raycaster = raycaster;
}

클릭 이벤트인 _onClick은 다음과 같습니다.

_onClick(event) {
    if(!event.ctrlKey) return false;
    const width = this._divContainer.clientWidth;
    const height = this._divContainer.clientHeight;
    const xy = {x: (event.offsetX / width) * 2 - 1, y: -(event.offsetY / height) * 2 + 1};
    this._raycaster.setFromCamera(xy, this._camera);
    const targets = this._raycaster.intersectObjects(this._scene.children);
    if(targets.length > 0) {
        const mesh = targets[0].object;
        this._zoomFit(mesh, "Y", true, 50);
    }
}

이 애니메이션 ZoomIn 기능을 이용해 만든 단위 기능에 대한 예제 영상은 다음과 같습니다.

three.js에서 유리 재질에 동적 환경맵 적용하기

three.js에서 유리 재질을 표현하기 위해서 MeshPhysicalMaterial를 사용합니다. 굴절률(ior)과 유리의 두께(thickness)도 표현할 수 있습니다. 그러나 매쉬에 MeshPhysicalMaterial를 사용하여 유리 재질을 적용하고 정적인 환경 맵과 동적인 환경 맵을 적용해 보면 정적 환경맵은 문제가 없으나 동적 환경맵의 경우 표현되지 않습니다.

이는 MeshPhysicalMaterial에서 동적 환경맵을 적용하기 위해서는 반드시 PMREM 형식으로 만들어진 텍스쳐맵을 사용해야 한다고 합니다. 즉 WebGLCubeRenderTarget으로부터 텍스쳐맵을 바로 재질의 envMap 속성에 지정해서는 안되고 PMREMGenerator 클래스를 사용해여 변환된 텍스쳐맵을 envMap 속성에 지정해줘야 합니다.

일반적인 동적 환경맵 생성과 PMREM 형식의 환경맵 가공을 위한 객체에 대한 코드는 다음과 같습니다.

const sphereRenderTarget = new THREE.WebGLCubeRenderTarget(1024, {
    format: THREE.RGBFormat,
    generateMipmaps: true,
    minFilter: THREE.LinearMipmapLinearFilter
});

sphereRenderTarget._pmremGen = new THREE.PMREMGenerator(this._renderer);
const sphereCamera = new THREE.CubeCamera(0.01, 10, sphereRenderTarget);

그리고 환경맵 지정을 위한 매쉬에 CubeCamera를 추가합니다.

sphere.add(sphereCamera);
this._sphere = sphere;

그리고 렌더링 시에 환경맵을 업데이트 해줘야 합니다.

if(this._sphere) {
    this._sphere.visible = false;

    const cubeCamera = this._sphere.children[0];
    cubeCamera.update(this._renderer, this._scene);
    const renderTarget = cubeCamera.renderTarget._pmremGen.fromCubemap(
        cubeCamera.renderTarget.texture);
    this._sphere.material.envMap = renderTarget.texture;
    this._sphere.visible = true;
}

실행 결과는 다음과 같이 정적인 환경맵과 동적인 환경맵 모두 잘표현되는 것을 확인할 수 있습니다.

전체 코드는 아래를 클릭해서 다운로드 받을 수 있습니다. (three.js 라이브러리는 포함되어 있지 않습니다)