Unity 구(Sphere)를 직접 만들어보자 🏐
Unity에서 기본적으로 제공하는 3D 오브젝트중 Sphere를 만들었을때 WireFrame을 보면 이런 삼각형들로 이루어져있다.
사실 컴퓨터로 표현되는 3D모델들은 모두 폴리곤이라는 삼각형들이 모여 하나의 물체를 이루기 때문에 구 또한 삼각형들의 집함으로 이루어져있다는걸 알 수 있다. 이 폴리곤들이 촘촘하면 촘촘 할 수록 더 매끈한 구가 되겠지만 그만큼 연산량은 많아질것이다.
넓은의미로 모두 구 이다.
구를 만들기 위해 차근차근 [ 평면 -> 정육면체 -> 구체 ] 순으로 만들어 볼 생각이다.
평면 만들기
평면을 만들기 위해 생성자로 3개의 인자를 전달받을 껀데, 정점배열들을 이용해서 면을 채워줄 mesh, 정점들의 갯수를 결정하는 resolution, 면이 중심으로부터 생성될 방향을 결정할 localUp을 정의해준다.
public class MeshFace
{
private Mesh mesh;
private int resolution;
private Vector3 localUp;
private Vector3 axisA;
private Vector3 axisB;
public MeshFace(Mesh mesh, int resolution, Vector3 localUp)
{
this.mesh = mesh;
this.resolution = resolution;
this.localUp = localUp;
axisA = new Vector3(localUp.y, localUp.z, localUp.x);
axisB = Vector3.Cross(localUp, axisA);
}
}
axisA와 axisB는 localUp벡터를 y축방향 벡터라고 생각한다면 각각 x, z 벡터라 할 수 있겠다.
화면에 직접 보이도록하기 위해서는 Mesh라는 컴포넌트를 이용해야하는데, Mesh에게 정점의 위치정보 mesh.vertices와 정점을 그리는 순서를 담은 mesh.triangles를 정해주면 그에따라 면을 그려주게 된다. https://docs.unity3d.com/kr/530/ScriptReference/Mesh.html
mesh.vertices
mesh.triangles
그렇다면 이제 만들어야할것은 vertices와 triangles에 들어가야할 배열들을 계산해줘야할듯 싶다.
먼저 vertices, 정점의 갯수는 resolution의 값에 따라 일괄적으로 줄여주거나 늘려줄것이다. resolution = 5 일경우 가로 5개 x 세로 5개 총 25개의 정점을 만들도록 할 것이다.
Vector3[] vertices = new Vector3[resolution * resolution];
다음 triangles,
resolution = 5 일경우 (5 - 1)개의 가로사각형 (5-1)개의 세로사각형 = 16개의 사각형을 만들수 있을것이고, 사각형1개는 2개의 삼각형으로 이루어져있고, 이 삼각형은 3개의 정점으로 이루어져 있다.
int[] triangles = new int[(resolution - 1) * (resolution - 1) * 2 * 3];
이제 vertices와 triangles의 값들을 채워보자
public void CreateMesh()
{
Vector3[] vertices = new Vector3[resolution * resolution];
int[] triangles = new int[(resolution - 1) * (resolution - 1) * 2 * 3];
int triIndex = 0;
for (int y = 0; y < resolution; y++)
{
for (int x = 0; x < resolution; x++)
{
int vertexIndex = x + y * resolution;
Vector2 percent = new Vector2(x, y) / (resolution - 1); // 0~1로 노멀라이즈
Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
Vector3 pointOnUnitSphere = pointOnUnitCube.normalized;
vertices[vertexIndex] = pointOnUnitCube;
if (x != resolution - 1 && y != resolution - 1)
{
triangles[triIndex] = vertexIndex;
triangles[triIndex + 1] = vertexIndex + resolution + 1;
triangles[triIndex + 2] = vertexIndex + resolution;
triangles[triIndex + 3] = vertexIndex;
triangles[triIndex + 4] = vertexIndex + 1;
triangles[triIndex + 5] = vertexIndex + resolution + 1;
triIndex += 6;
}
}
}
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.RecalculateNormals();
}
코드를 하나하나 봐보자면
int vertexIndex = x + y * resolution;
Vector2 percent = new Vector2(x, y) / (resolution - 1); // 0~1로 노멀라이즈
Vector3 pointOnUnitCube = (percent.x - 0.5f) * 2 * axisA + (percent.y - 0.5f) * 2 * axisB + localUp;
resolution = 5라고 가정하면,
percent : x, y가 (0,0) 일때부터 (4,4) 까지의 값을 가질텐데 그 값을 4로 나눠주기 때문에 (0,0) ~ (1,1) 의 값으로 노멀라이즈 해주게된다.
pointOnUnitCube : x랑 y의 값을 - 0.5 만큼 뺀 값을 2만큼 곱해주어, (0,0) ~ (1,1)의 범위의 값들이 (-1,-1) ~ (1,1)의 값으로 바뀔것이고 이에 localUp 벡터만큼 더해주면 다음과같은 평면의 벡터들을 구할 수 있을것이다.
triangles[ ] : 아래 그림의 각 숫자가 vertexIndex인데 삼각형의 정점을 시계방향순으로 써보면 vertexIndex, vertexIndex + 1, vertexIndex + resolution 이라는 하단 삼각형과 vertexIndex, vertexIndex + 1, vertexIndex + resolution + 1 이라는 상단 삼각형의 순서를 구할 수 있다.
이렇게 vertices와 triangles를 구했으면 mesh에 넣어주면된다.
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles
mesh.RecalculateNormals();
테스트를 해보자
using UnityEngine;
public class Sphere : MonoBehaviour
{
[Range(2, 256)]
public int resolution = 5;
private void OnValidate()
{
TEST();
}
void TEST()
{
GameObject meshObj = new GameObject("mesh");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
MeshFilter meshFilter = meshObj.AddComponent<MeshFilter>();
meshFilter.sharedMesh = new Mesh();
MeshFace face = new MeshFace(meshFilter.sharedMesh, resolution, Vector3.up);
face.CreateMesh();
}
}
GOOD!
정육면체 만들기
평면을 만들었다면 정육면체를 만드는건 너무나 쉽다. 평면을 6개만 만들면 되니까.
Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
for (int i = 0; i < 6; i++)
{
if (meshFilters[i] == null)
{
GameObject meshObj = new GameObject("mesh");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
meshFilters[i] = meshObj.AddComponent<MeshFilter>();
meshFilters[i].sharedMesh = new Mesh();
}
terrainFaces[i] = new MeshFace(meshFilters[i].sharedMesh, resolution, directions[i]);
}
EASY~
마지막으로 구체 만들기
고등학교 수학시간에 구를 배울때 구의 정의를 생각해보면 3차원에서 한 중점으로부터 거리가 같은 점들의 집합으로 배웠었다. 그렇다면 다시 돌아가서 아까 평면을 만들었을때를 봐보자
저 평면을 이루는 벡터는 중점으로부터 거리가 정점마다 모두 다를 것이다. 이 벡터들을 방향은 같고 크기만 동일하게 노멀라이즈 해준다면
마치 이런모양으로 바뀔것이다.
Vector3 pointOnUnitCube = localUp + (percent.x - .5f) * 2 * axisA + (percent.y - .5f) * 2 * axisB;
Vector3 pointOnUnitSphere = pointOnUnitCube.normalized; // 노멀라이즈
vertices[vertexIndex] = pointOnUnitSphere; // 노멀라이즈한 값을 넣어줌
GREAT!
Sphere.cs의 코드를 좀더 깔끔하게 다듬어 보면
using UnityEngine;
public class Sphere : MonoBehaviour
{
[Range(2, 256)]
public int resolution = 10;
[SerializeField, HideInInspector]
MeshFilter[] meshFilters;
MeshFace[] terrainFaces;
private void OnValidate()
{
Initialize();
GenerateMesh();
}
void Initialize()
{
if (meshFilters == null || meshFilters.Length == 0)
{
meshFilters = new MeshFilter[6];
}
terrainFaces = new MeshFace[6];
Vector3[] directions = { Vector3.up, Vector3.down, Vector3.left, Vector3.right, Vector3.forward, Vector3.back };
for (int i = 0; i < 6; i++)
{
if (meshFilters[i] == null)
{
GameObject meshObj = new GameObject("mesh");
meshObj.transform.parent = transform;
meshObj.AddComponent<MeshRenderer>().sharedMaterial = new Material(Shader.Find("Standard"));
meshFilters[i] = meshObj.AddComponent<MeshFilter>();
meshFilters[i].sharedMesh = new Mesh();
}
terrainFaces[i] = new MeshFace(meshFilters[i].sharedMesh, resolution, directions[i]);
}
}
void GenerateMesh()
{
foreach (MeshFace face in terrainFaces)
{
face.CreateMesh();
}
}
}
Author And Source
이 문제에 관하여(Unity 구(Sphere)를 직접 만들어보자 🏐), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dev_juicy/Unity-직접-구Sphere를-만들어보자저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)