절차적 메시로 정다각뿔, 원뿔 만들기를 확장하여 정다각기둥, 원기둥을 만들어보자.
높이를 설정할 height 변수를 public으로 선언한다.
여기서도 높이의 절반이 오브젝트의 기준점이 된다.
public float height = 1.0f;
vertices는 정다각형이 하나 더 추가되므로 polygon + 1개가 추가되고,
정다각형 사이의 사각형이 polygon 만큼 추가되므로 정점의 개수 4를 곱한 값을 더한다.
vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];
위와 마찬가지로 triangles는 아래와 같이 크기가 변한다.
triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];
첫번째 vertices와 triangles는 밑면을 만든다.
triangles의 방향을 반시계가 되도록 변경하였다.
그리고 height가 y에 들어가도록 좌표를 수정하였다.
/* -------------------- 밑면 -------------------- */
vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];
vertices[0] = new Vector3(0, -height / 2.0f, 0) + offset;
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices[i]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
}
triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];
for (int i = 0; i < polygon - 1; i++)
{
triangles[i * 3] = 0;
triangles[i * 3 + 1] = i + 2;
triangles[i * 3 + 2] = i + 1;
}
triangles[3 * polygon - 3] = 0;
triangles[3 * polygon - 2] = 1;
triangles[3 * polygon - 1] = polygon;
윗면은 아래와 같다.
triangles의 방향이 밑면과 다르다는 것을 참고하자.
/* -------------------- 윗면 -------------------- */
int vIdx = polygon + 1;
vertices[vIdx++] = new Vector3(0, height / 2.0f, 0) + offset;
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx++]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
}
int tIdx = 3 * polygon;
for (int i = 0; i < polygon - 1; i++)
{
triangles[tIdx++] = polygon + 1;
triangles[tIdx++] = i + 1 + polygon + 1;
triangles[tIdx++] = i + 2 + polygon + 1;
}
triangles[tIdx++] = polygon + 1;
triangles[tIdx++] = polygon + polygon + 1;
triangles[tIdx++] = 1 + polygon + 1;
옆면은 vIdx를 추가하면서 triangles을 같이 추가하는 것이 편하다.
vertices는 위의 코드를 지나가면 2 * N + 2개의 원소를 가진다.
마찬가지로 triangles는 6 * N개를 가진다.
angle과 nextAngle을 기준으로 옆면의 사각형을 하나씩 그려간다.
/* -------------------- 옆면 -------------------- */
vIdx = 2 * polygon + 2;
tIdx = 6 * polygon;
for (int i = 1; i <= polygon - 1; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
float nextAngle = -(i + 1) * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 1]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 2]
= new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
vertices[vIdx + 3]
= new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 3;
triangles[tIdx++] = vIdx + 1;
vIdx += 4;
}
{
float angle = -polygon * (Mathf.PI * 2.0f) / polygon;
float nextAngle = -(0 + 1) * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 1]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 2]
= new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
vertices[vIdx + 3]
= new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 3;
triangles[tIdx++] = vIdx + 1;
}
게임을 실행하면 정다각기둥을 만들 수 있다.
아래는 콜라이더만 남겨서 mesh가 그려진 모습이다.
전체 코드는 다음과 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralRegularPrism : MonoBehaviour
{
public int polygon = 3;
public float size = 1.0f;
public float height = 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 || polygon >= 3 || height > 0)
{
setMeshData(size, polygon);
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
setMeshData(size, polygon);
createProceduralMesh();
}
void setMeshData(float size, int polygon)
{
/* -------------------- 밑면 -------------------- */
vertices = new Vector3[(polygon + 1) + (polygon + 1) + (polygon * 4)];
vertices[0] = new Vector3(0, -height / 2.0f, 0) + offset;
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices[i]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
}
triangles = new int[(3 * polygon) + (3 * polygon) + (6 * polygon)];
for (int i = 0; i < polygon - 1; i++)
{
triangles[i * 3] = 0;
triangles[i * 3 + 1] = i + 2;
triangles[i * 3 + 2] = i + 1;
}
triangles[3 * polygon - 3] = 0;
triangles[3 * polygon - 2] = 1;
triangles[3 * polygon - 1] = polygon;
/* -------------------- 윗면 -------------------- */
int vIdx = polygon + 1;
vertices[vIdx++] = new Vector3(0, height / 2.0f, 0) + offset;
for (int i = 1; i <= polygon; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx++]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
}
int tIdx = 3 * polygon;
for (int i = 0; i < polygon - 1; i++)
{
triangles[tIdx++] = polygon + 1;
triangles[tIdx++] = i + 1 + polygon + 1;
triangles[tIdx++] = i + 2 + polygon + 1;
}
triangles[tIdx++] = polygon + 1;
triangles[tIdx++] = polygon + polygon + 1;
triangles[tIdx++] = 1 + polygon + 1;
/* -------------------- 옆면 -------------------- */
vIdx = 2 * polygon + 2;
tIdx = 6 * polygon;
for (int i = 1; i <= polygon - 1; i++)
{
float angle = -i * (Mathf.PI * 2.0f) / polygon;
float nextAngle = -(i + 1) * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 1]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 2]
= new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
vertices[vIdx + 3]
= new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 3;
triangles[tIdx++] = vIdx + 1;
vIdx += 4;
}
{
float angle = -polygon * (Mathf.PI * 2.0f) / polygon;
float nextAngle = -(0 + 1) * (Mathf.PI * 2.0f) / polygon;
vertices[vIdx]
= new Vector3(Mathf.Cos(angle) * size, -height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 1]
= new Vector3(Mathf.Cos(angle) * size, height / 2.0f, Mathf.Sin(angle) * size) + offset;
vertices[vIdx + 2]
= new Vector3(Mathf.Cos(nextAngle) * size, -height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
vertices[vIdx + 3]
= new Vector3(Mathf.Cos(nextAngle) * size, height / 2.0f, Mathf.Sin(nextAngle) * size) + offset;
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + 2;
triangles[tIdx++] = vIdx + 3;
triangles[tIdx++] = vIdx + 1;
}
}
void createProceduralMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
Destroy(this.GetComponent<MeshCollider>());
this.gameObject.AddComponent<MeshCollider>();
}
}
참고로 polygon = 20, size(반지름) = 0.5, height = 2로 설정하면 유니티의 실린더가 된다.
유니티의 실린더의 경우 Capsule Collider를 쓰기 때문에 Mesh Renderer를 끄면 차이를 볼 수 있다.
참고
- 위의 경우 polygon이 많을 때 불필요한 정점이 상대적으로 많아진다.
- 따라서 정점을 필요한 만큼만 만들고 triangles로 배치한다.
Unity Plus:
Unity Pro:
Unity 프리미엄 학습:
댓글