three.js 정리

꿀이·2022년 7월 5일
0

개요

예전에 학교다닐때 졸업작품으로 opengl 을 사용해서 프로젝트를 진행한 적이 있었는데... 그때 생각이 나서 인프런 1분코딩 Three.js 강의 를 들으면서 정리하고 웹게임을 만들어 볼려고 한다.

기본 요소

아래 요소들을 모두 작성한 후에 renderer.render() 를 해주면 된다.

renderer

얘를 그냥 화면을 뿌려주는 tv 라고 생각하자. html 에 canvas 랑 연동해서 사용하는게 좋다고 하네

//이렇게 canvas 에 연결해서 사용하는게 나중에 좀 더 좋다고 한다.
const canvas = document.querySelector('#three-canvas');
const renderer = new THREE.WebGLRenderer({
    canvas: canvas,
    antialias:true,//계단현상 제거 속성
});
renderer.setSize(window.innerWidth, window.innerHeight);

scene

무대라고 생각하자 여기에 이제 지형, 캐릭터, 카메라 ... 이런 것들을 추가하면 된다.

//Scene 생성
const scene = new THREE.Scene();

camera

무대(scene) 를 바라보는 카메라의 위치를 생성해준다. scene.add() 를 해주면 된다.

//Camera 생성
const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth/window.innerHeight,
    0.1,
    1000
);
//카메라를 모니터에서 내가 앉아있는 방향으로 (z축으로 양수만큼 이동)
camera.position.z = 5;
camera.position.y = 2;
camera.position.x = 1;

//Scene 에 카메라를 추가시켜준다.
scene.add(camera);

mesh

얘는 캐릭터, 건물, 지형, ... 이런거라고 생각하면 되는데, 프레임을 나타내는 geometry 와 재질을 나타내는 material 로 나뉘어 진다.

//Mesh 생성
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
    color: 0xff0000,
});
const mesh = new THREE.Mesh(geometry, material);

//얘도 scene 에 추가해준다.
scene.add(mesh);

창 사이즈 변경 대응

EventListener 에 아래 함수를 넣어준다. 창이 변경될 때마다 setSize() 를 호출해줘서 render 를 다시 해준다. 창 크기가 변경되니까 기본적으로... 카메라의 aspect 가 변경이 필요하다. 카메라의 설정이 바뀌는 경우는 updateProjectionMatrix() 를 호출해줘야한다.renderer 에 윈도우 창 크기 관련된 설정들도 변경해줘야하고

    //이벤트 감지해서 동적 화면 변경 대응
    function setSize(){
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();//카메라의 변화가 있을 때 실행해줘야 하는 메서드
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.render(scene, camera);
    }
    window.addEventListener('resize',setSize);

고해상도 모니터를 위한 설정

요즘 고해상도 모니터들이 많이 나오기 때문에 좀더 부드러운 설정을 위해서 다음과 같이 설정해준다. 보통 2정도만 되도 충분하다고 한다. 그래서 max 로 2로 설정해준다.

pc 의 픽셀 비율이 만약 2 라고 출력되면 100px 이미지를 표현할 때 200px 을 사용한다.

    console.log(window.devicePixelRatio);
    renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);//2만되어도 충분하기 때문에 이렇게 함

배경 색 및 투명도

renderer 를 우리가 보는 tv 화면이라고 생각하고 scene를 실제 무대라고 생각! scene의 배경색 관련해서 우선순위가 더 높다.

아래와 같이 코드를 작성하면 파랑색 배경이 나오고 renderer에 설정한 초록색 배경은 무시된다.

    renderer.setClearAlpha(0.5);//투명 정도를 설정할 수 있다.
    renderer.setClearColor(0x00ff00);//이렇게 배경색을 입힐 수도 있다.

    //Scene 생성
    const scene = new THREE.Scene();

    //렌더러 (우리가 보는 tv화면) 보다 scene (실제 무대) 가 배경색에서 더 서선순위가 있다.
    //렌더러 위에 scene가 덧칠을 했다고 생각하자
    scene.background = new THREE.Color('blue');

시간 관련 함수 (개인pc별 성능보정)

js 를 이용하는 방법

Date.now() 를 이용해서 성능 보정에 사용할 수 있다. 단위가 밀리세컨드라 0.0001 을 곱해서 단위 보정이 필요

    //js 의 date.now() 이용
    let oldTime = Date.now();
    function draw() {
        const newTime = Date.now();
        const deltaTime = newTime - oldTime;
        oldTime = newTime;

        //라디안 기반이라 3.14 * 2 = 360도를 나타냄
        mesh.rotation.y += deltaTime * 0.001;//단위가 밀리초라서 되게 큰수가 나온다
        renderer.render(scene, camera);
        // window.requestAnimationFrame(draw);
        renderer.setAnimationLoop(draw); //이거도 내부적으로 requestAnimationFrame 을 호출한다고 함
    }

clock.getElapsedTime()

clock.getElapsedTime() 을 이용하면 실행 후 흐르고 있는 시간을 확인할 수 있다.

요걸 이용해서 빨리감기 같은걸 이용할 수 있을까?

   //그리기
    const clock = new THREE.Clock();

    function draw() {
        // console.log(clock.getElapsedTime());
        const time = clock.getElapsedTime();//절대 경과 시간
        //라디안 기반이라 3.14 * 2 = 360도를 나타냄
        
        mesh.rotation.y = time;
        renderer.render(scene, camera);
        // window.requestAnimationFrame(draw);
        renderer.setAnimationLoop(draw); //이거도 내부적으로 requestAnimationFrame 을 호출한다고 함
    }

안개

다음과 같이 안개를 설정할 수 있다. 랜덤값을 줘서 mesh 들을 생성해주고 scene 에 각각 추가해준다. 그리고 meshes에도 추가해주고 이 배열을 가지고 draw() 에서 활용한다.

    //Scene 생성
    const scene = new THREE.Scene();
    scene.fog = new THREE.Fog('blue',3 , 7)//색,near,far


	const meshes = [];
    let mesh;
    for (let i = 0; i < 10 ; i++) {
        mesh = new THREE.Mesh(geometry, material);
        mesh.position.x = Math.random() *5 -2.5;
        mesh.position.z = Math.random() * 5 -2.5;
        scene.add(mesh);
        meshes.push(mesh);
    }


    //그리기
    const clock = new THREE.Clock();

    //js 의 date.now() 이용
    let oldTime = Date.now();
    function draw() {
        const newTime = Date.now();
        const deltaTime = newTime - oldTime;
        oldTime = newTime;

        meshes.forEach(item => {
            item.rotation.y += deltaTime * 0.001;
        })

        renderer.render(scene, camera);
        // window.requestAnimationFrame(draw);
        renderer.setAnimationLoop(draw); //이거도 내부적으로 requestAnimationFrame 을 호출한다고 함
    }

xyz축 & 격자

그냥 만들 수도 있을거 같은데... 매번하기 번거로우니까 참고하면 될듯

프레임 출력하기

npm i stats.js 설치를 우선 하고 나서 Stats 객체 생성한 후에 body 에 stats.domElement 를 추가해준 후에 draw() 에서 계속 update를 해주면 된다.

	//Stats
	let stats = new Stats();
	document.body.append(stats.domElement);
	
	function draw() {
		stats.update();//draw 에서 update 를 해주면 출력된다.
		renderer.setAnimationLoop(draw);
	}

GUI

npm i dat.gui 을 설치하고 아래와 같이 사용할 수 있다. 이렇게 하는게 테스트 할때 간편할 거 같기도 하고~

	//Dat GUI
	let gui = new dat.GUI();
	gui.add(mesh.position, 'y', -5, 5, 0.01).name('mesh.y 이동');
	gui.add(camera.position, 'y')
		.min(-5)
		.max(5)
		.step(0.01)
		.name('카메라.y 이동');

Transform 관련

거리 구하기

		//원점으로부터 떨어진 거리
		console.log(mesh.position.length());
		//특정 좌표 사이의 거리
		console.log(mesh.position.distanceTo(new THREE.Vector3(5, 0, 0)));

크기 조정 (scale)

이거도 두가지 방식으로 가능

		mesh.scale.set(2, 1, 1);
		mesh.scale.y = 3;

회전

회전도 두가지 방식으로 가능하고 회전하기 전에 reorder() 메서드를 통해서 어떤축을 먼저 회전할지를 정할 수 있다. default 는 xyz 순서로 회전하는듯? 나중에 회전이 생각처럼 안되면 한번 참고해보자

		mesh.rotation.reorder('YXZ')
		mesh.rotation.set(10, 20, 0);

	 	mesh.rotation.reorder('YXZ');
		mesh.rotation.x = 10;
		mesh.rotation.y = 20;

그룹 만들기

그룹생성을 통해서 구현을 조금 더 쉽게 할 수 있는듯 태양,지구,달 을 만든다고 할때 각각의 위치와 회전을 제어하려고 하면 상당히 번거로울거 같은데, group 을 통해서 간단하게 할 수 있넹

group1 에 태양과 group2(지구,달) 이 있다고 하면 group1 을 회전시키면 group2 에 있는 매쉬들도 같이 회전이 되는걸 알 수 있다.

	//group 생성
	let group1 = new THREE.Group();
	let group2 = new THREE.Group();
	group2.position.x = 2;
	let group3 = new THREE.Group();
	group3.position.x = 0.5;
	//매쉬 생성
	const sun = new THREE.Mesh(geometry, material);
	const earth = new THREE.Mesh(geometry, material);
	earth.scale.set(0.3, 0.3, 0.3);
	const moon = new THREE.Mesh(geometry, material);
	moon.scale.set(0.15, 0.15, 0.15);

	group3.add(moon);
	group2.add(earth, group3);
	group1.add(sun, group2);
	scene.add(group1);

	// 그리기
	const clock = new THREE.Clock();
	function draw() {
		const delta = clock.getDelta();
		group1.rotation.y += delta;
		group2.rotation.y += delta * 3;
		group3.rotation.y += delta * 6;
		
		renderer.render(scene, camera);
		renderer.setAnimationLoop(draw);
	}

profile
내게 맞는 옷을 찾는중🔎

0개의 댓글