Convex Mesh Clipping Part 2

이전 파트 보기

시작하기전에

지난 시간 우리는 물체 하나를 자르기 위하여 이하의 것들을 다루었다.

  • 서덜랜드-호그만 알고리즘
  • Compute Intersection (교차점 연산)
    - Lerp (선형보간법)
    - Dot product (벡터의 내적)
    - Normal (법선 벡터)
  • Face Culling (CCW, …)

위의 개념들을 이용하여 삼각형 1개를 자르는데 성공하였다. 이제 3D로 응용해보자.

Mesh in triangles

‘Margaery — Low Poly’ - Mordi Levi / https://www.behance.net/mdlv

Mesh는 무늬(Texture)를 입힌 삼각형의 집합체이다. Part 1에서 했던 일련의 과정들을 반복해야 하는 것이다. 물론 차원이 늘어남에 따라 Z축과 평면의 개념이 들어간다.

정리하자면, 3D Mesh를 자른다는 것은 각각의 2D 삼각형들을 잘라서 나누고, 분리하여 재생성하는 반복작업이다.

2D → 3D

Part 1에서 삼각형과 이를 자르는 직선을 다루었다면, 이번에는 한차원 증가된 Mesh와 이를 자르는 평면을 다룰 것이다.

평면 (Plane)

실제 평면은 내 마음처럼 무한히 넓다 ㅎㅎ

2D에서 직선으로 잘랐다면, 3D에서는 평면으로 잘라야 한다. 서로 다른 세점 a, b, c을 정하면 평면을 정의 할수 있다. 또한 평면은 반드시 법선벡터 n을 가진다. n은 벡터의 외적(Cross product)의 성질을 이용하여 아래와 같이 쉽게 구할 수 있다.

Cross product

벡터의 외적의 성질을 통하여 평면에 속하는 임의의 두 벡터에 수직하는 n을 구하여 평면의 법선벡터로 만들었다.

Compute Intersection in 3D

빨간 세점은 평면을 이루는 세점, 파란 법선벡터, 그 안에 여러개의 삼각형으로 이루어진 정육면체 Mesh가 있다.

이번에도 우리가 구해야할 값은 삼각형과 평면의 교차점이다. 이번에도 Lerp와 내적을 이용하여 구할 것이다.

삼각형의 한점 a와 평면의 어느 점 p를 향하는 ap벡터와 단위벡터가(normalized)된 n을 내적하여 a와 평면사이의 거리d1를 구할 수 있다.

한번 더 강조하자면 한 차원 증가했어도 연산하는 모습은 거의 흡사하다. Part 1의 Lerp2D와 차이점이라고는 피연산자와 결과가 Vector3가 된것 뿐이다.

Unity에서의 구현

to Unity

로직을 다루었으니 이제 실제 구현에 들어가기에 앞서 구현환경인 Unity에서의 Mesh를 잠깐 알아보자. Mesh를 구성하는 요소 중 핵심은 다음과 같다.

public Vector3[] vertices;

public int[] triangles;

vertices는 복수의 vertex(정점)을 의미하며, triangles는 그 정점들을 엮는다.실시간으로 Mesh를 생성해 낼때는 vertices, triangles 두 값이 유효하게 입력되어야 한다.

고-급 삼각형

감이 오는가? triangles의 숫자는 vertices의 각 index 값이다. Culling을 염두하여(CCW 방향으로) 값을 배치하는 원리이다.

Unity.Mesh는 엔진에서 제공하며, Clipping 연산은 자체 작성한 WMesh에서 수행하고, 다시 Unity.Mesh로 export하는 구조로 작성하였다.

Unity에서는 평면Plane구조체가 이미 제공되어(링크) 있어서 관련된 연산을 따로 구현하지 않아도 된다. 아래와 같이 평면을 바로 만들 수 있다.

public Plane(Vector3 a, Vector3 b, Vector3 c);

평면과 특정 한 점과의 거리 역시 Plane에 구현되어 있다. 물론 Unity 엔지니어들의 실력을 믿지 못한다면 연산들은 직접 구현해도 된다.(굳이;;;)

public float GetDistanceToPoint(Vector3 inPt);

결과값인 float은 부호를 가진 거리 값으로 나오는데, 이를 통하여 삼각형의 한점이 평면의 앞에 있는지, 뒤에 있는지를 파악하여 삼각형의 어느 부분이 잘려 나갔는지를 알 수 있다.

Mathf.Epsilon 대신 0을 써도 상관은 없다. 보통 float같은 부동 소숫점을 비교할 때는 0에 근사한 Epsilon 값을 쓴다.

그리하여 삼각형을 이루는 각각의 세점과 평면과의 거리를 파악하면 잘려야 하는지, 잘리지 않는지를 파악할 수 있고, 잘려야 하는 삼각형이라면 교차점을 Lerp로 연산하여 구할 수 있다.

중간정리

호흡을 가다듬는 의미로 아래에 WMesh.cs의 일부분을 올리겠다. 제공하는 Plane으로 자르는 실질적인 작동부분이다. Intersect 함수는 위의 Lerp와 같다.

끝?

아직 안 끝났다. 중요한 부분이 남아있어 다음 Part 3에서 이어가도록 하겠다.

참조

Wikipedia — ‘평면’, ‘벡터곱
Unity Scripting API — ‘Mesh