절차적 메시를 이용하여 정사면체를 만들어보자.
길이가 a인 정삼각형이 있다고 가정하자.
정삼각형 넓이 공식은 다음과 같다.
정삼각형의 중심은 다음과 같다. (링크 참고)
아래의 그림을 참고하자.
그리고 정사면체의 높이는 다음과 같다.
정사면체의 부피는 정삼각형의 넓이(S)와 정사면체의 높이(H)의 곱을 3으로 나누면 된다.
따라서, 정사면체의 부피 V는 다음과 같다.
정사면체의 중심을 C라고 할 때, 이 중심은 모든 정사면체에서 높이가 동일하다.
따라서 4개의 사면체의 부피와 1개의 정사면체의 부피는 값이 같으므로 아래의 식이 성립한다.
따라서 정사면체의 중심은 다음과 같다.
위의 식과 절차적 메시로 삼각형 만들기를 바탕으로 ProceduralTetrahedron.cs를 만들어보자.
코드에서 사용할 정점 4개는 아래와 같이 정의한다.
위의 좌표를 바탕으로 정점을 정의하고 triangles에 순서대로 값을 넣는다.
void setMeshData(float size)
{
float g = Mathf.Sqrt(3.0f) / 6.0f * size; // 정삼각형의 중점
float h = Mathf.Sqrt(6.0f) / 3.0f * size; // 정사면체의 높이
float c = Mathf.Sqrt(6.0f) / 12.0f * size; // 정사면체의 중점
Vector3 d0 = new Vector3(0, h - c, 0) + offset;
Vector3 d1 = new Vector3(-0.5f * size, -c, -g) + offset;
Vector3 d2 = new Vector3(0, -c, Mathf.Sqrt(3.0f) / 2.0f * size - g) + offset;
Vector3 d3 = new Vector3(0.5f * size, -c, -g) + offset;
vertices = new Vector3[] { d0, d1, d2, d3 };
triangles = new int[] { 0, 3, 1, 0, 2, 3, 0, 1, 2, 1, 3, 2 };
}
빈 오브젝트에 스크립트와 Mesh Filter, Renderer, 머테리얼을 설정하자.
사면체가 만들어졌지만 면이 뿌옇게 변하였다.
현재 정사면체의 정점은 4개이고, 정점 하나는 면 3개에 대한 노멀을 처리해야 한다.
그런데 하나의 정점은 하나의 정보만 가지므로, 정사면체의 정점은 12개여야 한다. (실제 큐브의 정점의 개수도 24개다.)
따라서 아래와 같이 코드를 수정한다.
void setMeshData(float size)
{
float g = Mathf.Sqrt(3.0f) / 6.0f * size; // 정삼각형의 중점
float h = Mathf.Sqrt(6.0f) / 3.0f * size; // 정사면체의 높이
float c = Mathf.Sqrt(6.0f) / 12.0f * size; // 정사면체의 중점
Vector3 d0 = new Vector3(0, h - c, 0) + offset;
Vector3 d1 = new Vector3(-0.5f * size, -c, -g) + offset;
Vector3 d2 = new Vector3(0, -c, Mathf.Sqrt(3.0f) / 2.0f * size - g) + offset;
Vector3 d3 = new Vector3(0.5f * size, -c, -g) + offset;
vertices = new Vector3[] { d0, d1, d2, d0, d2, d3, d0, d3, d1, d1, d3, d2 };
triangles = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
}
완성된 정사면체는 다음과 같다.
정점이 4개인 정사면체가 흐릿해지는 현상이 사라졌다.
콜라이더만 보면 아래와 같다.
최종 코드는 다음과 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralTetrahedron : MonoBehaviour
{
public float size = 1.0f;
public Vector3 offset = new Vector3(0, 0, 0);
Mesh mesh;
Vector3[] vertices;
int[] triangles;
void OnValidate()
{
if (mesh == null) return;
if (size > 0 || offset.magnitude > 0)
{
setMeshData(size);
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
setMeshData(size);
createProceduralMesh();
}
void setMeshData(float size)
{
float g = Mathf.Sqrt(3.0f) / 6.0f * size; // 정삼각형의 중점
float h = Mathf.Sqrt(6.0f) / 3.0f * size; // 정사면체의 높이
float c = Mathf.Sqrt(6.0f) / 12.0f * size; // 정사면체의 중점
Vector3 d0 = new Vector3(0, h - c, 0) + offset;
Vector3 d1 = new Vector3(-0.5f * size, -c, -g) + offset;
Vector3 d2 = new Vector3(0, -c, Mathf.Sqrt(3.0f) / 2.0f * size - g) + offset;
Vector3 d3 = new Vector3(0.5f * size, -c, -g) + offset;
vertices = new Vector3[] { d0, d1, d2, d0, d2, d3, d0, d3, d1, d1, d3, d2 };
triangles = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
}
void createProceduralMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
Destroy(this.GetComponent<MeshCollider>());
this.gameObject.AddComponent<MeshCollider>();
}
}
Unity Plus:
Unity Pro:
Unity 프리미엄 학습:
댓글