유니티 도형 자르기 - yuniti dohyeong jaleugi

Mesh Cut이란 이름 그대로 메쉬를 자르는 기능입니다.

보통 게임에선 칼로 물건을 잘랐을 때 해당 물건이 베인 곳을 기준으로 두동강 난 형태를 연출하기 위해서 사용합니다.

이 연출을 활용하여 유명한 게임으로는 후르츠 닌자가 있습니다.

그리고 VR에는 비트 세이버가 있죠.

실시간으로 메쉬를 자르고, 나누고, 자른 부분을 채워야 하기에 꽤나 고비용임에도 불구하고 유저의 행동이 그대로 결과에 반영된다는 이 기능 연출은 매력적이지 않을 수 없습니다.

그래서 저도 한 번 해보았습니다.

Mesh Cut의 코드는 공개 되어 있는 것을 사용했습니다.

구글에 Mesh Cut이라고 검색만 해도 나오는데 //gist.github.com/alexcmd/f4c19a24e8f48bb2aadf 링크 걸어두겠습니다.

사용 방법은 간단합니다.

GameObject[] gameObjects = MeshCut.Cut(gameObject, transform.position, Vector3.down, capMaterial);

MeshCut.Cut은 public static 함수라 어디서든 사용이 가능합니다.

 (자를 오브젝트, 자를 중심점, 잘린 오브젝트의 방향, 잘린 영역을 메꿀 Material) 을 다 채워주면 간단하게 오브젝트가 잘리고 원본 오브젝트의 속성을 지닌 잘린 오브젝트가 gameObjects[0]에, 잘려서 새롭게 생긴 오브젝트가 gameObjects[1]에 생깁니다.

예를 들어 위 스크립트 한 줄을 실행한 기본 Cube의 경우

이런 식으로 두동강 나고 오브젝트명은 각각 위가 leftSide, 아래가 rightSide란 이름으로 변경되어 생겨있습니다.

방향에 Vector3.down을 주었기 때문에 아래쪽이 잘려서 새로 생성 된 오브젝트가 됩니다.

이건 MeshCut 스크립트의 기본 설정이라 본인이 원하는데로 변경하시면 됩니다.

기본 설정을 따라가게 되면 leftSide가 원본의 컴포넌트들을 그대로 가져가게 되고 rightSide는 메쉬만을 가진 오브젝트가 됩니다.

여기서 주의할 점이 만약 기본설정으로 놔둘 시 leftSide의 콜라이더가 잘리기 전 모양을 유지하고 있다는 점입니다.

잘린 파편에도 물리 효과를 정상적으로 주고 싶다면 설정을 좀 바꿔 기존 오브젝트를 그대로 이어받는게 아닌 leftSide도 rightSide처럼 새롭게 생성 후 컴포넌트들을 달아주시면 됩니다.

현재 이 스크립트를 활용하면 잘리는 면의 크기는 무한이 됩니다.

자르다 만다거나 하는 행위는 불가능 하고 점의 위치가 꼭 오브젝트 내에 위치할 필요는 없습니다.

이걸 VR에 적용시켜 비트 세이버 마냥 검으로 자르는 행위가 하고 싶다면 간단하게 흉내낼 수 있습니다.

백터의 외적은 새로운 수직백터가 나온다는 것을 이용한 방법인데 충돌하는 순간 이전 프레임의 검의 위치와 비교해서 외적을 구하면 현재 검이 그대로 직선으로 관통했다는 가정 된 결과물이 나오게 됩니다.

유니티에선 Vector3.Cross라는 아주 편한 함수가 있어서 외적을 쉽게 구할 수 있습니다.

리턴되는 두 개의 오브젝트에 바로 Rigidbody 컴포넌트를 추가시켜주면 꽤 리얼하게 두 조각이 떨어지는 모습을 볼 수 있습니다.

만들어본 예시

베는 속도가 빠를수록 조각이 더 멀리 날아갑니다.

VR로 하면 실제로 움직일 수도 있어서 비트세이버같은 느낌도 납니다.

이번 글에서는 작업중이던 도형 자르기에 대해서 얘기해보자 합니다.
제가 구현한 사항에서는 볼록한 도형만 처리됩니다.

자르는 방법에는 2가지로 나눠보도록 하겠습니다.

1. Ray를 이용한 자르기 방법.

2. Collider를 이용한 자르기 방법.

위 두가지 방법은 크게 다른점은 없습니다. 단지, 실제 칼날을 쓸것이냐, 가상의 칼날을 쓸것이냐에 따른 차이가 존재할 뿐입니다.

1. Ray를 이용
-> Ray를 이용하는 것은 의외로 간단합니다. 먼저 물체가 잘리는 조건을 정하고 진행하도록 하겠습니다. 
물체가 잘리는 조건: 물체를 완벽하게 그어야 된다.(물체 중간에서 멈추면 물체가 잘리지 않는다).
위 조건대로 방법을 보자면 다음과 같습니다.
마우스를 누른다(물체에 맞았나? Y: 물체를 저장하고, 해당 위치를 저장 N: 스킵)
마우스를 뗀다(아직도 Ray가 저장된 물체 위에 있나? Y: 안잘린다 N: 자르는 함수 실행)

2. Collider를 이용
-> 이것이 현재 사용되는 방법입니다. 어떤 물체에 rigidbody와 collider를 붙이고, OnCollisionEnter(Collision col)에서 col.contacts를 통해 충돌 위치들을 저장합니다
마찬가지로 OnCollisionExit(Collision col) col.contacts를 통해 빠져나갈때의 충돌 위치를 저장합니다.
이러면 어느위치에서부터 어느 위치까지 칼을 지나게했는지 알 수 있습니다.
이 두 점을 바탕으로 물체를 자르는 것입니다.

실제 물체 자르기 동작

한 오브젝트는 다음과 같이 이루어져 있습니다. Vertices, Triangles, Normals, Tangents, uv 등등 몇가지 더 추가요소가 있습니다만, 현재는 Vertex, triangle, normal
 만이 조작되는 상태입니다.
Vertices는 점을 의미합니다.
Triangles는 int형 배열인데, 어떤 vertex가 한 삼각형을 이루는지 알려주는 배열입니다.
3단위로 끊어 읽어야 합니다. 또한 사용자가 바라보는 방향에서  시계방향으로 값을 넣어줘야 보입니다. 
Normal은 vertices와 1:1매칭이되는 vector3형 배열입니다. 

위에서 한 동작은 자르는 기준을 마련하는 행동이었습니다. 따라서 이제는 어떻게 실제로 자르는지를 지정해줘야 하는데, 이는 Unity에서 제공하는 Plane을 사용하여 구현하였습니다. 물체를 지나는 점의 시작과 끝, 추가로 정한 (검의 위치 혹은 플레이어 위치)를 토대로 평면(P)을 하나 생성합니다.

해당 물체의 triangle들을 순차적으로 돌면서 그에 매치되는 점들을 가져와 3점이 모두 P의 양(+)/음(-)의 방향에 있다면 Plane 기준으로 Mesh A(+),B(-)에 저장합니다.
만일 모두 음/양의 방향이 아니라면 그 삼각형은 평면에 의해 잘리는 것으로 판단되어, 동일한 방향의 점 S1,S2 와 한개의 다른 점 O1 를 잇는 선을 그어 평면과의 교점을 C1,C2라고 잡습니다.  그러면 다음과 같은 생각을 할 수 있습니다.

1. S1,S2,C1,C2를 바탕으로 새로운 삼각형 2개를 구성하기
2. O1,C1,C2를 바탕으로 새로운 삼각형 1개를 구성하기

위의 2가지를 구현하면, 깔끔하게 물체를 자를 수 있게 됩니다.
위의 2가지 점에서 상세하게 고려해야 할 것은

1. 시계방향의 판정
2. (S1,S2), (O1)이 각각 A,B 어느 Mesh에 속하게 되는가.

2번은 1번에 비해서 상당히 쉬운 문제입니다.
제일 큰 문제는 시계방향의 판정이라고 볼 수 있습니다.
시계방향의 판정은 해당 점들의 평균값을 구하고, 한 vertex의 normal값을 가져와 그에 따라 시계방향으로 정렬한 뒤, vertices와 triangles를 구성해주는 방법이 있습니다.

단, 위의 방법은 재정렬을 지속적으로 하는 것이므로 약간의 성능 부하의 가능성이 존재합니다. 따라서, 다음과 같이 생각해 봅시다.

1. S1과 P1, O1은 한 직선 위에 존재한다.
2. S2와 P2, O1은 한 직선 위에 존재한다.
3. normal 값을 알 수 있다.

위의 세 조건을 통해, 재정렬을 하지 않아도  충분히 if else 문으로만 처리를 가능합니다.
(S1,S2,C1,C2)로 이루어지는 사각형의 경우 두 삼각형은 항상 다음과 같은 세트로 존재합니다 (S1,S2,C1), (S2,C1,C2). 이해가 안가신다면 그려보시는게 편합니다.
여튼, 각 세트를 3점을 토대로 plane을 생성합니다. new plane(S1,S2,C1) 하고, 생성한 plane의 normal값과 저희가 알고있는 normal 값의 dot product가 +라면 제대로 생성된 것이고, 아니라면 역순으로 지정해주시면 됩니다.

이러한 방법을 통해 깔끔하게 물체를 자르 실 수 있습니다.

면 매꾸기
- 면을 매꾸는 방법은 볼록 물체에선 쉽습니다. 
생각해야 할 것은 다음과 같습니다.
1. 새로생긴 점들
2. 새로생길 면의 방향
3. normal값 설정해주기.
위의 3가지를 처리하면 물체를 배는 면이 매꿔지는 것을 처리할 수 있습니다.

Toplist

최신 우편물

태그