즐거운 three.js 10편. 나비 만들기
나비 만들기라 제목을 붙였지만, 텍스처가 없는 민망한 나비다. 단, Mesh를 생성하는 과정을 복습하는 차원에서 벡터값과 면을 통제하는 정도의 포스팅이다. 웹에서 3D로 Box지오메트리를 생성하는 코드는 간단하다. new THREE.BoxGeometry( )로 박스가 나타난다. 하지만 박스는 박스이기 전에 한 가정(?)의 '면'이었고, 한 가정의 면이기 전에 또 한 가정의 '선(엣지)'이었고, 또 '점(Veltex)'이었다는 사실!
그렇다. 모든 지오메트리의 시작은 '점'이다. 볼텍스. 시작을 알아야 끝도 알 수 있는 법. 점부터 시작해서 면 그리고 지오메트리 생성까지의 과정을 알고 코딩을 하는 것과 대충 하는 것의 차이는 '위기' 대처능력에서 드러날 것이다. 간단한 사면체를 만들고, 날개처럼 보이는 볼텍스를 움직여 나비처럼 만들어보자. 움직임(애니메이팅)이 들어가면 코딩은 의외로 복잡해질 수 있다.
이번 포스팅에서는 점을 움직이는 애니메이팅을 통해 Mesh에 관한 기본기를 다져 본다.
▷ 기본 뼈대 생성
vertices를 움직이는 애니메이팅 동작을 만들기 위해서는 geometry의 vertices와 face를 하나하나 움직여야 한다. 기본 geometry를 만드는 게 아닌 처음부터 만든다는 생각으로 다음 변수들을 선언하쟈~
let scene, camera, renderer, navi, trackballControls; let add = 0.7; //vertices 움직이는 속도 let createNavi = function(){} let init = function(){} let render = function(){} init(); render(); |
씬, 카메라, 랜더러,트랙볼(마우스 움직임)은 기본으로 깔고, navi라는 변수를 새로 만든다. navi라는 변수에 geometry와 vertices, face를 넣을 생각이다. face와 vertices관련 api는 three.js 사이트를 참고하자. threejs와 관련된 명령어와 기능들은 three.js.org의 DOC에 자세하게 나와 있다. 아래 사이트의 모든 api를 블로그 포스트에서 다루는 건 불가능하겠지만, 끝까지 가보자.
▷ CreateNavi( )함수
three.js API에 보면, Vector3와 Face3는 한쌍이다. 얘네들은 같이 붙어 다녀야 한다. thre.js API문서를 보면, Face3(index1, index2, index3, normal, color)이라고 돼 있다. 여기서 index1, 2, 3은 Vector3의 index값을 의미한다.
가령, THREE.Vector3(0,0,0), THREE.Vector3(0,1,0), THREE.Vector3(3,2,1)이라는 세 개의 벡터값을 .push로 밀어 넣는다면, index1 : (0, 0, 0), index2 : (0, 1, 0), index3 : (3, 3, 1)이다. THREE.Face3(index1, index2, index3)의 index는 벡터3의 각 index를 지칭한다.
긴말 필요없고, 바로 실습에 들어가보쟈!~
let createNavi = function(){ let geometry = new THREE.Geometry(); let material = new THREE.MeshBasicMaterial({ color:0xabcdff}); navi = new THREE.Mesh(geometry,material);
geometry.vertices.push(new THREE.Vector3(0,0,0)); //index0 geometry.vertices.push(new THREE.Vector3(5,0,0)); //index1 geometry.vertices.push(new THREE.Vector3(2,4,3)); //index2 geometry.vertices.push(new THREE.Vector3(2,4,-3)); //index3 let wing = new THREE.Face3(1,2,0); geometry.faces.push(wing); wing = new THREE.Face3(1,3,0); geometry.faces.push(wing); navi.rotation.x = 0.4; navi.rotation.x = 0.6;
scene.add(navi); } |
Box나 Torus를 생성할 때는 new THREE.TorusGeometry()식으로 이름을 직접 넣었다. 하지만 vertices를 직접 찍어야하는 상황에서는 빈 Geometry를 선언한다. Material도 똑같다. navi라는 변수에 THREE.Mesh를 넣었다면, 본격적으로 vertices를 만든다. 여기서 vertices숫자는 상관없다. 많이 만들어도 된다. 단 면이 나오려면 최소 삼각김밥!!이다.
Face3( )같은 경우, 파라미터에 들어가는 숫자는 '위치'가 절대 아니다. 위에서 찍은 vertices의 index번호라는 사실을 반드시 기억하쟈~ 이걸 x, y, z 좌표로 헷갈리는 순간 말려버린다. let wing = new THREE.Face3(1,2,0) 부분은 다음과 같이 한방에 써도 된다.
geometry.faces.push(new THREE.Face3(1,2,0));
geometry.faces.push(new THREE.Face3(1,3,0));
씬에 navi를 넣었다면, init()함수를 작성하고, 마지막 루프함수로 넘어가보쟈~
let init = function(){ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000); camera.position.set(-30,40,30); camera.lookAt(scene.position); let axes = new THREE.AxesHelper(3); scene.add(axes); createNavi(); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(new THREE.Color(0x000000)); renderer.setSize(window.innerWidth*0.375,window.innerHeight*0.375); document.getElementById('webgl-navi').appendChild(renderer.domElement); trackballControls = initTrackballControls(camera,renderer); clock = new THREE.Clock(); } |
▷ Render( ) 루프 함수
현재 진행하는 프로젝트의 목표는 "나비의 날개짓"이다. 쉽게 말하자면, vertices 중에서 두 개의 점을 아래 위로 왔다갔다하게 만드는 것이다. 아래 위로 왔다갔다~ 깊게 생각하지 말자, 특정 거리에 닿으면 y축에 들어가는 값이 -에서 +로 반복적으로 변하면 된다. 이와 관련된 아래의 코드는 반복적인 애니메이팅의 정석이라 할 수 있다. 여차하면 외워 버리자.
let render = function(){ navi.geometry.vertices[2].y -= add; navi.geometry.vertices[3].y -= add; navi.geometry.verticesNeedUpdate = true; if(navi.geometry.vertices[2].y > 5 || navi.geometry.vertices[2].y <-5){ add *= -1; } trackballControls.update(clock.getDelta()); renderer.render(scene,camera); requestAnimationFrame(render); } |
변수를 선언할 때, 'add'를 선언했었다. navi.geometry.vertice[index].y -= add;라고 하면, add의 수치가 계속 navi의 vertices[index]에 더해진다. 즉, vertices가 움직인다! 그러나 vertices가 계속 움직여 대기권을 넘어가버리면 곤란하다. 우리가 원하는 건 왔다갔다~ 하는 것이다.
if문을 살펴보자. 문제의 vertices 인덱스 값은 [2]과 [3]이다. 그런데, add값은 [2]과[3] 모두 적용되고 있기 때문에 어느 한쪽으로 인해 add의 부호가 바뀌면 양쪽 모두 적용될 수 있다. 위의 코드에서 가장 중요한 부분은 감귤색이 있는 navi.geometry.verticesNeedUpdate = true 이다. 이 한줄이 없으면 vertices는 절대로 움직이지 않는다!
update가 안 되면 움직이지 않는다는 점을 반드시 명심하자. 이 부분 때문에 30분 가량 삽질했댜~
[최종 코드]
let scene,camera,renderer,trackballControls, navi; let add = 0.8; let createNavi = function(){ let geometry = new THREE.Geometry(); let material = new THREE.MeshBasicMaterial({ color:0xabcdff }); navi = new THREE.Mesh(geometry,material); geometry.vertices.push(new THREE.Vector3(0,0,0)); geometry.vertices.push(new THREE.Vector3(5,0,0)); geometry.vertices.push(new THREE.Vector3(2,4,3)); geometry.vertices.push(new THREE.Vector3(2,4,-3)); geometry.faces.push(new THREE.Face3(1,3,0)) geometry.faces.push(new THREE.Face3(1,2,0))
scene.add(navi); } let init = function(){ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000); camera.position.set(-30,40,30); camera.lookAt(scene.position); let axes = new THREE.AxesHelper(3); scene.add(axes); createNavi(); renderer = new THREE.WebGLRenderer(); renderer.setClearColor(new THREE.Color(0x000000)); renderer.setSize(window.innerWidth*0.375,window.innerHeight*0.375); document.getElementById('webgl-navi').appendChild(renderer.domElement); trackballControls = initTrackballControls(camera,renderer); clock = new THREE.Clock(); } let render = function(){ navi.geometry.vertices[2].y -= add; navi.geometry.vertices[3].y -= add; navi.geometry.verticesNeedUpdate = true; if(navi.geometry.vertices[2].y > 5 || navi.geometry.vertices[2].y <-5){ add *= -1; } trackballControls.update(clock.getDelta()); renderer.render(scene,camera); requestAnimationFrame(render); } init(); render(); </script> |
'코드 스터디' 카테고리의 다른 글
Three.js 11편 웹에서 3D Text작성하기 (0) | 2019.05.01 |
---|---|
자바스크립트 문법 (4) Map / Set (0) | 2019.05.01 |
자바스크립트 문법 (3) forEach / map / some / filter (0) | 2019.04.30 |
자바스크립트 문법 (2) 배열 Method (0) | 2019.04.29 |
three.js 9편 "토성 만들기" (0) | 2019.04.29 |
댓글