구를 만드는 알고리즘?


27

누구든지 la위도 선, lo경도 선 및 반지름으로 구를 절차 적으로 생성하는 알고리즘 이 r있습니까? Unity와 함께 작동하려면 정점 위치를 정의 한 다음 색인을 통해 삼각형을 정의해야합니다 ( 더 많은 정보 ).


편집하다

여기에 이미지 설명을 입력하십시오

코드가 통일되도록했습니다. 하지만 뭔가 잘못한 것 같습니다. 를 켜면 detailLevel모든 정점과 다각형을 움직이지 않고 추가합니다. 내가 뭔가를 잊었습니까?


편집 2

여기에 이미지 설명을 입력하십시오

법선을 따라 메쉬의 크기를 조정하려고했습니다. 이것이 내가 얻은 것입니다. 뭔가 빠진 것 같아요. 특정 법선 만 스케일링해야합니까?


1
기존 오픈 소스 구현이 어떻게 수행되는지 보지 않겠습니까? 예를 들어 Three.js 가 메시를 사용하여 어떻게 수행하는지 살펴보십시오 .
brice

3
작은 참고로 : 위도 / 경도를 수행하지 않는 한 거의 원하지 않는 것이 있습니다. 왜냐하면 삼각형이 다른 방법을 사용하는 삼각형보다 균일하기 때문입니다. (북극 근처의 삼각형과 적도 근처의 삼각형을 비교하십시오. 어느 경우 든 위도 한 줄을 중심으로 동일한 수의 삼각형을 사용하지만 위도 선은 적도보다 매우 작은 극 근처에 있습니다. David Lively의 답변과 같은 기술은 일반적으로 훨씬 낫습니다.
Steven Stadnicki 2016 년

1
세분화 후 정점 위치를 정규화하지 않습니다. 나는 그 예를 그 부분에 포함시키지 않았다. 정규화하면 모두 중심에서 등거리가되어 원하는 곡선 근사치를 만듭니다.
3Dave

정 이십 면체 중앙에 풍선을 부 풀리는 것을 생각하십시오. 풍선이 메쉬를 밀면 풍선 (구)의 모양과 일치합니다.
3Dave

4
"정규화"는 벡터의 길이를 1로 설정하는 것을 의미합니다 vertices[i] = normalize(vertices[i]). 또한 새롭고 정확한 법선도 제공하므로 normals[i] = vertices[i]나중에 해야합니다 .
sam hocevar

답변:


31

이와 같은 것을 얻으려면 :

여기에 이미지 설명을 입력하십시오

정 이십 면체 (20면 일반 솔리드)를 만들고면을 세분하여 구를 만듭니다 (아래 코드 참조).

아이디어는 기본적으로 다음과 같습니다.

  • 규칙적인 n- 헤드 론 (모든면이 같은 크기의 솔리드)을 만듭니다. 정 이십 면체를 사용합니다. 왜냐하면 모든면의 크기가 같은면이 가장 많기 때문입니다. (어딘가에 대한 증거가 있습니다. 궁금한 점이 있다면 Google에 부담없이 문의하십시오.) 이렇게하면 거의 모든 얼굴의 크기가 같은 구가 생겨 텍스처링이 조금 더 쉬워집니다.

여기에 이미지 설명을 입력하십시오

  • 각면을 같은 크기의 네면으로 나눕니다. 이 작업을 수행 할 때마다 모델의면 수가 4 배가됩니다.

    ///      i0
    ///     /  \
    ///    m02-m01
    ///   /  \ /  \
    /// i2---m12---i1

i0, i1그리고 i2원래 삼각형의 꼭지점이다. (실제로 정점 버퍼에 대한 인덱스이지만 다른 주제입니다). m01가장자리의 중간 점은 (i0,i1), M12는 모서리의 중간 점이며 (i1,12), 그리고 m02, 분명히, 가장자리의 중간 점입니다 (i0,i2).

면을 세분 할 때마다 중복 정점을 만들지 않아야합니다. 각 중간 점은 하나의 다른 소스면과 공유됩니다 (가장자리가면간에 공유되므로). 아래 코드는 생성 된 이름이 지정된 중간 점의 사전을 유지하고, 이전에 생성 된 중간 점을 사용할 때 새 색인을 생성하는 대신 색인을 반환하여이를 설명합니다.

  • 큐브의 원하는 수에 도달 할 때까지 반복하십시오.

  • 완료되면 모든 정점을 정규화하여 표면을 매끄럽게 만듭니다. 이 작업을 수행하지 않으면 구 대신 고해상도의 정 이십 면체를 얻게됩니다.

  • 짜잔! 끝났습니다. 결과 벡터 및 인덱스 버퍼를 a VertexBuffer및 로 변환하고로 그 IndexBuffer립니다 Device.DrawIndexedPrimitives().

모델을 생성하기 위해 "Sphere"클래스에서 사용하는 내용은 다음과 같습니다 (XNA 데이터 유형 및 C #이지만 매우 명확해야 함).

        var vectors = new List<Vector3>();
        var indices = new List<int>();

        GeometryProvider.Icosahedron(vectors, indices);

        for (var i = 0; i < _detailLevel; i++)
            GeometryProvider.Subdivide(vectors, indices, true);

        /// normalize vectors to "inflate" the icosahedron into a sphere.
        for (var i = 0; i < vectors.Count; i++)
            vectors[i]=Vector3.Normalize(vectors[i]);

그리고 GeometryProvider수업

public static class GeometryProvider
{

    private static int GetMidpointIndex(Dictionary<string, int> midpointIndices, List<Vector3> vertices, int i0, int i1)
    {

        var edgeKey = string.Format("{0}_{1}", Math.Min(i0, i1), Math.Max(i0, i1));

        var midpointIndex = -1;

        if (!midpointIndices.TryGetValue(edgeKey, out midpointIndex))
        {
            var v0 = vertices[i0];
            var v1 = vertices[i1];

            var midpoint = (v0 + v1) / 2f;

            if (vertices.Contains(midpoint))
                midpointIndex = vertices.IndexOf(midpoint);
            else
            {
                midpointIndex = vertices.Count;
                vertices.Add(midpoint);
                midpointIndices.Add(edgeKey, midpointIndex);
            }
        }


        return midpointIndex;

    }

    /// <remarks>
    ///      i0
    ///     /  \
    ///    m02-m01
    ///   /  \ /  \
    /// i2---m12---i1
    /// </remarks>
    /// <param name="vectors"></param>
    /// <param name="indices"></param>
    public static void Subdivide(List<Vector3> vectors, List<int> indices, bool removeSourceTriangles)
    {
        var midpointIndices = new Dictionary<string, int>();

        var newIndices = new List<int>(indices.Count * 4);

        if (!removeSourceTriangles)
            newIndices.AddRange(indices);

        for (var i = 0; i < indices.Count - 2; i += 3)
        {
            var i0 = indices[i];
            var i1 = indices[i + 1];
            var i2 = indices[i + 2];

            var m01 = GetMidpointIndex(midpointIndices, vectors, i0, i1);
            var m12 = GetMidpointIndex(midpointIndices, vectors, i1, i2);
            var m02 = GetMidpointIndex(midpointIndices, vectors, i2, i0);

            newIndices.AddRange(
                new[] {
                    i0,m01,m02
                    ,
                    i1,m12,m01
                    ,
                    i2,m02,m12
                    ,
                    m02,m01,m12
                }
                );

        }

        indices.Clear();
        indices.AddRange(newIndices);
    }

    /// <summary>
    /// create a regular icosahedron (20-sided polyhedron)
    /// </summary>
    /// <param name="primitiveType"></param>
    /// <param name="size"></param>
    /// <param name="vertices"></param>
    /// <param name="indices"></param>
    /// <remarks>
    /// You can create this programmatically instead of using the given vertex 
    /// and index list, but it's kind of a pain and rather pointless beyond a 
    /// learning exercise.
    /// </remarks>

    /// note: icosahedron definition may have come from the OpenGL red book. I don't recall where I found it. 
    public static void Icosahedron(List<Vector3> vertices, List<int> indices)
    {

        indices.AddRange(
            new int[]
            {
                0,4,1,
                0,9,4,
                9,5,4,
                4,5,8,
                4,8,1,
                8,10,1,
                8,3,10,
                5,3,8,
                5,2,3,
                2,7,3,
                7,10,3,
                7,6,10,
                7,11,6,
                11,0,6,
                0,1,6,
                6,1,10,
                9,0,11,
                9,11,2,
                9,2,5,
                7,2,11 
            }
            .Select(i => i + vertices.Count)
        );

        var X = 0.525731112119133606f;
        var Z = 0.850650808352039932f;

        vertices.AddRange(
            new[] 
            {
                new Vector3(-X, 0f, Z),
                new Vector3(X, 0f, Z),
                new Vector3(-X, 0f, -Z),
                new Vector3(X, 0f, -Z),
                new Vector3(0f, Z, X),
                new Vector3(0f, Z, -X),
                new Vector3(0f, -Z, X),
                new Vector3(0f, -Z, -X),
                new Vector3(Z, X, 0f),
                new Vector3(-Z, X, 0f),
                new Vector3(Z, -X, 0f),
                new Vector3(-Z, -X, 0f) 
            }
        );


    }



}

좋은 대답입니다. 감사. 말할 수는 없지만이 통일 코드입니까? 아, 그리고 위도 / 경도는 해상도를 설정할 수있는 한 중요하지 않습니다.
Daniel Pendergast 2016 년

Unity (XNA)는 아니지만 정점 좌표와 색인 목록을 제공합니다. Vector3을 Unity와 동등한 것으로 대체하십시오. 세분화 반복 횟수를 조정하여 해상도를 설정합니다. 각 루프는면 수에 4를 곱합니다. 2 또는 3 회 반복하면 멋진 구가됩니다.
3Dave

아 알 겠어요 Unity C #과 거의 동일합니다. 몇 가지 질문이 있습니다 ... 왜 인덱스가 정의 될 때 int배열 안에 넣 습니까? 그리고 무엇을 .Select(i => i + vertices.Count)합니까?
Daniel Pendergast 2016 년

.Select(i => i + vertices.Count)전혀 작동하지 않습니다. XNA 전용 기능입니까?
Daniel Pendergast

1
정의에 따라 'System.Linq 사용'을 포함 시키십시오. 선택 등
3Dave

5

구체의 파라 메트릭 정의를 고려해 봅시다 :

구의 파라 메트릭 정의

여기서 세타와 피있는 우리라고 부르는 것으로, 강도를 증가시키는 두 가지 var tvar u이고, Rx, Ry가 및 Rz가이 분야의 경우, 하나로서 정의되며, 독립적 반경 (반경) 세 직교 방향이다 반경 var rad.

이제 ...심볼이 루프의 사용을 암시하는 반복을 나타냅니다. 의 개념 stacks과는 rows"몇 번 것이다 당신으로 반복"입니다. 각 반복은 t 또는 u의 값을 더하기 때문에 반복이 많을수록 값이 작을수록 구의 곡률이 더 정확합니다.

'구면 그리기'기능의 전제 조건은 다음과 같은 주어진 매개 변수를 갖습니다 int latitudes, int longitudes, float radius. 사후 조건 (출력)은 반환 된 정점을 반환하거나 적용하는 것입니다. 이것을 어떻게 사용 하려는지에 따라 함수는 vector3(3 차원 벡터) 배열을 반환 하거나 2.0 이전 버전의 간단한 OpenGL을 사용하는 경우 정점을 컨텍스트에 직접 적용 할 수 있습니다.

NB OpenGL에 꼭짓점을 적용하는 것은 다음 함수를 호출합니다 glVertex3f(x, y, z). 정점을 저장하는 경우 vector3(x, y, z)간편한 저장을 위해 새 항목 을 추가합니다 .

또한 위도 및 경도 시스템이 작동하도록 요청한 방식은 구 정의에 대한 조정 (기본적으로 z 및 y 전환)을 조정해야하지만 정의가 매우 가변적이며 자유롭게 전환 할 수 있음을 보여줍니다. x, y 및 z 매개 변수는 구를 그리는 방향 (위도와 경도가있는 위치)을 변경합니다.

이제 위도와 경도를 어떻게할지 살펴 보겠습니다. 위도는 변수로 표시되며 u0에서 2π 라디안 (360도)까지 반복됩니다. 따라서 반복을 다음과 같이 코딩 할 수 있습니다.

float latitude_increment = 360.0f / latitudes;

for (float u = 0; u < 360.0f; u += latitude_increment) {
    // further code ...
}

이제 경도는 변수로 표시되며 t0에서 π (180도) 동안 반복됩니다. 따라서 다음 코드는 이전 코드와 유사합니다.

float latitude_increment = 360.0f / latitudes;
float longitude_increment = 180.0f / longitudes;

for (float u = 0; u <= 360.0f; u += latitude_increment) {
    for (float t = 0; t <= 180.0f; t += longitude_increment) {
        // further code ...
    }
}

(루프는하는 것으로 클루 시브 파라 메트릭 통합을위한 간격이 2π 0에서 있기 때문에이 터미널 조건은 인 클루 시브 . 당신의 조건이 아닌이 포함됩니다 경우 일부 영역을 얻을 것이다.)

이제 구의 간단한 정의에 따라 변수 정의를 다음과 같이 도출 할 수 있습니다 (가정 float rad = radius;) :

float x = (float) (rad * Math.sin(Math.toRadians(t)) * Math.sin(Math.toRadians(u)));
float y = (float) (rad * Math.cos(Math.toRadians(t)));
float z = (float) (rad * Math.sin(Math.toRadians(t)) * Math.cos(Math.toRadians(u)));

하나 더 중요한 경고! 대부분의 경우 OpenGL 형식을 사용하게되며, 그렇지 않은 경우에도이를 수행해야 할 수도 있습니다. 3 차원의 객체는 몇 개의 정점을 정의해야합니다. 이것은 일반적으로 계산 가능한 다음 정점을 제공함으로써 달성됩니다.

(원시) 모양을 정의하기 위해 여러 정점을 사용하는 방법

그냥 다른 좌표 위의 그림에서 어떻게 x+∂하고 y+∂, 우리가 쉽게 원하는 사용하기위한 세 가지 다른 정점을 생성 할 수 있습니다. 다른 정점은 (가정 float rad = radius;)입니다.

float x = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.sin(Math.toRadians(u)));
float y = (float) (rad * Math.cos(Math.toRadians(t + longitude_increment)));
float z = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.cos(Math.toRadians(u)));

float x = (float) (rad * Math.sin(Math.toRadians(t)) * Math.sin(Math.toRadians(u + latitude_increment)));
float y = (float) (rad * Math.cos(Math.toRadians(t)));
float z = (float) (rad * Math.sin(Math.toRadians(t)) * Math.cos(Math.toRadians(u + latitude_increment)));

float x = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.sin(Math.toRadians(u + latitude_increment)));
float y = (float) (rad * Math.cos(Math.toRadians(t + longitude_increment)));
float z = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.cos(Math.toRadians(u + latitude_increment)));

마지막으로 구의 모든 정점을 반환하는 전체 기능이 작동하고 두 번째는 작동하는 OpenGL 코드 구현을 보여줍니다 (이것은 JavaScript가 아닌 C 스타일 구문이며 모든 C 스타일 언어에서 작동합니다. Unity를 사용할 때 C # 포함).

static Vector3[] generateSphere(float radius, int latitudes, int longitudes) {

    float latitude_increment = 360.0f / latitudes;
    float longitude_increment = 180.0f / longitudes;

    // if this causes an error, consider changing the size to [(latitude + 1)*(longitudes + 1)], but this should work.
    Vector3[] vertices = new Vector3[latitude*longitudes];

    int counter = 0;

    for (float u = 0; u < 360.0f; u += latitude_increment) {
        for (float t = 0; t < 180.0f; t += longitude_increment) {

            float rad = radius;

            float x = (float) (rad * Math.sin(Math.toRadians(t)) * Math.sin(Math.toRadians(u)));
            float y = (float) (rad * Math.cos(Math.toRadians(t)));
            float z = (float) (rad * Math.sin(Math.toRadians(t)) * Math.cos(Math.toRadians(u)));

            vertices[counter++] = new Vector3(x, y, z);

        }
    }

    return vertices;

}

OpenGL 코드 :

static int createSphereBuffer(float radius, int latitudes, int longitudes) {

    int lst;

    lst = glGenLists(1);

    glNewList(lst, GL_COMPILE);
    {

        float latitude_increment = 360.0f / latitudes;
        float longitude_increment = 180.0f / longitudes;

        for (float u = 0; u < 360.0f; u += latitude_increment) {

            glBegin(GL_TRIANGLE_STRIP);

            for (float t = 0; t < 180.0f; t += longitude_increment) {

                float rad = radius;

                float x = (float) (rad * Math.sin(Math.toRadians(t)) * Math.sin(Math.toRadians(u)));
                float y = (float) (rad * Math.cos(Math.toRadians(t)));
                float z = (float) (rad * Math.sin(Math.toRadians(t)) * Math.cos(Math.toRadians(u)));

                vertex3f(x, y, z);

                float x1 = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.sin(Math.toRadians(u + latitude_increment)));
                float y1 = (float) (rad * Math.cos(Math.toRadians(t + longitude_increment)));
                float z1 = (float) (rad * Math.sin(Math.toRadians(t + longitude_increment)) * Math.cos(Math.toRadians(u + latitude_increment)));

                vertex3f(x1, y1, z1);

            }

            glEnd();

        }

    }
    glEndList()

    return lst;

}

// to render VVVVVVVVV

// external variable in main file
static int sphereList = createSphereBuffer(desired parameters)

// called by the main program
void render() {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glCallList(sphereList);

    // any additional rendering and buffer swapping if not handled already.

}

추신 당신은이 진술을 알아 차렸을 것 rad = radius;입니다. 이를 통해 위치 또는 각도를 기반으로 루프에서 반경을 수정할 수 있습니다. 즉, 구에 노이즈를 적용하여 구를 거칠 수 있으므로 원하는 효과가 행성과 같은 경우 더 자연스럽게 보입니다. 예 :float rad = radius * noise[x][y][z];

클로드 헨리


`float z = (float) (rad * Math.sin (Math.toRadians (t)) * Math.cos (Math.toRadians (u)));`줄이 잘못되었습니다. 이미 빗변이있는 X, Y를 계산했습니다 rad. 이제 삼각형의 한쪽 다리를 만들고 삼각형의 빗변도임을 암시합니다 rad. 이것은 효과적으로 반경을 제공합니다 rad * sqrt(2).
3Dave

@DavidLively 그것을 지적 해 주셔서 감사합니다.
claudehenry

몇 년 전 내 게시물 중 하나에서 실수를 발견하면 항상 재미 있습니다. 일어난다. :)
3Dave

4

나는 재미와 과학을 위해 큐브의 구체를 만들기 위해 잠시 동안 이와 같은 것을 만들었습니다. 너무 어렵지 않습니다. 기본적으로 정점의 원을 만드는 기능을 수행 한 다음 구를 만드는 데 필요한 반경에서 각 높이에서 원을 만들려는 높이 증분을 단계별로 수행합니다. 큐브가 아닌 코드를 수정했습니다.

public static void makeSphere(float sphereRadius, Vector3f center, float heightStep, float degreeStep) {
    for (float y = center.y - sphereRadius; y <= center.y + sphereRadius; y+=heightStep) {
        double radius = SphereRadiusAtHeight(sphereRadius, y - center.y); //get the radius of the sphere at this height
        if (radius == 0) {//for the top and bottom points of the sphere add a single point
            addNewPoint((Math.sin(0) * radius) + center.x, y, (Math.cos(0) * radius) + center.z));
        } else { //otherwise step around the circle and add points at the specified degrees
            for (float d = 0; d <= 360; d += degreeStep) {
                addNewPoint((Math.sin(d) * radius) + center.x, y, (Math.cos(d) * radius) + center.z));
            }
        }
    }
}

public static double SphereRadiusAtHeight(double SphereRadius, double Height) {
    return Math.sqrt((SphereRadius * SphereRadius) - (Height * Height));
}

이제이 코드는 위도에 대한 점을 만듭니다. 그러나 거의 동일한 코드를 사용하여 경도 선을 만들 수 있습니다. 각 반복 사이를 회전하고 각에서 전체 원을 만들어야 degreeStep합니다.

죄송합니다. 전체 답변이나 Unity 고유의 내용은 아니지만 시작하기를 바랍니다.


위도 / 경도 구가 필요한 경우 꽤 좋지만 마지막 단계까지 구면 좌표로 작업하여 조금 단순화 할 수 있습니다.
3Dave

1
감사합니다 @David. 구형 좌표를 사용하여 버전을 작성하는 경우 여기에 게시 할 것에 동의합니다.
MichaelHouse

3

단순한 모양으로 시작하는 것이 아니라 중심에서 모서리까지 r 거리의 상자가 될 수 있습니다. 더 자세한 구를 만들려면 모든 다각형을 세분화 한 다음 정점을 중심으로부터 r 거리로 이동하여 벡터가 현재 위치를 통과하도록합니다.

당신의 취향에 맞게 구형이 될 때까지 계속 반복하십시오.


이것은 본질적으로 icosahedral 방식과 동일하며 시작 모양이 다릅니다. 내가 생각하지 않은 큐브로 시작하는 한 가지 장점은 큐브 맵을 사용할 수 있고 텍스처 이음새가 구형 메쉬의 가장자리와 완벽하게 정렬 될 수 있기 때문에 괜찮은 UV 맵을 작성하는 것이 훨씬 쉽다는 것입니다.
Steven Stadnicki 2016 년

@StevenStadnicki 내가 큐브에 가지고있는 유일한 문제는 몇 번의 세분화 후 얼굴이 매우 다른 크기로 감추는 경향이 있다는 것입니다.
3Dave

@DavidLively 그것은 당신이 세분화하는 방법에 많이 의존합니다-큐브의 정사각형면을 고른 그리드로 자르고 바깥 쪽 / 정규화로 투영하면 사실이지만, 얼굴을 불균일하게 격자로 만들면 실제로는 돌출부는 모서리의 호를 따라 균등하게 이격되고; 꽤 잘 작동합니다.
Steven Stadnicki 2016 년

트윗 담아 가기
3Dave

@EricJohansson btw, 선생님으로서 이것은 이전에 세분화 방법을 보지 못했던 누군가에게 상당히 중요한 통찰력이라고 언급해야한다고 생각합니다. 다음 12 시간 동안 인류에 대한 나의 믿음을 새롭게했습니다.
3Dave

2

실제로 3D 지오메트리 또는 모양 만 필요합니까?

단일 쿼드를 사용하여 '가짜'구를 만들 수 있습니다. 그 위에 원을 넣고 올바르게 음영 처리하십시오. 이는 카메라와의 거리 또는 해상도에 관계없이 정확하게 필요한 해상도를 얻을 수 있다는 이점이 있습니다.

있다 여기 튜토리얼 .


1
해킹은 좋지만 질감이 필요하면 실패합니다.
3Dave

@DavidLively 다각형을 개별적으로 텍스쳐링 할 필요가 없다면 회전에 따라 픽셀 당 텍스처 좌표를 계산할 수 있어야합니다.
David C. Bishop

@DavidCBishop 표면의 "렌즈 길이"를 고려해야합니다. 텍셀 좌표는 원근 경계 근처에서 원근에 밀착되어 회전합니다. 또한 이것은 버텍스 쉐이더에서 수행 될 수있는 픽셀 쉐이더로 더 많은 작업을 이동시키는 것과 관련이 있습니다.
3Dave

0

다음은 구의 동일한 간격으로 정점에 대한 코드입니다. 오렌지 껍질과 같이 구 주위에 점 선을 나선형으로 감습니다. 나중에 정점에 참여하는 방법은 사용자에게 달려 있습니다. 루프에서 각 삼각형의 2로 이웃 점을 사용할 수 있고 세 번째는 구 주위에서 비례하여 1 위 또는 아래로 비틀어지는 것을 알 수 있습니다 ... 루프와 가장 가까운 이웃으로 삼각형을 할 수도 있습니다. 더 나은 방법을 알고 있습니까?

var spherevertices = vector3 generic list...

public var numvertices= 1234;
var size = .03;  

function sphere ( N:float){//<--- N is the number of vertices i.e 123

var inc =  Mathf.PI  * (3 - Mathf.Sqrt(5));
var off = 2 / N;
for (var k = 0; k < (N); k++)
{
    var y = k * off - 1 + (off / 2);
    var r = Mathf.Sqrt(1 - y*y);
    var phi = k * inc;
    var pos = Vector3((Mathf.Cos(phi)*r*size), y*size, Mathf.Sin(phi)*r*size); 

    spherevertices   add pos...

}

};


-1

David는 그의 대답에서 절대적으로 맞지만 다른 관점을 제시하고 싶습니다.

절차 적 콘텐츠 생성을위한 과제를 위해, 나는 전통적인 분할 된 구체와 정 이십 면체를 살펴 보았습니다. 다음과 같이 절차 적으로 생성 된 구체를 살펴보십시오.

멋진 분야

둘 다 완벽하게 유효한 구체처럼 보입니다. 자, 그들의 와이어 프레임을 보자 :

와우 밀도

와우, 무슨 일이야? 두 번째 구의 와이어 프레임 버전은 너무 조밀 하여 질감이 있습니다! 두 번째 버전은 정 이십 면체입니다. 거의 완벽한 영역이지만 높은 가격에 제공됩니다.

Sphere 1은 총 3,844 개의 면 에 대해 x 축에 31 개의 하위 분할과 z 축에 31 개의 하위 분할을 사용합니다 .

Sphere 2는 5 개의 재귀 세분을 사용하여 총 109,220 개의 면을 사용합니다.

하지만 괜찮습니다. 품질을 상당히 축소하자 :

울퉁불퉁

Sphere 1은 총 100 개의면에 대해 x 축에서 5 개의 세분화 및 z 축에서 5 개의 세분화를 사용합니다.

Sphere 2는 0 개의 재귀 세분을 사용하여 총 100 개의면을 만듭니다.

그들은 같은 양의 얼굴을 사용하지만 제 생각에는 왼쪽의 구가 더 좋아 보입니다. 덜 울퉁불퉁하고 훨씬 더 둥글게 보입니다. 두 방법으로 얼마나 많은 얼굴을 생성하는지 살펴 보겠습니다.

정 이십 면체 :

  • 레벨 0-100 얼굴
  • 레벨 1-420면
  • 레벨 2-1,700 얼굴
  • 레벨 3-6,820의 얼굴
  • 4 ~ 27,300 레벨
  • 레벨 5-109,220의 얼굴

세분화 된 구체 :

  • YZ : 5 ~ 100면
  • YZ : 10 ~ 400면
  • YZ : 15 ~ 900면
  • YZ : 20 ~ 1,600면
  • YZ : 25 ~ 2,500면
  • YZ : 30 ~ 3,600면

보시다시피, 정 이십 면체는 기하 급수적으로 세 번째 힘으로 증가합니다! 모든 삼각형마다 3 개의 새로운 삼각형으로 세분화해야하기 때문입니다.

진실은 이십 면체가 당신에게 줄 정밀함이 필요 하지 않다는 것입니다. 둘 다 훨씬 더 어려운 문제를 숨기고 있기 때문에 3D 구체에서 2D 평면을 텍스처링합니다. 상단은 다음과 같습니다.

최고 짜증

왼쪽 상단에서 텍스처가 사용되고 있음을 확인할 수 있습니다. 우연히도 절차 적으로 생성되고 있습니다. (이것은 절차 적 생성에 관한 과정 이었습니까?)

끔찍해 보이죠? 글쎄, 이것은 얻을만큼 좋은 것입니다. 대부분의 사람들은 이것을 제대로 얻지 못하기 때문에 텍스처 매핑에 최고 점수를 받았습니다.

따라서 코사인과 사인을 사용하여 구를 생성하는 것을 고려하십시오. 같은 양의 디테일로 훨씬 적은면을 생성합니다.


6
나는 이것을 하향식으로 만 감당 할까 두렵다. icosphere는 기하 급수적으로 증가합니까? 그 때문입니다 당신은 당신이 기하 급수적으로 확장해야했다. UV 구체는 동일한 양의 세부 묘사를 위해 icosphere보다 적은면을 생성합니까? 그것은 완전히 틀린 것입니다.
sam hocevar

4
세분은 재귀적일 필요는 없습니다. 삼각형의 가장자리를 원하는만큼 같은 부분으로 나눌 수 있습니다. N부품을 사용 N*N하면 UV-sphere에서하는 것과 똑같은 2 차 삼각형 을 얻을 수 있습니다 .
sam hocevar

6
또한 당신이 말하는 "구석이 적고 훨씬 더 둥글다"고 말하는 구가 가장 좋은 각도에서 보여 져서 부정직 한 주장을한다고 덧붙여 야합니다. 위에서 본 구체로 동일한 스크린 샷을 수행하여 내가 의미하는 바를 확인하십시오.
sam hocevar

4
또한 정 이십 면체 숫자가 정확하지 않습니다. 레벨 0은 20 개의면 (정의에 따라)과 80, 320, 1280 등입니다. 원하는 수와 패턴으로 세분화 할 수 있습니다. 모델의 매끄러움은 최종 결과에서 얼굴의 수와 분포에 따라 결정되며 (얼굴을 생성하는 데 사용 된 방법에 관계없이) 각 얼굴의 크기를 가능한 한 균일하게 유지하려고합니다 (극성이 없음) 시야각에 관계없이 일관된 프로파일을 유지합니다. 또한 세분 코드가 훨씬 간단하다는 사실을 추가하십시오.
3Dave

2
이 답변에 약간의 노력이 기울여서 그것을 내리는 것에 대해 약간 기분이 좋지 않습니다. 그러나 그것은 완전히 그리고 완전히 틀리기 때문에 나는해야합니다. FullHD에서 전체 화면을 채우는 완벽하게 둥글게 보이는 Icosphere는 5 개의 하위 분할이 필요하며 기본 분할 정사각형은 하위 분할이 없습니다. 세분화가없는 정 이십 면체는 100 개의면이없고 20 개의면이 있습니다. 이코 사 = 20. 이름입니다! 각 세분은면 수에 4를 곱하므로 1-> 80, 2-> 320, 3-> 1280, 4-> 5120, 5-> 20,480입니다. 지구권을 사용하면 똑같이 둥근 구를 얻기 위해 최소 40,000 개의면이 필요합니다.
피터

-1

아래 스크립트는 n 개의 다각형 ...베이스 12를 가진 정 이십 면체를 만듭니다. 또한 다각형을 별도의 메쉬로 세분화하고 총 버텍스 복제본 및 다각형을 계산합니다.

비슷한 것을 찾을 수 없으므로 이것을 만들었습니다. 스크립트를 게임 오브젝트에 연결하고 에디터에서 서브 디비전을 설정하십시오. 다음으로 노이즈 수정 작업.


/* Creates an initial Icosahedron with 12 vertices...
 * ...Adapted from https://medium.com/@peter_winslow/creating-procedural-icosahedrons-in-unity-part-1-df83ecb12e91
 * ...And a couple other Icosahedron C# for Unity scripts
 * 
 * Allows an Icosahedron to be created with multiple separate polygon meshes
 * I used a dictionary of Dictionary<int, List<Vector3>> to represent the 
 * Polygon index and the vertice index
 * polygon[0] corresponds to vertice[0]
 * so that all vertices in dictionary vertice[0] will correspond to the polygons in polygon[0]
 * 
 * If you need help understanding Dictionaries
 * https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx
 * 
 * --I used dictionaries because I didn't know what programming instrument to use, so there may be more
 * elegant or efficient ways to go about this.
 * 
 * Essentially int represents the index, and 
 * List<Vector3> represents the actual Vector3 Transforms of the triangle
 * OR List<Vector3> in the polygon dictionary will act as a reference to the indice/index number of the vertices
 * 
 * For example the polygon dictionary at key[0] will contain a list of Vector3's representing polygons
 * ... Vector3.x , Vector3.y, Vector3.z in the polygon list would represent the 3 indexes of the vertice[0] list
 * AKA the three Vector3 transforms that make up the triangle
 *    .
 *  ./_\.
 * 
 * Create a new GameObject and attach this script
 *  -The folders for the material and saving of the mesh data will be created automatically 
 *    -Line 374/448
 * 
 * numOfMainTriangles will represent the individual meshes created
 * numOfSubdivisionsWithinEachTriangle represents the number of subdivisions within each mesh
 * 
 * Before running with Save Icosahedron checked be aware that it can take several minutes to 
 *   generate and save all the meshes depending on the level of divisions
 * 
 * There may be a faster way to save assets - Line 430 - AssetDatabase.CreateAsset(asset,path);
 * */

using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class UnityIcosahedronGenerator : MonoBehaviour {
    IcosahedronGenerator icosahedron;
    public const int possibleSubDivisions = 7;
    public static readonly int[] supportedChunkSizes = { 20, 80, 320, 1280, 5120, 20480, 81920};

    [Range(0, possibleSubDivisions - 1)]
    public int numOfMainTriangles = 0;
    [Range(0,possibleSubDivisions - 1)]
    public int numOfSubdivisionsWithinEachTriangle = 0;
    public bool saveIcosahedron = false;

    // Use this for initialization
    void Start() {
        icosahedron = ScriptableObject.CreateInstance<IcosahedronGenerator>();

        // 0 = 12 verts, 20 tris
        icosahedron.GenBaseIcosahedron();
        icosahedron.SeparateAllPolygons();

        // 0 = 12 verts, 20 tris - Already Generated with GenBaseIcosahedron()
        // 1 = 42 verts, 80 tris
        // 2 = 162 verts, 320 tris
        // 3 = 642 verts, 1280 tris
        // 5 = 2562 verts, 5120 tris
        // 5 = 10242 verts, 20480 tris
        // 6 = 40962verts, 81920 tris
        if (numOfMainTriangles > 0) {
            icosahedron.Subdivide(numOfMainTriangles);
        }
        icosahedron.SeparateAllPolygons();

        if (numOfSubdivisionsWithinEachTriangle > 0) {
            icosahedron.Subdivide(numOfSubdivisionsWithinEachTriangle);
        }

        icosahedron.CalculateMesh(this.gameObject, numOfMainTriangles,numOfSubdivisionsWithinEachTriangle, saveIcosahedron);
        icosahedron.DisplayVertAndPolygonCount();
    }
}

public class Vector3Dictionary {
    public List<Vector3> vector3List;
    public Dictionary<int, List<Vector3>> vector3Dictionary;

    public Vector3Dictionary() {
        vector3Dictionary = new Dictionary<int, List<Vector3>>();
        return;
    }

    public void Vector3DictionaryList(int x, int y, int z) {
        vector3List = new List<Vector3>();

        vector3List.Add(new Vector3(x, y, z));
        vector3Dictionary.Add(vector3Dictionary.Count, vector3List);

        return;
    }

    public void Vector3DictionaryList(int index, Vector3 vertice) {
        vector3List = new List<Vector3>();

        if (vector3Dictionary.ContainsKey(index)) {
            vector3List = vector3Dictionary[index];
            vector3List.Add(vertice);
            vector3Dictionary[index] = vector3List;
        } else {
            vector3List.Add(vertice);
            vector3Dictionary.Add(index, vector3List);
        }

        return;
    }

    public void Vector3DictionaryList(int index, List<Vector3> vertice, bool list) {
        vector3List = new List<Vector3>();

        if (vector3Dictionary.ContainsKey(index)) {
            vector3List = vector3Dictionary[index];
            for (int a = 0; a < vertice.Count; a++) {
                vector3List.Add(vertice[a]);
            }
            vector3Dictionary[index] = vector3List;
        } else {
            for (int a = 0; a < vertice.Count; a++) {
                vector3List.Add(vertice[a]);
            }
            vector3Dictionary.Add(index, vector3List);
        }

        return;
    }

    public void Vector3DictionaryList(int index, int x, int y, int z) {
        vector3List = new List<Vector3>();

        if (vector3Dictionary.ContainsKey(index)) {
            vector3List = vector3Dictionary[index];
            vector3List.Add(new Vector3(x, y, z));
            vector3Dictionary[index] = vector3List;
        } else {
            vector3List.Add(new Vector3(x, y, z));
            vector3Dictionary.Add(index, vector3List);
        }

        return;
    }

    public void Vector3DictionaryList(int index, float x, float y, float z, bool replace) {
        if (replace) {
            vector3List = new List<Vector3>();

            vector3List.Add(new Vector3(x, y, z));
            vector3Dictionary[index] = vector3List;
        }

        return;
    }
}

public class IcosahedronGenerator : ScriptableObject {
    public Vector3Dictionary icosahedronPolygonDict;
    public Vector3Dictionary icosahedronVerticeDict;
    public bool firstRun = true;

    public void GenBaseIcosahedron() {
        icosahedronPolygonDict = new Vector3Dictionary();
        icosahedronVerticeDict = new Vector3Dictionary();

        // An icosahedron has 12 vertices, and
        // since it's completely symmetrical the
        // formula for calculating them is kind of
        // symmetrical too:

        float t = (1.0f + Mathf.Sqrt(5.0f)) / 2.0f;

        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(-1, t, 0).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(1, t, 0).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(-1, -t, 0).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(1, -t, 0).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(0, -1, t).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(0, 1, t).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(0, -1, -t).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(0, 1, -t).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(t, 0, -1).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(t, 0, 1).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(-t, 0, -1).normalized);
        icosahedronVerticeDict.Vector3DictionaryList(0, new Vector3(-t, 0, 1).normalized);

        // And here's the formula for the 20 sides,
        // referencing the 12 vertices we just created.
        // Each side will be placed in it's own dictionary key.
        // The first number is the key/index, and the next 3 numbers reference the vertice index
        icosahedronPolygonDict.Vector3DictionaryList(0, 0, 11, 5);
        icosahedronPolygonDict.Vector3DictionaryList(1, 0, 5, 1);
        icosahedronPolygonDict.Vector3DictionaryList(2, 0, 1, 7);
        icosahedronPolygonDict.Vector3DictionaryList(3, 0, 7, 10);
        icosahedronPolygonDict.Vector3DictionaryList(4, 0, 10, 11);
        icosahedronPolygonDict.Vector3DictionaryList(5, 1, 5, 9);
        icosahedronPolygonDict.Vector3DictionaryList(6, 5, 11, 4);
        icosahedronPolygonDict.Vector3DictionaryList(7, 11, 10, 2);
        icosahedronPolygonDict.Vector3DictionaryList(8, 10, 7, 6);
        icosahedronPolygonDict.Vector3DictionaryList(9, 7, 1, 8);
        icosahedronPolygonDict.Vector3DictionaryList(10, 3, 9, 4);
        icosahedronPolygonDict.Vector3DictionaryList(11, 3, 4, 2);
        icosahedronPolygonDict.Vector3DictionaryList(12, 3, 2, 6);
        icosahedronPolygonDict.Vector3DictionaryList(13, 3, 6, 8);
        icosahedronPolygonDict.Vector3DictionaryList(14, 3, 8, 9);
        icosahedronPolygonDict.Vector3DictionaryList(15, 4, 9, 5);
        icosahedronPolygonDict.Vector3DictionaryList(16, 2, 4, 11);
        icosahedronPolygonDict.Vector3DictionaryList(17, 6, 2, 10);
        icosahedronPolygonDict.Vector3DictionaryList(18, 8, 6, 7);
        icosahedronPolygonDict.Vector3DictionaryList(19, 9, 8, 1);

        return;
    }

    public void SeparateAllPolygons(){
        // Separates all polygons and vertex keys/indicies into their own key/index
        // For example if the numOfMainTriangles is set to 2,
        // This function will separate each polygon/triangle into it's own index
        // By looping through all polygons in each dictionary key/index

        List<Vector3> originalPolygons = new List<Vector3>();
        List<Vector3> originalVertices = new List<Vector3>();
        List<Vector3> newVertices = new List<Vector3>();
        Vector3Dictionary tempIcosahedronPolygonDict = new Vector3Dictionary();
        Vector3Dictionary tempIcosahedronVerticeDict = new Vector3Dictionary();

        // Cycles through the polygon list
        for (int i = 0; i < icosahedronPolygonDict.vector3Dictionary.Count; i++) {
            originalPolygons = new List<Vector3>();
            originalVertices = new List<Vector3>();

            // Loads all the polygons in a certain index/key
            originalPolygons = icosahedronPolygonDict.vector3Dictionary[i];

            // Since the original script was set up without a dictionary index
            // It was easier to loop all the original triangle vertices into index 0
            // Thus the first time this function runs, all initial vertices will be 
            // redistributed to the correct indicies/index/key

            if (firstRun) {
                originalVertices = icosahedronVerticeDict.vector3Dictionary[0];
            } else {
                // i - 1 to account for the first iteration of pre-set vertices
                originalVertices = icosahedronVerticeDict.vector3Dictionary[i];
            }

            // Loops through all the polygons in a specific Dictionary key/index
            for (int a = 0; a < originalPolygons.Count; a++){
                newVertices = new List<Vector3>();

                int x = (int)originalPolygons[a].x;
                int y = (int)originalPolygons[a].y;
                int z = (int)originalPolygons[a].z;

                // Adds three vertices/transforms for each polygon in the list
                newVertices.Add(originalVertices[x]);
                newVertices.Add(originalVertices[y]);
                newVertices.Add(originalVertices[z]);

                // Overwrites the Polygon indices from their original locations
                // index (20,11,5) for example would become (0,1,2) to correspond to the
                // three new Vector3's added to the list.
                // In the case of this function there will only be 3 Vector3's associated to each dictionary key
                tempIcosahedronPolygonDict.Vector3DictionaryList(0, 1, 2);

                // sets the index to the size of the temp dictionary list
                int tempIndex = tempIcosahedronPolygonDict.vector3Dictionary.Count;
                // adds the new vertices to the corresponding same key in the vertice index
                // which corresponds to the same key/index as the polygon dictionary
                tempIcosahedronVerticeDict.Vector3DictionaryList(tempIndex - 1, newVertices, true);
            }
        }
        firstRun = !firstRun;

        // Sets the temp dictionarys as the main dictionaries
        icosahedronVerticeDict = tempIcosahedronVerticeDict;
        icosahedronPolygonDict = tempIcosahedronPolygonDict;
    }

    public void Subdivide(int recursions) {
        // Divides each triangle into 4 triangles, and replaces the Dictionary entry

        var midPointCache = new Dictionary<int, int>();
        int polyDictIndex = 0;
        List<Vector3> originalPolygons = new List<Vector3>();
        List<Vector3> newPolygons;

        for (int x = 0; x < recursions; x++) {
            polyDictIndex = icosahedronPolygonDict.vector3Dictionary.Count;
            for (int i = 0; i < polyDictIndex; i++) {
                newPolygons = new List<Vector3>();
                midPointCache = new Dictionary<int, int>();
                originalPolygons = icosahedronPolygonDict.vector3Dictionary[i];

                for (int z = 0; z < originalPolygons.Count; z++) {
                    int a = (int)originalPolygons[z].x;
                    int b = (int)originalPolygons[z].y;
                    int c = (int)originalPolygons[z].z;

                    // Use GetMidPointIndex to either create a
                    // new vertex between two old vertices, or
                    // find the one that was already created.
                    int ab = GetMidPointIndex(i,midPointCache, a, b);
                    int bc = GetMidPointIndex(i,midPointCache, b, c);
                    int ca = GetMidPointIndex(i,midPointCache, c, a);

                    // Create the four new polygons using our original
                    // three vertices, and the three new midpoints.
                    newPolygons.Add(new Vector3(a, ab, ca));
                    newPolygons.Add(new Vector3(b, bc, ab));
                    newPolygons.Add(new Vector3(c, ca, bc));
                    newPolygons.Add(new Vector3(ab, bc, ca));
                }
                // Replace all our old polygons with the new set of
                // subdivided ones.
                icosahedronPolygonDict.vector3Dictionary[i] = newPolygons;
            }
        }
        return;
    }

    int GetMidPointIndex(int polyIndex, Dictionary<int, int> cache, int indexA, int indexB) {
        // We create a key out of the two original indices
        // by storing the smaller index in the upper two bytes
        // of an integer, and the larger index in the lower two
        // bytes. By sorting them according to whichever is smaller
        // we ensure that this function returns the same result
        // whether you call
        // GetMidPointIndex(cache, 5, 9)
        // or...
        // GetMidPointIndex(cache, 9, 5)

        int smallerIndex = Mathf.Min(indexA, indexB);
        int greaterIndex = Mathf.Max(indexA, indexB);
        int key = (smallerIndex << 16) + greaterIndex;

        // If a midpoint is already defined, just return it.
        int ret;
        if (cache.TryGetValue(key, out ret))
            return ret;

        // If we're here, it's because a midpoint for these two
        // vertices hasn't been created yet. Let's do that now!
        List<Vector3> tempVertList = icosahedronVerticeDict.vector3Dictionary[polyIndex];

        Vector3 p1 = tempVertList[indexA];
        Vector3 p2 = tempVertList[indexB];
        Vector3 middle = Vector3.Lerp(p1, p2, 0.5f).normalized;

        ret = tempVertList.Count;
        tempVertList.Add(middle);
        icosahedronVerticeDict.vector3Dictionary[polyIndex] = tempVertList;

        cache.Add(key, ret);
        return ret;
    }

    public void CalculateMesh(GameObject icosahedron, int numOfMainTriangles, int numOfSubdivisionsWithinEachTriangle, bool saveIcosahedron) {
        GameObject meshChunk;
        List<Vector3> meshPolyList;
        List<Vector3> meshVertList;
        List<int> triList;

        CreateFolders(numOfMainTriangles, numOfSubdivisionsWithinEachTriangle);
        CreateMaterial();

        // Loads a material from the Assets/Resources/ folder so that it can be saved with the prefab later
        Material material = Resources.Load("BlankSphere", typeof(Material)) as Material;

        int polyDictIndex = icosahedronPolygonDict.vector3Dictionary.Count;

        // Used to assign the child objects as well as to be saved as the .prefab
        // Sets the name
        icosahedron.gameObject.name = "Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle;

        for (int i = 0; i < polyDictIndex; i++) {
            meshPolyList = new List<Vector3>();
            meshVertList = new List<Vector3>();
            triList = new List<int>();
            // Assigns the polygon and vertex indices
            meshPolyList = icosahedronPolygonDict.vector3Dictionary[i];
            meshVertList = icosahedronVerticeDict.vector3Dictionary[i];

            // Sets the child gameobject parameters
            meshChunk = new GameObject("MeshChunk");
            meshChunk.transform.parent = icosahedron.gameObject.transform;
            meshChunk.transform.localPosition = new Vector3(0, 0, 0);
            meshChunk.AddComponent<MeshFilter>();
            meshChunk.AddComponent<MeshRenderer>();
            meshChunk.GetComponent<MeshRenderer>().material = material;
            meshChunk.AddComponent<MeshCollider>();
            Mesh mesh = meshChunk.GetComponent<MeshFilter>().mesh;

            // Adds the triangles to the list
            for (int z = 0; z < meshPolyList.Count; z++) {
                triList.Add((int)meshPolyList[z].x);
                triList.Add((int)meshPolyList[z].y);
                triList.Add((int)meshPolyList[z].z);
            }

            mesh.vertices = meshVertList.ToArray();
            mesh.triangles = triList.ToArray();
            mesh.uv = new Vector2[meshVertList.Count];

            /*
            //Not Needed because all normals have been calculated already
            Vector3[] _normals = new Vector3[meshVertList.Count];
            for (int d = 0; d < _normals.Length; d++){
                _normals[d] = meshVertList[d].normalized;
            }
            mesh.normals = _normals;
            */

            mesh.normals = meshVertList.ToArray();

            mesh.RecalculateBounds();

            // Saves each chunk mesh to a specified folder
            // The folder must exist
            if (saveIcosahedron) {
                string sphereAssetName = "icosahedronChunk" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle + "_" + i + ".asset";
                AssetDatabase.CreateAsset(mesh, "Assets/Icosahedrons/Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle + "/" + sphereAssetName);
                AssetDatabase.SaveAssets();
            }
        }

        // Removes the script for the prefab save
        // Saves the prefab to a specified folder
        // The folder must exist
        if (saveIcosahedron) {
            DestroyImmediate(icosahedron.GetComponent<UnityIcosahedronGenerator>());
            PrefabUtility.CreatePrefab("Assets/Icosahedrons/Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle + "/Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle + ".prefab", icosahedron);
        }

        return;
    }

    void CreateFolders(int numOfMainTriangles, int numOfSubdivisionsWithinEachTriangle){
        // Creates the folders if they don't exist
        if (!AssetDatabase.IsValidFolder("Assets/Icosahedrons")) {
            AssetDatabase.CreateFolder("Assets", "Icosahedrons");
        }
        if (!AssetDatabase.IsValidFolder("Assets/Icosahedrons/Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle)) {
            AssetDatabase.CreateFolder("Assets/Icosahedrons", "Icosahedron" + numOfMainTriangles + "Recursion" + numOfSubdivisionsWithinEachTriangle);
        }
        if (!AssetDatabase.IsValidFolder("Assets/Resources")) {
            AssetDatabase.CreateFolder("Assets", "Resources");
        }

        return;
    }

    static void CreateMaterial() {
        if (Resources.Load("BlankSphere", typeof(Material)) == null) {
            // Create a simple material asset if one does not exist
            Material material = new Material(Shader.Find("Standard"));
            material.color = Color.blue;
            AssetDatabase.CreateAsset(material, "Assets/Resources/BlankSphere.mat");
        }

        return;
    }

    // Displays the Total Polygon/Triangle and Vertice Count
    public void DisplayVertAndPolygonCount(){
        List<Vector3> tempVertices;
        HashSet<Vector3> verticeHash = new HashSet<Vector3>();

        int polygonCount = 0;
        List<Vector3> tempPolygons;

        // Saves Vertices to a hashset to ensure no duplicate vertices are counted
        for (int a = 0; a < icosahedronVerticeDict.vector3Dictionary.Count; a++) {
            tempVertices = new List<Vector3>();
            tempVertices = icosahedronVerticeDict.vector3Dictionary[a];
            for (int b = 0; b < tempVertices.Count; b++) {
                verticeHash.Add(tempVertices[b]);
            }
        }

        for (int a = 0; a < icosahedronPolygonDict.vector3Dictionary.Count; a++) {
            tempPolygons = new List<Vector3>();
            tempPolygons = icosahedronPolygonDict.vector3Dictionary[a];
            for (int b = 0; b < tempPolygons.Count; b++) {
                polygonCount++;
            }
        }

        Debug.Log("Vertice Count: " + verticeHash.Count);
        Debug.Log("Polygon Count: " + polygonCount);

        return;
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.