참고 - 절차적 메시로 지형 만들기
평면을 하나 만들고 와이어프레임만 보면 10 x 10 크기의 평면인 것을 알 수 있다.
여기서는 N x M 크기의 평면을 절차적 메시로 만들어보자.
10 x 10 평면은 정점이 11 x 11 = 121개다.
따라서 width x height의 정점은 (width + 1) x (height + 1)개다.
그리고 삼각형의 개수는 10 x 10 평면인 경우 200개이므로, 필요한 triangles의 크기는 600개이다.
즉 triangles의 size는 width * height * 6가 된다.
vertices = new Vector3[(width + 1) * (height + 1)];
triangles = new int[width * height * 6];
이제 N x M 평면에서 시작 좌표를 찾아보자.
위의 그림대로라면 (sx, sy, sz)는 아래와 같다.
그리드의 크기(= cellSize)도 미리 고려하였다.
float sx = offset.x - width / 2.0f * cellSize;
float sy = offset.y;
float sz = offset.z + height / 2.0f * cellSize;
vertices 좌표는 다음과 같이 구한다.
int vIdx = 0;
for(int z = 0; z <= height; z++)
{
for(int x = 0; x <= width; x++)
{
float nx, nz;
nx = sx + x * cellSize;
nz = sz - z * cellSize;
vertices[vIdx++] = new Vector3(nx, sy, nz);
}
}
그림으로 보면 아래의 순서대로 vertices를 저장하였다.
이제 triangles를 채워보자. (width = N)
첫번째 Grid가 N개가 첫 줄에 모두 생성되고 이 때의 순서는 0 ~ N - 1이다.
그리고 다음 Grid는 N번째 사각형이다.
삼각형은 아래의 vertex를 시계방향으로 넣는다.
코드로 정리하면 다음과 같다.
점 1개가 정해지면(= vIdx) 나머지 3개의 점은 자동으로 정해진다.
다음 줄은 (width + 1)개 만큼 차이가 나기 때문에 width + 1이 더해진다.
vIdx = 0;
int tIdx = 0;
for (int z = 0; z < height; z++)
{
for (int x = 0; x < width; x++)
{
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + (width + 1);
triangles[tIdx++] = vIdx + (width + 1);
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + (width + 1) + 1;
vIdx++;
}
vIdx++;
}
빈 오브젝트에 Mesh Filter, Renderer, Default-Material, ProceduralPlane.cs를 추가하고 게임을 실행하자.
설정을 변경하면 아래와 같이 원하는 크기, offset, cellSize대로 평면을 만들 수 있다.
최종 코드는 다음과 같다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter))]
public class ProceduralPlane : MonoBehaviour
{
Mesh mesh;
Vector3[] vertices;
int[] triangles;
public float cellSize = 1;
public Vector3 offset;
public int width, height;
void OnValidate()
{
if (mesh == null) return;
if(offset != Vector3.zero || width > 0 || height > 0 || cellSize > 0)
{
setMeshData();
createProceduralMesh();
}
}
void Start()
{
mesh = GetComponent<MeshFilter>().mesh;
setMeshData();
createProceduralMesh();
}
void setMeshData()
{
vertices = new Vector3[(width + 1) * (height + 1)];
triangles = new int[width * height * 6];
float sx = offset.x - width / 2.0f * cellSize;
float sy = offset.y;
float sz = offset.z + height / 2.0f * cellSize;
int vIdx = 0;
for(int z = 0; z <= height; z++)
{
for(int x = 0; x <= width; x++)
{
float nx, nz;
nx = sx + x * cellSize;
nz = sz - z * cellSize;
vertices[vIdx++] = new Vector3(nx, sy, nz);
}
}
vIdx = 0;
int tIdx = 0;
for (int z = 0; z < height; z++)
{
for (int x = 0; x < width; x++)
{
triangles[tIdx++] = vIdx;
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + (width + 1);
triangles[tIdx++] = vIdx + (width + 1);
triangles[tIdx++] = vIdx + 1;
triangles[tIdx++] = vIdx + (width + 1) + 1;
vIdx++;
}
vIdx++;
}
}
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 프리미엄 학습:
댓글