주변 환경을 비추는 객체 표현

환경맵(envmap)을 사용해서 주변 환경의 모습을 비추는 객체를 표현할 때 적용할 수 있는 코드입니다. 먼저 아무런 재질도 적용되지 않은 검정색 구체입니다.

위의 검정색 구에 주변 환경 이미지를 환경 맵 소스로 활용하는 코드는 다음과 같습니다.

const cubeRenderTarget = new THREE.WebGLCubeRenderTarget( 1024 );
this._mirrorSphereCamera = new THREE.CubeCamera( 0.05, 50, cubeRenderTarget );
this._scene.add( this._mirrorSphereCamera );
const mirrorSphereMaterial = new THREE.MeshBasicMaterial( { envMap: cubeRenderTarget.texture } );
this._sphere.material = mirrorSphereMaterial;

환경맵을 생성하는 CubeCamera 객체의 상태가 변경될 경우… 예를들어 카메라의 위치 등이 변경될 경우 다음처럼 update 매서드를 호출해줘야 합니다.

this._mirrorSphereCamera.update( this._renderer, this._scene );

이 글에 사용된 코드와 결과는 Three.js에서 제공되는 예제 코드 중 webgl_animation_skinning_ik.html에서 환경맵 적용에 대한 내용만을 정리한 것입니다.

three.js의 행렬 변환

Three.js는 이동(position), 회전, 크기변환과 같은 3차원 변환을 위해 행렬을 사용함. Object3D의 파생 클래스의 모든 객체는 matrix 속성을 가지고 있고 이 속성은 객체의 위치, 회전, 크기 변환값이 저장되어 있음. 이 글은 객체의 변환을 어떻게 업데이트 하는지 기술함.

객체의 변환을 업데이트하기 위해서는 2가지 방법이 있음.

1. 객체의 position, quaternion, scale 속성을 변경하고 이 속성들로부터 객체의 행렬을 다시 계산시킴.

object.position.copy( start_position );
object.quaternion.copy( quaternion );

기본적으로 matrixAutoUpdate 속성이 true이므로 행렬은 자동적으로 재계산됨. 만약 객체가 정적이거나 행렬 재계산을 수동으로 제어하고자 한다면 matrixAutoUpdate를 false로 지정하여 더 나은 퍼포먼스를 얻을 수 있음. 이때 다음처럼 수동으로 행렬을 재계산할 수 있음.

object.updateMatrix();

2. 객체의 행렬을 직접 변경해도 됨. Matrix4 클래스는 행렬 변경을 위한 다양한 매서드를 제공함.

object.matrix.setRotationFromQuaternion( quaternion );
object.matrix.setPosition( start_position );
object.matrixAutoUpdate = false;

윗 코드의 경우 matrixAutoUpdate의 값은 false로 지정했고 updateMatrix 매서드가 호출되지 않도록 보장해야 함. updateMatrix 매서드를 호출하면 position, scale 등의 속성값으로 행렬이 재계산되어 버림.

객체의 행렬에는 부모 객체와 연관된 객체의 변환값이 저장되어 있음. 월드 좌표계에 대한 객체의 변환을 얻기 위해서는 객체의 Object3D.matrixWorld에 접근해야 함.

부모 또는 자식 객체의 변환이 변경될 때, 자식 객체의 matrixWorld를 업데이트되도록 updateMatrixWorld 매서드를 호출할 수 있음.

Three.js는 3차원 회전을 위해 2가지 방식을 제공: 오일러(Euler) 각도와 쿼터니안(Quaternions). 이 2가지 방식은 상호간의 변환이 가능함. 오일러 각도 방식은 짐벌락(Gimbal Lock)이라고 불리우는 문제가 있음으로 객체의 회전은 반드시 쿼터니안 방식을 위한 quaternion 속성을 이용해야 함.

이 글의 원문

three.js로 체스, 볼링 게임 웹 만들기

three.js를 이용해 웹에서 어떠한 플러그인 없이도 멋진 3차원 그래픽 웹페이지를 개발할 수 있는데요. 게임 느낌의 3D 웹 개발에 대한 컨텐츠입니다. 내용이 다소 길지만 차근 차근 따라하시다보면 금새 완성하실 수 있습니다.


three.js의 RenderTarget 주요 코드 (WebGLRenderTarget)

이 글은 제가 추후 개발시 참조하기 위해 정리한 글로 설명이 매우 함축적일 수 있습니다.

three.js의 RenderTarget은 WebGLRenderTarget 타입으로 Texture 객체를 내부적으로 가지고 있는데, 이 Texture에 장면을 렌더링할 수 있다. 이름이 RenderTarget인 이유는 three.js의 렌더러(Renderer)의 렌더링 대상으로 지정될 수 있기 때문이며 렌더링 대상으로 지정하기 위해 사용되는 메서드는 setRenderTarget이다.

주요 코드를 정리한다. RenderTarget 객체를 생성하고 이 RenderTarget에 그려넣을 장면과 카메라 등을 준비하는 코드다.

_setupRenderTargets() {
    const rtSize = new THREE.Vector2(1024, 1024);
    const renderTarget = new THREE.WebGLRenderTarget(
        rtSize.width, rtSize.height, 
        {
            depthBuffer: false, stencilBuffer: false
        }
    );

    const fov = 75;
    const aspect = rtSize.width / rtSize.height;
    const near = 0.1;
    const far = 5;
    const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
    camera.position.z = 4;
        
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0x444444);

    const color = 0xffffff;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);

    const geometry = new THREE.BoxGeometry(1,4,1);
    const makeInstance = (color, x) => {
        const material = new THREE.MeshPhongMaterial({ color });
        const cube = new THREE.Mesh(geometry, material);
        scene.add(cube);
        cube.position.x = x;
        return cube;
    };

    const cubes = [
        makeInstance(0xff0000, -2),
        makeInstance(0x00ff00, 0),
        makeInstance(0x0000ff, 2),
    ];

    this._renderTargetsObject = { camera, scene, renderTarget, cubes };
}

RenderTarget은 Texture로 사용될 수 있으므로 다음처럼 재질의 map 속성의 값으로 지정될 수 있다.

const material = new THREE.MeshPhongMaterial({ 
    map: this._renderTargetsObject.renderTarget.texture 
});

RenderTarget도 렌더러에 의해 렌더링되어야 하므로 다음 코드가 필요하다.

this._renderer.setRenderTarget(this._renderTargetsObject.renderTarget);
this._renderer.render(this._renderTargetsObject.scene, this._renderTargetsObject.camera);
this._renderer.setRenderTarget(null);

결과는 다음과 같다.