threejs note - animation

김경민·2022년 5월 10일
0

threejs

목록 보기
2/4
post-thumbnail

studiomeal 님의 'three.js로 시작하는 3D 인터랙티브 웹'
강의를 듣고 단순 정리한 것임을 밝힘쓰.(보는 이에 대한 친절을 염두하지 않은 글)

requestAnimationFrame()

새로운 동작 또는 업데이트를 할 경우 렌더링 되기 전에 이 함수를 호출한다.
mesh를 y 축을 기준으로 회전 하고 싶은 경우 다음과 같이 사용한다.

	const scene = new THREE.Scene();
    const renderer = new THREE.WebGLRenderer( {
        canvas: canvas,
        antialias: true,
        alpha: true
    });

    renderer.setSize(window.innerWidth, window.innerHeight);

    // 고해상도 디바이스에서는 디바이스의 pixel ratio 를 가져와서 세팅해주면 렌더링도 고해상도로 된다.
    // 개발자 도구로 렌더링된 캔버스와 이미지의 사이즈를 보면 된다.
    renderer.setPixelRatio(window.devicePixelRatio > 1 ? window.devicePixelRatio : 1);
    // alpha(투명도) 값 (renderer 에 alpha:true 되어야 함. )
    // clearcolor 를 먼저쓰고 alpha를 세팅해야 잘 적용된다.!!!
    // scene 의 background를 세팅 하면 이 설정은 덮여진다. 투명도 적용할때는 렌더러에다가 한다.
    renderer.setClearColor('#00ff00');
    renderer.setClearAlpha(0.5);
    const camera = new THREE.OrthographicCamera(
        -(window.innerWidth / window.innerHeight), //left
        window.innerWidth / window.innerHeight, //right
        1, // top
        -1, // bottom
        0.1,
        1000
    );
    
    camera.position.z = 5;
    camera.lookAt(0, 0, 0);
    camera.zoom = 0.3;
    camera.updateProjectionMatrix(); // camera render 속성을 변경하면 호출 해야함.
    scene.add(camera);
	const mesh = new THREE.Mesh(geometry, meterial);
    scene.add(mesh);
    
	function draw() {
        // radian
        // 2파이(3.14*2) = 360도
        mesh.rotation.y += 0.1;

        renderer.render(scene, camera);

        // 얘는 반복을 하는놈은 아니다.
        window.requestAnimationFrame(draw);
    }

아래 영상에서 잘 설명해주신다.
studiomeal 영상

WebGLRender.setAnimationLoop

https://threejs.org/docs/#api/en/renderers/WebGLRenderer.setAnimationLoop

A built in function that can be used instead of requestAnimationFrame. For WebXR projects this function must be used.

이 놈도 윗놈이랑 동일하게 쓸 수 있다. 단 문서에서 보면 WebXR 즉 웹에서 VR, AR 등을 구현할 때는 이놈을 써야한다고 되어 있다.

    // 위와 동일
	function draw() {

        // radian
        // 2파이(3.14*2) = 360도
        mesh.rotation.y += 0.1;

        renderer.render(scene, camera);

        // 얘는 반복을 하는놈은 아니다.
        renderer.setAnimationLoop(draw);
    }

애니메이션 성능 이슈와 보정

성능 이슈

우리가 만든 애니메이션은 실행되는 디바이스마다 성능 차이가 있다.
requestAnimationFrame 함수는 1초에 60번, 즉 60fps(frame per second) 를 '목표'로 실행된다. 60번 실행을 목표로 한다는 말은 반드시 보장되는게 아니라는 뜻이다. 컨텐츠에 따라 상관이 없을 수 있지만 게임 같은 경우에는 문제가 크다.

보정

THREE.Clock().getElapsedTime()

THREE에서 제공하는 Clock 객체의 getElapsedTime() 함수를 이용하면 실행부터 경과된 시간을 돌려준다. 이는 디바이스와 상관없이 절대적인 시간을 뜻하므로 성능이 떨어져 끊겨 보일 지언정 클라이언트간의 시점은 동일하다. 즉 1초가 경과된 경우의 애니메이션의 결과는 동일하게 보정할 수 있다는 말.

draw 함수 마지막에 setAnimationLoop(또는 requestAnimationFrame) 함수를 실행하고 있으니까 그 안에서 시간을 이용한다.

주의할 점은 시간은 계속 증가되는 값이므로 더하기 할당(+=) 에서 할당(=) 연산자로 바꿔 준다.

    // 위와 동일
    const clock = new THREE.Clock();
	function draw() {
        const time = clock.getElapsedTime();
        // 시간은 계속 증가한다.
        mesh.rotation.y = time;

        renderer.render(scene, camera);

        // 얘는 반복을 하는놈은 아니다.
        renderer.setAnimationLoop(draw);
    }

THREE.Clock().getDelta()

getDelta()는 이전 실행과의 시간차를 말한다. 시간 차이기 때문에 증가하는 값이 아니므로 = 이 아닌 += 으로 바꿔 써주면 된다.

getElapsedTime() 과 함께 쓰면 안된다고 한다. 값이 꼬인다..?

	// 위와 동일
    const clock = new THREE.Clock();
	function draw() {
        const delta = clock.getDelta();

        // 시간은 어떤 디바이스에서도 동일하니까
        mesh.rotation.y += delta * 2;        
        
        renderer.render(scene, camera);

        // 얘는 반복을 하는놈은 아니다.
        renderer.setAnimationLoop(draw);
    }

Date.now()

threejs 가 아니라 javascript 내장 객체 Date 를 사용하는 방법.(1970년 1월 1일부터 지난 시간을 millisecond로 반환)
이전에 실행될 때 저장해놓은 시간과 현재 실행될 때의 시간 차를 직접 계산해서 사용한다.
결국 시간차를 이용하는 THREE.Clock().getDelta()와 비슷한 방법이다.

three js 와 상관없이 canvas 애니메이션에도 사용할 수 있다는 장점이 있다.

	// 위와 동일
    let oldTime = Date.now();
	function draw() {
		const newTime = Date.now();
        const deltaTime = newTime - oldTime;
        oldTime = newTime;

        // 시간은 어떤 디바이스에서도 동일하니까
        mesh.rotation.y += deltaTime * 2;        
        
        renderer.render(scene, camera);

        // 얘는 반복을 하는놈은 아니다.
        renderer.setAnimationLoop(draw);
    }

라이브러리를 이용한 애니메이션(feat. gsap)

gsap 이라는 라이브러리를 사용하는 방법이다.

아래와 같이 설치 후,

$ npm i gsap

최상단에서 import 한다. Mesh 의 위치(position) 속성을 변경하려면 아래와 같이 하면 된다.

import gsap from 'gsap';

// ... 중략 ...

gsap.to(
        mesh.position, // 바꿀 속성
        {
            duration: 1, // seconds
            y: 2,
            z: 3
        }
    )

정리

자바스크립트 애니메이션 처리 시에 권장되는 방법은 크게 자바스크립트와 three js 내에서 해결하는 방법과 외부 라이브러리를 이용하는 방법으로 볼 수 있다.

자바스크립트 / three js 내에서 해결

  • requestAnimationFrame(callBack) : 어느 때나 사용 가능.
  • WebGLRender.setAnimationLoop(callBack) : requestAnimationFrame 기반으로 동작한다. WebXR 구현 시에는 반드시 이놈을 쓴다.
    두 놈 다 렌더링하는 함수 마지막에 호출하며 자기 자신(함수)를 callback 으로 넘겨주는 형태로 사용한다.

성능 이슈

애니메이션은 디바이스에 따라 성능 이슈가 있고, 성능 보정을 위한 3가지 방법이 있다.

  • THREE.Clock().getElapsedTime()
  • THREE.Clock().getDelta()
  • Date.now()

fps 자체를 높일 수는 없지만 fps 는 떨어지더라도 특정 시점의 애니메이션 실행 결과는 동일하게 보정할 수 있다.

setInterval 등으로도 애니메이션을 구현할 수 있지만 이는 성능상에 영향을 미친다.

외부 라이브러리 사용

  • gsap 말고도 애니메이션 라이브러리는 많은 것 같다. javascript animation library 로 검색하면 많이 나온다. anime.js, velocity.js, MO.js 등...

0개의 댓글