1주차 · 수업 노트
KRthree.js로 서재 만들기
Three.js는 3D를 만드는 자바스크립트(JS) 라이브러리입니다. 우리는 이걸로 이번에 서재를 만들어 볼 거예요. 오늘은 3D 화면이 만들어지는 여섯 단계를 처음부터 끝까지 직접 짭니다.
첫 번째 · 장면
이름을 정합시다 — 장면
상수 장면 = 새 삼차원.장면()
상수로 "장면"이라는 이름을 만들고, 새 3D 장면을 담습니다.
.은 조사 '의' — '삼차원의 장면'이라고 읽으면 돼요.
Tip
이름을 정한다는 건 상수를 만드는 거예요. 저는 먼저 장면부터 만들 거라, 이름을 "장면"으로 넣을게요.
두 번째 · 배경색
장면의 색
장면.배경 = 새 삼차원.색('#000')
장면의 배경에 색을 넣습니다. '#000'은 검정입니다.
아직 화면에는 아무것도 안 보여요 — 기본 세팅이 다 갖춰져야 보입니다.
Tip
장면의 색은 값을 넣어야 하니까, 괄호 ( )를 열고 따옴표 안에 '#000'을 넣을게요. 근데 아직 화면에는 아무 변화가 없어요. 3D는 원래 갖춰야 할 기본 세팅이 꽤 많아서, 그걸 다 갖춰야 비로소 화면이 보이거든요.
세 번째 · 카메라
카메라 — 우리의 눈
상수 카메라 = 새 삼차원.원근카메라()
원근카메라 — 사람 눈처럼 멀수록 작게 보이는 카메라를 만듭니다.
Tip
우리는 눈이 있으니까 이 화면을 볼 수 있는 거잖아요. Three.js도 카메라가 있어야 사물을 볼 수 있어요. 그래서 이번에는 카메라를 부릅니다.
세 번째 · 카메라
카메라에 넣는 네 가지 정보
상수 카메라 = 새 삼차원.원근카메라( 75, 창.너비 / 창.높이, 0.1, 1000 )
순서대로: 시야각 75 · 화면 비례(너비÷높이) · 가까운 한계 0.1 · 먼 한계 1000.
멀리까지 다 그리게 하면 무거워져서(렉), 그리는 범위를 정해 둡니다.
Tip
첫 번째는 시야각. 눈을 얼마나 넓게 뜨고 볼지예요. 두 번째는 비례. 지금 웹 브라우저가 16:9인지 3:4인지 — 그래서 여기엔 보통 창.너비/창.높이가 들어가요. 세 번째와 네 번째는 "어디부터 어디까지 그릴지"예요. 먼 곳까지 다 그리게 하면 그만큼 무거워지거든요(렉 걸리잖아요). 우리는 0.1부터 1000까지로 할게요.
세 번째 · 카메라
카메라의 위치, 그리고 바라보는 곳
카메라.위치.설정(10, 10, 10) 카메라.바라보기(0, 0, 0)
카메라를 (10, 10, 10)에 놓고, 정중앙 (0, 0, 0)을 바라보게 합니다.
옆으로 10 · 위로 10 · 앞으로 10 — 비스듬히 위에서 내려다보는 자리예요.
Tip
근데 이 카메라, 위치가 중요하죠. (10, 10, 10)은 옆으로 10, 위로 10, 앞으로 10 — 비스듬히 위에서 내려다보는 자리예요. 그리고 카메라가 어디를 바라볼지도 정해야겠죠? (0, 0, 0)은 공간의 정중앙. 즉 "가운데를 봐라"라는 뜻이에요.
네 번째 · 렌더러
렌더러 — 우리의 뇌
상수 렌더러 = 새 삼차원.렌더러() 렌더러.크기설정(창.너비, 창.높이)
렌더러를 만들고, 그릴 화면 크기를 창에 꽉 차게 정합니다.
Tip
눈이 있어도, 들어온 정보를 처리해서 "보이게" 만드는 건 뇌잖아요. Three.js에서 그 역할을 하는 게 렌더러예요. 카메라가 본 장면을 실제 화면으로 그려 줍니다. 렌더러의 크기도 설정해야죠. 이래야 화면이 가로세로 꽉 차겠죠.
네 번째 · 렌더러
화면(canvas)을 문서에 붙이기
문서.본문.붙이기(렌더러.돔요소)
렌더러의 돔요소가 곧 canvas — 그걸 문서의 body에 붙입니다.
돔요소는 html의 <div> <p> 같은 조각들. "body에 canvas를 붙여라"라는 뜻이에요.
Tip
이제 렌더러가 만든 화면(canvas)을 HTML 문서의 body 안에 붙일 차례예요. 돔요소는 html에서 <div> <p> 이런 게 돔요소인데, 렌더러의 돔요소가 바로 canvas거든요? 그래서 "body에 canvas를 붙여라" 이 얘기입니다.
다섯 번째 · 애니메이션
JS는 반복하면서 켜지는 애예요
렌더러.애니메이션시작(() => {
렌더러.렌더링(장면, 카메라)
})
괄호 안 코드가 매 프레임 — 1초에 약 60번 — 반복 실행됩니다.
여기서 렌더링이 되는 순간, 드디어 화면이 보이기 시작해요.
Tip
근데 이게 JS의 특징인데, JS 화면은 한 장짜리 그림이 아니라 같은 일을 계속 반복하면서 돌아가는 애예요. 우리가 시간을 느끼는 것처럼 1초, 2초, 3초 — 이 화면 안에도 시간이 흐르고 있어요. 그래서 애니메이션 안에서 렌더링을 하면, 이제 화면이 보이기 시작할 거예요.
여섯 번째 · 도형
자, 이제 상자를 만들 거예요
상수 모양 = 새 삼차원.상자모양(2, 2, 2)
상수 재질 = 새 삼차원.표준재질({ 색: '하늘색' })
상수 큐브 = 새 삼차원.도형(모양, 재질)
장면.추가(큐브)
모양 + 재질 = 도형. 만든 큐브는 장면에 추가해야 화면에 보입니다.
Tip
상자는 두 가지 정보로 만들어요. 모양(크기 2짜리 상자)과 재질(하늘색 표면). 지금은 분리된 정보죠? 이 두 개를 합쳐서 하늘색 박스, 즉 도형을 만들어 봅시다. 만들었으면 장면에 추가해야겠죠. 자, 이제 생겼죠.
일곱 번째 · 빛
근데 새까맣네요
상수 주변빛 = 새 삼차원.주변빛('흰색', 0.8)
장면.추가(주변빛)
상수 방향빛 = 새 삼차원.방향빛('흰색', 0.8)
방향빛.위치.설정(5, 5, 5)
장면.추가(방향빛)
주변빛은 전체를 은은하게, 방향빛은 (5, 5, 5)에서 한 방향으로 또렷하게.
Tip
자, 이유를 생각해 봅시다. 우리 눈이 색과 명암을 구별하는 건 빛이 눈에 맺히기 때문이죠? 맞아요, 빛을 넣어야 돼요. 빛은 두 가지를 쓸 거예요. 먼저 공간 전체를 은은하게 비추는 주변빛. 근데 이 빛은 방향이 없어요. 그래서 한 방향에서 또렷하게 비추는 방향빛을 하나 더 추가합니다.
여덟 번째 · 회전
너무 정적이라, 회전을 줘볼게요
렌더러.애니메이션시작(() => {
큐브.회전.x += 0.01
큐브.회전.y += 0.01
렌더러.렌더링(장면, 카메라)
})
+= 는 지금 값에 0.01씩 계속 더하기 — 매 프레임 아주 조금씩 돌아갑니다.
Tip
마지막으로 큐브가 지금 너무 정적이라, 회전 같은 걸 한번 줘보려고요. += 는 "지금 값에 0.01씩 계속 더하겠다"라는 뜻이에요. 애니메이션 안에 넣었으니 매 프레임 — 1초에 약 60번씩 — 아주 조금씩 돌아갑니다.
아홉 번째 · 크기변경
창 크기를 한번 바꿔보겠어요
창.이벤트걸기('크기변경', () => {
카메라.화면비율 = 창.너비 / 창.높이
카메라.투영갱신()
렌더러.크기설정(창.너비, 창.높이)
})
'크기변경' 사건이 일어날 때마다 — 카메라 비율을 다시 맞추고, 적용하고, 렌더러 크기를 다시 정합니다.
( ) => 는 사건이 일어났을 때 실행할 일 묶음. 괄호가 빈 건 받아올 값이 필요 없어서예요.
Tip
창 크기를 바꾸면, 캔버스가 따라서 안 변해요. 캔버스 크기는 애니메이션이 챙겨 주는 게 아니거든요. 그래서 "창 크기가 바뀔 때만" 실행되는 이벤트를 걸어볼게요. '크기변경'은 사건 이름이에요. 그 뒤 ( ) => 는 사건이 일어났을 때 실행할 일 묶음이고요. 괄호가 비어 있는 건, 따로 받아올 값이 필요 없어서예요.
숙제
책 표지 5권 준비해 오기
1) 콘셉트를 하나 정합니다. — 내가 좋아했던 책 / 선물받은 책 / 앞으로 읽을 책 2) 그 콘셉트에 맞는 책 5권의 표지 이미지를 가져옵니다. (집에 있는 책 표지를 스캔하거나 사진 찍기, 또는 인터넷에서 찾기)
다음 주에는 이 표지들을 책에 입혀서(텍스처), 진짜 "나의 서재"를 만들기 시작합니다.
표지가 세로로 긴 직사각형 이미지면 제일 좋아요 — 우리 책 모양(가로 1.8 × 세로 2.6)과 비율이 맞거든요.