ThreeJS 8. Geometries and meshes

jiho·2019년 12월 23일
3

Threejs

목록 보기
9/10
var sphereGeometry = new THREE.SphereGeometry(4,20,20); 
var sphereMaterial = new THREE.MeshBasicMaterial({color: 0x7777ff); 
var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial)

지금까지 object를 추가할 때, 위 예시와 같이 SphereGeometry 와 MeshBasicMaterial객체를 생성해서 Mesh 속에서 결합을 했습니다.
이번 포스트에서 geometries와 mesh에 대해 자세히 알아보겠습니다.

The Properties and Functions of a geometry

ThreeJS에서는 저희가 특별한 설정없이 사용할 수 있는 아주 많은 geomerties이 있습니다. 단순히, material을 추가하고 mesh를 만들면 끝입니다.

위 스크린샷은 Three.js에서 제공하는 몇개의 geometry를 적용한 object를 scene에 추가한 것입니다. 차후에 Threejs에서 제공하는 기초적인 geometry부터 고급 수준의 geometry에 대해 알아보겠습니다. 지금부터는 geometry가 무엇인지 자세히 보겠습니다.

Threejs 뿐만 아니라 대부분의 3D library에서 geomtry는 기본적으로 3D 공간에서의 점들(Vertices 라고 불립니다.)과 그러한 점들을 연결하는 Face들의 집합입니다.
저희들이 ThreeJS에서 제공되는 geometry를 사용할 때는 모든 vertices와 faces를 직접 정의할 필요없습니다. 큐브의 경우, 단지 width, height, depth만 정의해줄 필요가 있습니다. 그렇게하면 ThreeJS에서 받은 정보를 바탕으로 8 vertices와 그에 알맞은 face를 가진 geometry를 만들냅니다.
하지만 저희가 직접 vertices와 faces를 사용해서 geometry를 만들 수도 있습니다.

geometry.faces

geometry.vertices

geometry.computeFaceNormals()

geometry.verticesNeedUpdate

var vertices = [  
  new THREE.Vector3(1,3,1),  
  new THREE.Vector3(1,3,-1),  
  new THREE.Vector3(1,-1,1),  
  new THREE.Vector3(1,-1,-1),  
  new THREE.Vector3(-1,3,-1), 
  new THREE.Vector3(-1,3,1),  
  new THREE.Vector3(-1,-1,-1),  
  new THREE.Vector3(-1,-1,1) ];
var faces = [  
  new THREE.Face3(0,2,1),  
  new THREE.Face3(2,3,1),  
  new THREE.Face3(4,6,5),  
  new THREE.Face3(6,7,5),  
  new THREE.Face3(4,5,1), 
  new THREE.Face3(5,0,1),  
  new THREE.Face3(7,6,2),  
  new THREE.Face3(6,3,2),
  new THREE.Face3(5,7,0),  
  new THREE.Face3(7,2,0),  
  new THREE.Face3(1,3,4),  
  new THREE.Face3(3,6,4), 
];
var geom = new THREE.Geometry(); 
geom.vertices = vertices; 
geom.faces = faces; 
geom.computeFaceNormals(); 

위 코드는 간단한 큐브를 만드는 방법을 보여줍니다. 저희는 vertices array속에 이 큐브를 구성하는 점들을 정의합니다. 그리고 이 array의 인덱스를 기반으로 triangular face를 만들기 위해 점들을 연결합니다. 예를 들어 new THREE.Face3(0, 2, 1)vertices의 0, 2, 1 인덱스에 있는 점들을 이용해서 traiangular face를 만듭니다.

Face를 만들기 위해 vertices들의 순서를 잘 다뤄야합니다. 왜냐하면 vertices의 순서를 어떻게 정의하느냐에 따라 Three.js는 그것이 front-facing face(a face facing the camera) 인지 back-facing face인지를 결정하기 때문입니다.

If you create the faces, you should use a clockwise sequence for front-facing faces and a counter-clockwise sequence if you want to create a back-facing face.

카메라 시점으로 face가 뒤를 돌아보고 있는지 혹은 앞을 보는지는 점의 순서에 따라 결정된다는 내용입니다.

잠시 언급하고 지나가자면
저희는 6개의 면을 가진 큐브를 만들기 위해 12개의 Triangular face를 사용했습니다. 이전 버젼의 ThreeJS에서는 triangular face대신에 quad face를 사용할 수 있었습니다. quad face는 면을 이루기위해 4개의 점을 이용합니다. quad가 좋은지는 triagngular 가 좋은지는 3D-modeling에서 아직 열띤 논쟁 중이라고 합니다.

Basically though, using quads is often preferred during modeling since they can be more easily enhanced and smoothed than triangles. For rendering and game engines though, working with triangles is often easier since every shape can be rendered very efficiently using triangles.

다시 본론으로 돌아가서 코드를 보겠습니다.

var geom = new THREE.Geometry(); 
geom.vertices = vertices; 
geom.faces = faces; 
geom.computeFaceNormals(); 

THREE.Geometry를 생성해서 vertices property 에 점들을 정의한 배열을 faces property 에는 face들을 할당해줍니다. 그리고 마지막 단계에는 computeFaceNormals()를 호출 해줍니다. 우리가 이 함수를 호출했을 때, Three.js는 각 face에 대한 normal vector를 결정합니다. 쉽게 말하면, 면에 대한 법선백터를 계산하는 것이죠. 이 정보(normal vector)는 scene에서의 다양한 light에 대해 면을 무슨 색으로 보여줄지를 결정하기 위해 사용됩니다.

위 geometry를 가지고 이전에 봤던 것처럼 mesh를 만들 수 있습니다. 간단한 예제로 결과를 한번 보겠습니다.

코드를 보기전에 앞서 설명을 하자면
위 예제 프로그램은 우측의 dat.GUI에서 점의 좌표를 바꾸면 그에 따라 렌더링되는 형태가 달라집니다. 위 예제에서 처럼 object가 변화에 따라 모양이 바뀌게 하려면 특정한 작업을 해줘야합니다. ThreeJS는 성능상의 이유로 mesh의 geometry는 lifetime동안에 변화되지않는다고 가정합니다. 그리고 대부분의 geometries와 사용사례에서 이 가정은 매우 타당합니다.

하지만 위와 예시처럼 geometry를 변경에 따라 반응하려면 저희는 아래와 같은 코드를 render loop에 추가해줘야합니다.

mesh.children.forEach(function(e) {  
  e.geometry.vertices = vertices;  
  e.geometry.verticesNeedUpdate = true;
  e.geometry.computeFaceNormals(); 
}); 
  • 우리는 변화된 vertices 배열을 렌더링할 geometry의 vertices에 할당해줍니다.
    하지만 저희는 face는 재할당해줄 필요없습니다.(이전에 점들과의 관계는 같기때문)
  • 그리고 우리가 vertices를 업데이트한 후, geometry에게 vertices가 업데이트되었다고 알려줘야합니다.verticesNeedUpdate를 true로 설정해줍으로써 이걸 수행할 수 있습니다.
  • 마지막으로 모델을 업데이트하기 위해 computeFaceNormals()를 호출함으로써 face를 재계산할 수 있습니다.

마지막으로 볼 geometry 기능은 clone() function입니다. 이름에서 암시할 수 있듯이 geometry의 복사본을 생성할 수도 있고 다른 Material을 가지는 다른Mesh를 생성하기 위해 사용할 수 도 있습니다.

바로 clone 코드를 보기전에 사용예제에서 material 설정이 복잡한 부분이 있어서 먼저 언급하고 넘어가겠습니다.

다중 Material 설정

 var materials = [      
   new THREE.MeshLambertMaterial({opacity: 0.6, color: 0x44ff44, transparent: true}),      
   new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})  ]; 

이번 예제에서는 single material을 사용하지않았습니다. 2개의 material이 들어간 배열을 material로 사용하고 있습니다. 이유는 투명한 green cube를 보여주는 것 이외에도 저희는 wireframe을 보여주기 원하기때문이죠. vertex와 face가 어디에 위치한지 명백히 보여주려고 이렇게 나타낸 것 같습니다.
Threejs 는 mesh를 만들 때 multiple materials를 사용하는 것을 가능하게 합니다.

다중 material을 적용하기 위해 SceneUtils.createMultiMaterialObject 함수를 사용할 수 있습니다. 아래 코드 처럼.

var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);

이 함수에서는 단지 하나의 Mesh Objects를 만들지 않습니다. 여기서는 저희가 정의한 각각의 Material에 대해 Mesh를 만들어냅니다. 그리고 group(THREE.Object3D)에 생성된 Mesh들을 집어넣습니다. 이 그룹은 우리가 Scene Object를 사용한 것처럼 동일한 방식으로 사용될 수 있습니다.

예를들어, 그룹의 자식 모두에게 cast shadow를 적용하고 싶으면 아래처럼하면 됩니다.

mesh.children.forEach(function(e){
  e.castShadow = true
}

자! 이제 다시 clone function으로 돌아가보겠습니다.
아래는 dat.GUI의 clone 버튼을 눌렀을 때 호출되는 콜백함수입니다.

this.clone = function(){
  var cloneGeom = mesh.children[0].geometry.clone()
  var materials = [    
    new THREE.MeshLambertMaterial( { opacity:0.6, color: 0xff44ff, transparent:true } ),
    new THREE.MeshBasicMaterial( { color: 0x000000, wireframe: true })
  ];
  var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeom, materials);
  mesh2.children.forEach(function(e) {e.castShadow=true});
  mesh2.translateX(5);
  mesh2.translateZ(5);
  mesh2.name="clone";
  scene.remove(scene.getObjectByName("clone"));
  scene.add(mesh2); 
}

장황하게 설명하자면.
geometry는 기존의 geometry는 clone해서 생성해냅니다. 그리고 새로운 material는 색깔(그린 -> 핑크)과 투명도가 달라졌습니다. 그리고 이전과 동일하게 2개의 Material을 가지는 MeshGroup하나를 mesh2에 할당합니다. 그림자 효과도 해당 MeshGroup의 자식요소 모두에게 적용하구요.
translateX, translateZ 는 처음보는 함수지만 이름을 통해 쉽게 예측가능합니다.
name 프로퍼티를 설정하고 기존의 Scene에 추가되있던 name이 clone이었던 object는 Scene에서 제거 해줍니다. 그리고 방금 생성했던 Mesh를 추가해주면 새로운 Object가 렌더링되는 것을 확인 가능합니다.

Tip)
wireFrame을 보여주기위해 저희는 두개의 Material을 가지는 Mesh를 생성하는
SceneUtils.createMultiMaterialObject 함수를 사용합니다. ThreeJS는 물론 대안적인 방법으로 THREE.WireFrameGeometry를 제공합니다. 사용법은 간단합니다.
geom라는 변수에 geometry가 있다고 가정하고 코드를 보자


var wireframe = new THREE.WireframeGeometry(geom);
var line = new THREE.LineSegements(wireframe);
scene.add(line)

Functions and attribute for meshes

Mesh 의 property는 매우 직관적이라 소개만하고 이 포스트를 마치겠습니다.

position

부모의 위치에 있어서 상대적으로 object가 어디에 있는지를 결정합니다. 대부분의 경우, 부모는 THREE.Scene 이거나 THREE.Object3D입니다.
position을 수정하는 방법을 3가지 정도로 나눌 수 있습니다.

  • position.x = 1 과 같이 개별적인 값을 수정하는 방법
  • position.set(10, 20, 30) x, y, z값을 한번에 수정하는 방법
  • position = new THREE.Vector3(10, 3, 1)
    position property는 THREE.Vector object이기 때문에 3번째 방법도 가능합니다.

rotation

이 속성을 가지고 객체의 축을 기준으로 회전을 설정할 수 있습니다. Threejs는 명시적으로 축에 대한 함수를 제공합니다. rotateX(), rotateY(), rotateZ()

scale

이 속성은 우리가 object를 축을 기준으로 scale하는 것을 가능하게 합니다.

translateX(amount)

translateY(amount)

translateZ(amount)

특정 축 방향으로 지정한 양만큼 이동시킬 수 있습니다.

tanslateOnAxis(axis, distance)

위 3개의 함수와 동일합니다. 축을 인자로넣어서 처리할 수 있습니다.

visible

만약 이 property를 false로 설정한다면 해당 Mesh는 Three.js에 의해 렌더링 되지 않습니다.

profile
Scratch, Under the hood, Initial version analysis

0개의 댓글