본문 바로가기

「three.js」웹에서 3D애니메이팅을 구현해보자

by Recstasy 2019. 4. 24.

 

CG의 꽃은 '애니메이팅'이다. 움직임이 없으면, 2D 포토샵 그래픽이 최상이다. 그래서 이 같은(움직임) CG의 강점은 자바스크립트의 강점과 닮아 있다. 자바스크립트 역시 정적인 Web을 동적인 웹으로 탈바꿈 시켰기 때문이다. 어쨌든 자바스크립트는 웹을 움직이도록 만들었다. 

 

FBXLoader Test(three.js)

 

필자가 three.js에 흥미를 느꼈던 부분 역시 '애니메이팅'이었다. 텍스트나 이미지를 움직이는 것이 아닌 '3D 소스'들을 제어할 수 있다는 점은 2016년 당시 혁신처럼 다가왔다.  

 

requestAnimationFrame = 부드러움

 

 

 


| requestAnimationFrame

과거 자바스크립트는 setInterval()함수를 사용해서 2D 게임을 구현했다. 하지만 3D 프로그래밍에서는 setInterval()함수를 사용하지 않는다. setInterval()함수는 접속환경과 브라우저 상태에 따라 부드러움이 달라지기 때문이다. 즉, 불안정하다. 게다가 CPU사용량까지 많다보니 태블릿 PC나 모바일에서는 제대로 된 동작을 구현하기 힘들다. 

 

setInterval()함수의 대안으로 떠오른 방법은 'requestAnimationFrame()함수다. requestAnimationFrame함수는 부드러운 동작을 만들 수 있으며, cpu사용량도 효율적이다. 이에 따라, 3D 애니메이팅을 효과적으로 표현하는 방식으로 다음과 같은 코드가 정석으로 통하고 있다. 

 

function renderScene(){
   requestAnimationFrame(renderScene) //재귀
   renderer.render(scene, camera)
}

 

 

 

 

 

three.js에서 requestAnimationFrame으로 구현한 애니메이팅 동작은 대략 위와 같다.

 

위의 코드에서 핵심은 막줄 renderScene()함수다. renderer를 반복(재귀)하는 함수는 모든 오브젝트를 움직이게 만들 수 있다. requestAnimationFrame(renderScene)함수는 일종의 재귀함수이므로 renderScene()함수 안에 x,y,z의 움직임을 설정하는 것만으로 해당 오브젝트들을 동적으로 만든다.

 

 

//renderScene()함수

 function renderScene(){
    stats.update();
    cube.rotation.x += 0.02;
    cube.rotation.y += 0.02;
    cube.rotation.z += 0.02;
    
    step += 0.02;
    
    cube.position.x = 2+(3*(Math.cos(step)));
    cube.position.y = 3+(20*Math.abs(Math.sin(step)));
    requestAnimationFrame(renderScene);
    renderer.render(scene,camera)
 }

 

 

cube의 x, y, z축으로 0.02씩 회전하고 있으며, x, y방향으로 각각 (Cos, Sin) 운동을 한다. 최종 코드는 아래와 같다.

 

 

//최종코드

(function () {
    init();
})()

function init() {

  //블로그 div설정
	let contentEl = document.getElementById('webgl-ani')
    
  //THREE.js 환경설정
    const scene = new THREE.Scene();
    const camera = new THREE.PerspectiveCamera(45, contentEl.clientWidth / contentEl.clientHeight, 0.1, 1000);
    let renderer = new THREE.WebGLRenderer();
    renderer.setClearColor(new THREE.Color(0x000000));
    renderer.setSize(contentEl.clientWidth, contentEl.clientHeight );
    renderer.shadowMap.enabled = true;
	
    let spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-5, 30, 15);
    spotLight.castShadow = true;
    
  //Object설정
    let planeGeometry = new THREE.PlaneGeometry(40, 60);
    let planeMaterial = new THREE.MeshLambertMaterial({
        color: 0xffffff
    });
	
    let plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.set(0, 0, 10);
    plane.rotation.x = -0.4 * Math.PI;
    plane.receiveShadow = true;
    
    let cubeGeometry = new THREE.BoxGeometry(5, 5, 5);
    let cubeMaterial = new THREE.MeshLambertMaterial({
        color: 0x7777ff
    });
	
    let cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
    cube.position.y = 6;
    cube.position.x = 4;
    cube.position.z = -5;
    cube.rotation.x = 60;
    cube.castShadow = true;
    
    let axes = new THREE.AxesHelper(20);
    axes.position.set(5, 0, 10);
    
  //씬 추가하기
    scene.add(cube);
    scene.add(axes);
    scene.add(plane);
    scene.add(spotLight);
    camera.position.set(10, 50, -15);
    camera.lookAt(scene.position);
    
    let step = 0;
    contentEl.appendChild(renderer.domElement)
	
  //재귀 함수
    renderScene();
    
    function renderScene() {
        cube.rotation.x += 0.02;
        cube.rotation.y += 0.02;
        cube.rotation.z += 0.02;
        step += 0.02;
        cube.position.x = 2 + (3 * (Math.cos(step)));
        cube.position.y = 3 + (20 * Math.abs(Math.sin(step)));
        requestAnimationFrame(renderScene);
        renderer.render(scene, camera)
    }
}

 

댓글

최신글 전체

이미지
제목
글쓴이
등록일