카테고리 없음

Mesh 만들기

HulstleHustle 2022. 2. 15. 16:21

오늘은 메쉬mesh를 스크립트로 제어및 생성해볼것이다
기존에 내가 알던방식과
이번에 새로 알게된 재밌는 다른 방식 해서 총 2가지의 방식이다

 

유니티로 하였다 모두

첫번째로 기존에 내가 알면서 해왔던 방식

 

게임씬에 빈 게임오브젝트를 만들고

[RequireComponent(typeof(MeshFilter), typeof(MeshRenderer))]
public class SimpleProceduralMesh : MonoBehaviour
{
    private void OnEnable()
    {
        Mesh mesh = new Mesh { name = "Procedural Mesh" };
        
1.정점의 위치정보
        mesh.vertices = new Vector3[]
            {
                Vector3.zero, Vector3.right, Vector3.up
                ,new Vector3(1f,1f)
사각형 모양을 만든다
            };

2. 정점배열의 인덱스번호로 , 삼각형그리기
        mesh.triangles = new int[]
            {
                // 시계방향으로 정점을 읽을시, 유니티 z축 방향으로 그려짐
                0,2,1,1,2,3
            };

3. 정점의 노말방향, 즉 면이 바라볼 정면의 방향
        mesh.normals = new Vector3[]
            { 
              // z축 방향으로 바라볼시 메쉬가 보이기에, 노말(표면이 나아가는 방향은)은 back으로 맞출수있음
                Vector3.back,Vector3.back,Vector3.back,
                Vector3.back
            };
4. 탄젠트는 사실 아직도 완벽히 이해못하였다, 셰이더를 할때도 거의 사용을 아직 안해보았다
        mesh.tangents = new Vector4[]
            {
                new Vector4(1f,0f,0f,-1f),
                new Vector4(1f,0f,0f,-1f),
                new Vector4(1f,0f,0f,-1f),
                new Vector4(1f,0f,0f,-1f),
            };
5. uv이다, 정점에게 텍스처의 어디부터 어디까지를 보일지, 유니티와 OPENGL은 왼쪽 하단부터가 0,0으로 시작한다고 한다
        mesh.uv = new Vector2[]
        // uv 의 xy를 정해서 출력가능
            { Vector2.zero, Vector2.right, Vector2.up,
               Vector2.one

              };

        6. 마지막으로 메쉬필터의 메쉬로 만들어둔 메쉬 를 넣어주기
        GetComponent<MeshFilter>().mesh = mesh;
    }
}

 

위까지는 내가 알고있던 방식이다, 
그러나 재밋는 글을 보았고 , 출처를 남길수잇는지 좀 더 확인후 남겨보도록하겠다
패트론에서 1달러로 후원자가 되며 다양한 셰이더관련 튜토리얼 글을 읽을수있었다
https://catlikecoding.com/unity/tutorials/procedural-meshes/square-grid/

 

두번쨰로 이번에 알게된 새로운방식

 

using Unity.Mathematics;
using System.Runtime.InteropServices;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;

public class AdvancedSingleStreamProceduralMesh : MonoBehaviour // 버전 2020부터
// 유니티에서 실행하였음
1. 버텍스가 가질 요소를 정의한다, 구조체로

{
[StructLayout(LayoutKind.Sequential)] // using System.Runtime.InteropServeices; 필수!

struct Vertex
{  // float -> half -> fixed 순으로 바이트가 적어진다,   float 32비트  half 16비트  fixed 8비트 셰이더 데이터 개념으로        알고있다

 public float3 position, normal;   포지션과 노말

 public half4 tangent;         탄젠트

 public half2 texCoord0;     텍스처

}
2. 활성화상태에서 바로 재생
private on Enable()

{

   int vertexAttributeCount = 4;  속성의 갯수는 4개

   int vertexCount = 4; 

   int triangleIndexCount = 6;    정점4개와 이어서 만들어줄 삼각형에 드는 정점 6개(2개는 중복)


   메쉬데이터배열은 2020버전부터 쓸수있는듯하였다, 2018.LTS에서 사용하려니 인식을 못하여 버전업 하였다
   말그대로 메쉬가 가질 데이터배열을 의미하는데 , 추가로 내가 직접 작성하여 의미가 많아질듯하다,       

   WritableMeshData(인자)에서   에디터와 씬뷰에서 만들어질 메쉬의 갯수를 의미한다
   Mesh.MeshDataArray meshDataArray = Mesh.AllocateToWritableMeshData(1);
   Mesh.MeshData meshData = meshDataArray[0];
  

  버텍스 요소들을 구조체로 정의하고 그걸 직접 버텍스속성에 적용하려한다 Attribute : 속성
  var vertexAttributes = new NativeArray<VertexAttributeDescriptor>
            (vertexAttributeCount, Allocator.Temp , NativeArrayOptions.UninitializedMemory); 

 

 애초에 정점(버텍스)의 개수를 4개로 정하였기에 자연스레 배열의 크기도 4개 (0으로 시작해 3까지의 길이)
 순서대로 하면  [0] 포지션 float3,  [1] 노말 flaot3, [2] 탄젠트 float4, [4] 텍스처 flaot2
 허나 버텍스속성포맷으로 그저 반복되면서 수정될일이 없는 (대표적으로 탄젠트)는 flaot32->16으로 압축을 한거같다

 vertexAttributes[0] = new VertexAttributeDescriptor(dimension:3); 
        vertexAttributes[1] = new VertexAttributeDescriptor
            (VertexAttribute.Normal , dimension:3);
        //노말 float3 라서 삼차원 ,스트림은 재생순서  우리는 포지션 노말 탄젠트 텍스쳐순으로 재생
        vertexAttributes[2] = new VertexAttributeDescriptor
            (VertexAttribute.Tangent, VertexAttributeFormat.Float16,4);

        vertexAttributes[3] = new VertexAttributeDescriptor
            (VertexAttribute.TexCoord0, VertexAttributeFormat.Float16, 2);


위처럼 버텍스의 각 속성들을 버텍스속성배열에 정의하였고

이제 버텍스구간에 파라미터를 이용하여 버텍스 위치등을 실제로 배치한다
위에서 속성들을 선언한 구조체를 사용한다
인자로 버텍스 갯수와, 배열로 각각 지정해둔 버텍스속성배열이다

meshData.SetVertexBufferParams(vertexCount, vertexAttributes);
vertexAttributes.Dispose();
// 실제 에디터상에서 정점들의 정보를 담아줄 vertices배열
NativeArray<Vertex> vertices = meshData.GetVertexData<Vertex>():

half h0 = half(0f), h1 = half(1f); 로 하프를 쓸수있게

var vertex = new Vertex // 모두 같은 값을 지니는 노말과 탄젠트는 묶어버려 한번에 해결
        {
            normal = back(),
            tangent = half4(h1, h0, h0, half(-1))
        };

각 버텍스(소문자) 들의 배열요소들의 각 위치등의 속성정보들을 수정

        vertex.position = 0f;
        vertex.texCoord0 = h0;
        vertices[0] = vertex;

        vertex.position = right();
        vertex.texCoord0 = half2(h1,h0);
        vertices[1] = vertex;

        vertex.position = up();
        vertex.texCoord0 = half2(h0,h1);
        vertices[2] = vertex;

        vertex.position = float3(1f,1f,0f);
        vertex.texCoord0 = h1;
        vertices[3] = vertex;

 

정점들을 완성하였으면, 삼각형을 만들어주자

정점에서는 셋버텍스파라미터 함수를 썻다면

이번엔 셋인덱스버퍼파마리터이다

meshData.SetIndexBufferParams(triangleIndexCount, IndexFormat.UInt16);

겟인덱스 데이타는 버텍스버퍼에 담긴 각 인덱스버퍼의 정보를 끄집어 사용
        NativeArray<ushort> triangleIndicies = meshData.GetIndexData<ushort>();
        triangleIndicies[0] = 0;
        triangleIndicies[1] = 2;
        triangleIndicies[2] = 1;
        triangleIndicies[3] = 1;
        triangleIndicies[4] = 2;
        triangleIndicies[5] = 3;

 

바운딩작업

var bounds = new Bounds(new Vector3(0.5f, 0.5f), new Vector3(1f,1f)) ;

        meshData.subMeshCount = 1;
        meshData.SetSubMesh(0,
            new SubMeshDescriptor(0,triangleIndexCount){ bounds = bounds, vertexCount = vertexCount }
            , MeshUpdateFlags.DontRecalculateBounds);

        Mesh mesh = new Mesh {bounds = bounds, name = "Advanced Procedural Mesh"};
        Mesh.ApplyAndDisposeWritableMeshData(meshDataArray, mesh);

생성한 메쉬를 넣어주기
        GetComponent<MeshFilter>().mesh = mesh;

}



결과적으로 첫번쨰 방식과 두번째 방식 모두 같은 그림을 그린다
사진은 2번쨰 방식만 올리지만 결과는 어차피 동일하다

float -> half로 줄인 탄젠트와 UV(텍스처) 절반으로 줄어든게 보인다
마찬가지로 정점또한 6개가 아닌 4개로(2개는 재활용) 삼각형을 2개 만들고 쿼드를 만들며
텍스처또한 잘 붙은걸로 보인다