three.js 도전기 (디자인베이스 님 유튜브) - 1탄

saebyeol·2022년 8월 14일
1
post-thumbnail

이번 졸업작품에서 3D 전시회를 구현하기로 했다.
3D환경을 구현하려면 three.js를 사용해야하므로 참고하기 좋은 자료들을 찾아보다가
유튜버 디자인베이스님의 영상( https://www.youtube.com/watch?v=KxR4NZDzNOU&list=PLkbzizJk4Ae9hHI_YUD3fRv8xLfS3jGEW&index=5 )이 가장 쉽고 자세히 기본을 알려주는 것 같아 이 영상을 보며 기본을 다졌고
이에 대한 내용을 기록하고자 한다.

  1. 설치
    웹팩 설치, npm 설치, 파일 직접 다운로드 등의 방법이 있지만 난 npm 설치를 선택하였다.

    npm i --save three

  2. 기본 개념 (기본 구성요소 구현, 회전까지)



가장 기본적인 구성요소가 renderer, scene, camera가 있고
화면에 보여질 도형인 mesh가 있다.

이들을 구현한 코드는

import * as THREE from '../node_modules/three/build/three.module.js'

//장면
const scene=new THREE.Scene();
scene.background=new THREE.Color(0x004fff)

//카메라
const camera=new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.z=2;

//렌더러
const renderer=new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);

//렌더러의 장면을 어디에 띄울지 ( canvas 사용해서 특정 html 요소에 띄울 수도 있음 )
document.body.appendChild(renderer.domElement);

//메쉬(도형) : geometry+meterial
const geometry01=new THREE.BoxGeometry(0.5,0.5,0.5); //geometry: 도형
const meterial01=new THREE.MeshStandardMaterial(
    {color:0x999999}
);
const obj01=new THREE.Mesh(geometry01, meterial01);
obj01.position.x=-1;
scene.add(obj01);

const geometry02=new THREE.ConeGeometry(0.4,0.7,6); //geometry: 도형
const meterial02=new THREE.MeshStandardMaterial(
    {color:0x999999}
);
const obj02=new THREE.Mesh(geometry02, meterial02);
scene.add(obj02);

const geometry03=new THREE.IcosahedronGeometry(0.4,0); //geometry: 도형
const meterial03=new THREE.MeshStandardMaterial(
    {color:0x999999}
);
const obj03=new THREE.Mesh(geometry03, meterial03);
obj03.position.x=+1;
scene.add(obj03);



//애니메이션 적용
function render(time){
    time*=0.0005;
    //큐브 회전
    
    obj01.rotation.y=time;
    
    obj02.rotation.y=time;
    
    obj03.rotation.y=time;

    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
requestAnimationFrame(render);

여기서는 mesh를 세개를 만들어보았고 각각 박스모양 하나, 원뿔모양 하나, 이십면체 모양 하나를 만들었다.
이러한 모양들은 공식홈페이지인 https://threejs.org/docs/index.html?q=geometry#api/en/geometries/IcosahedronGeometry 에서 도형마다 속성값을 변경해보며 원하는 도형의 코드를 참고하여 작성했다.

mesh를 만들기 위해서는 geometry와 meterial을 정의하여야 하고, 이를 scene에 추가하면 화면에 mesh를 띄울 수 있다.

mesh를 가만히 두면 이 mesh의 측면이나 뒷면을 볼 수가 없다. 따라서 animation을 지정해주었고 나는 y축 회전을 통해 mesh들이 돌아가도록 하였다.
마지막에 renderer.render(scene, camera); 를 통해 이 내용들이 화면에 렌더링 되게 된다.

  1. 렌더러 옵션, 반응형
//렌더러
const renderer=new THREE.WebGLRenderer({
    alpha:true, //scene의 background 없애고 이 옵션 사용하면 빈 배경에 도형만 뜨게 됨.
    antialias: true //계단 현상 없애기
});

렌더러의 옵션을 통해 계단 현상을 없애 매끄럽게 만들거나 도형의 배경을 없애는 등의 효과를 줄 수 있다.

//반응형처리 (화면크기에 맞게 렌더러크기 조정)
function onWindowResize(){
    camera.aspect=window.innerWidth / window.innerHeight; //도형의 종횡비 일정하게
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight)
}
window.addEventListener('resize', onWindowResize);

또한 화면 크기의 resize의 대한 반응형 처리를 위해 위와 같은 코드를 짤 수 있다.
화면이 resize되면 camera.aspect 설정을 통해 도형의 종횡비는 일정하도록 설정해주고
renderer의 size는 늘어나거나 줄어든 화면의 크기에 맞게 재설정을 해준다.

여기까지 구현했을 때의 결과는 아래와 같다.
(도형이 검은색으로 보이는 것은 아직 light 를 설정해주지 않아서 그렇다)

  1. meterial (재질)

먼저 light를 추가하자

//빛
const pointLight=new THREE.PointLight(0xffffff, 1); //(색상, 세기)
pointLight.position.set(0,2,12);
scene.add(pointLight);

light의 색상과 세기, 위치를 지정한 후 scene에 추가해주면 지금까지는 보이지 않았던 도형의 색이 보이게 될 것이다.


그리고 다양한 재질(meterial)을 설정해보았다.
재질도 마찬가지로 공식 홈페이지에 (https://threejs.org/docs/index.html?q=me#api/en/materials/MeshBasicMaterial) 다양한 재질의 종류들과 각각의 옵션들을 제공하고 있다. 이를 참고하여 원하는 재질을 설정하면 된다.

//메쉬(도형) : geometry+meterial
const geometry=new THREE.TorusGeometry(0.3,0.15,16, 40); //geometry: 도형
const meterial01=new THREE.MeshStandardMaterial(
    {color:0xFF7F00, 
    roughness: 0.1
    }
);
const obj01=new THREE.Mesh(geometry, meterial01);
obj01.position.x=-2;
scene.add(obj01);

const meterial02=new THREE.MeshPhongMaterial(
    {color:0xFF7F00,
    shininess: 60}
);
const obj02=new THREE.Mesh(geometry, meterial02);
obj02.position.x=-1;
scene.add(obj02);

const meterial03=new THREE.MeshPhysicalMaterial(
    {color:0xFF7F00,
    clearcoat: 1, 
    clearcoatRoughness: 0.1
    }
);
const obj03=new THREE.Mesh(geometry, meterial03);
scene.add(obj03);

const meterial04=new THREE.MeshStandardMaterial(
    {color:0xFF7F00}
);
const obj04=new THREE.Mesh(geometry, meterial04);
obj04.position.x=+1;
scene.add(obj04);

const meterial05=new THREE.MeshStandardMaterial(
    {color:0xFF7F00}
);
const obj05=new THREE.Mesh(geometry, meterial05);
obj05.position.x=+2;
scene.add(obj05);

나의 경우 MeshStandardMeterial, MeshPhongMeterial, MeshPhysicalMeterial 을 사용해보았고 각 meterial마다 정말 많은 옵션들이 존재하지만 다 써보진 못했으니 공식홈페이지를 참고하면 되겠다.

이 코드의 결과는 아래와 같다.

  1. 재질에 텍스처 추가

도형에 텍스처를 추가하려면 먼저 원하는 텍스처의 이미지를 다운받아야 한다.
https://3dtextures.me/
에 다양한 텍스처들을 제공하고 있는데 나는 바닥재같은 텍스처를 원해서 나무바닥 느낌의 텍스처를 다운받았다.

다운받으면 이렇게 유형별 이미지가 다운받아진다. 이를 img 폴더에 넣으면 된다.

//텍스처 추가
const TextureLoader=new THREE.TextureLoader();
const textureBaseColor=TextureLoader.load('img/Wood_027_basecolor.jpg');
const textureNormalMap=TextureLoader.load('img/Wood_027_normal.jpg');
const textureHeightMap=TextureLoader.load('img/Wood_027_height.png');
const textureRoughnessMap=TextureLoader.load('img/Wood_027_roughness.jpg');

각 유형별 이미지로 사용할 텍스쳐들을 정의해준다.

//메쉬(도형) : geometry+meterial
const geometry=new THREE.SphereGeometry(0.3, 32 ,16); //geometry: 도형
const meterial01=new THREE.MeshStandardMaterial(
    { map: textureBaseColor}
);
const obj01=new THREE.Mesh(geometry, meterial01);
obj01.position.x=-2;
scene.add(obj01);

const meterial02=new THREE.MeshStandardMaterial(
    { map: textureBaseColor,
    nomalMap: textureNormalMap
    } //빛이 닿지 않는 부분을 좀 더 디테일하게 표시함.
);
const obj02=new THREE.Mesh(geometry, meterial02);
obj02.position.x=-1;
scene.add(obj02);

const meterial03=new THREE.MeshStandardMaterial(
    {map: textureBaseColor,
    nomalMap: textureNormalMap,
    displacementMap: textureHeightMap,
    displacementScale: 0.5
    }
);
const obj03=new THREE.Mesh(geometry, meterial03);
scene.add(obj03);

const meterial04=new THREE.MeshStandardMaterial(
    {map: textureBaseColor,
    nomalMap: textureNormalMap,
    displacementMap: textureHeightMap,
    displacementScale: 0.5,
    roughnessMap:textureRoughnessMap,
    roughness: 0.8
    }
);
const obj04=new THREE.Mesh(geometry, meterial04);
obj04.position.x=+1;
scene.add(obj04);

meterial의 옵션으로 map, nomalMap, 등을 사용하여 원하는 텍스쳐를 적용한다. 여기서는 텍스쳐들을 좀 더 자세히 보기 위해 구 모양의 도형을 사용했다.

normalMap의 경우 baseColor보다 좀 더 세심한 표현이 가능하고 , HeightMap의 경우 텍스처의 높낮이를 표현해주며 height의 정도도 설정가능하다.
RoughnessMap의 경우에는 광이 나듯 빛 표현이 가능하고 이 또한 정도를 설정할 수 있다.

  1. 카메라
  • fov
    : 시야각(화각)
    렌즈의 종류 - 광각, 표준, 망원
    광각의 fov: 84 ~ 63
    표준의 fov: 47
    망원의 fov: 28 ~ 8

  • aspect
    : 종횡비 ( 가로세로비율 )

-near & far

이 네가지 속성을 아래와 같이 카메라에 적용시키면 되는 것이다.

const fov=75; //시야각
const aspect=window.innerWidth/window.innerHeight;
const near=0.1;
const far=1000;
const camera=new THREE.PerspectiveCamera(fov, aspect, near, far);
  • 카메라의 위치, 시야 조정
//camera.position.z=2;
camera.position.set(2,2,1); //x: 좌우 이동 , y: 상하 이동, z: 앞뒤 이동(깊이감)
//카메라가 바라보는 위치 조정 
camera.lookAt(new THREE.Vector3(0,0,0)) //항상 카메라가 (0,0,0)을 바라보도록

카메라의 위치를 (2,2,1)로 설정함으로써 카메라가 물체와 멀리 떨어지게 되어 물체가 보이지 않을 수도 있다. 이럴때에는 카메라가 물체를 바라보도록 (이 경우에는 내려다보도록) 카메라가 보는 위치 또한 lookAt을 사용하여 설정 가능하다. 이 코드에서는 카메라가 (0,0,0) 즉 물체가 위치하는 곳을 바라보도록 지정하였다.
아 그리고 바닥도 만들어주었다.

//바닥 추가
const planeGeometry=new THREE.PlaneGeometry(30,30,1,1);
const planeMaterial=new THREE.MeshStandardMaterial({
    color:0xeeeeee
})
const plane=new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotation.x=-0.5*Math.PI;
plane.position.y=-0.5;
scene.add(plane);

결과는 아래와 같다.

아 그리고.. 지금 안 부분인데 지금껏 material을 meterial로 잘못 쓰고 있었다..ㅋㅋㅋㅋㅋ 그래서 이제부터 제대로 쓰고 있다..

  1. light
  • AmbientLight
    : 모든 물체를 대상으로 전역에서 빛을 비춤, 그림자의 개념이 없음

  • DirectionalLight
    : 특정방향으로 빛을 비춤, 해와 비슷하다고 생각하면 됨.

const directionalLight=new THREE.DirectionalLight(0xffffff,0.5);
directionalLight.position.set(1,1,1);
const dlHelper=new THREE.DirectionalLightHelper(directionalLight, 0.5, 0x0000ff) //파랑으로 색 지정함
scene.add(dlHelper);
scene.add(directionalLight);

여기서 helper는 빛이 어디에서 오는지를 보여주는 편리한 도구이다.

위 사진에서 보이다시피 파란선으로 빛의 방향이 표시되고 있다.

  • HemisphereLight
    : 하늘에서 오는 빛, 지상에서 오는 빛을 각각 지정 가능

  • PointLight
    : 특정 위치에 빛이 존재

const pointLight=new THREE.PointLight(0xffffff,1);
scene.add(pointLight);
pointLight.position.set(-2, 0.5, 0.5);
const plHelper=new THREE.PointLightHelper(pointLight, 0.4, 0x0000ff); //헬퍼 대상, 헬퍼 사이즈
scene.add(plHelper);

  • RectAreaLight
    : 빛의 width, height 지정 가능
const rectLight=new THREE.RectAreaLight(0xffffff, 2 ,1 , 1.5);//색, width, height, 강도
scene.add(rectLight)
rectLight.position.set(0.5, 0.5, 1);
rectLight.lookAt(0,0,0) //빛이 어디를 향할지 

  • SpotLight
    : 무대나 공연장에서 쓰이듯 특정 위치를 부각하기 위한 빛, 은은하게 퍼지는 빛보다는 강렬히 특정 위치만을 강조
const spotLight=new THREE.SpotLight(0xffffff, 0.5);
scene.add(spotLight);


지금까지 올라온 디자인베이스님의 강의는 여기까지였다. 다음 영상이 올라오면 나도 2탄을 기록해야겠다.

profile
프론트엔드 개발자

1개의 댓글

comment-user-thumbnail
2023년 10월 20일

도움이 됐어요

답글 달기