Algoritmo per la creazione di sfere?


27

Qualcuno ha un algoritmo per la creazione di una sfera proceduraly con laquantità di linee di latitudine, loquantità di linee di longitudine e un raggio di r? Ho bisogno che funzioni con Unity, quindi è necessario definire le posizioni dei vertici e quindi i triangoli definiti tramite gli indici ( maggiori informazioni ).


MODIFICARE

inserisci qui la descrizione dell'immagine

Sono riuscito a far funzionare il codice in unità. Ma penso che avrei potuto fare qualcosa di sbagliato. Quando alzo il detailLevel, Tutto ciò che fa è aggiungere più vertici e poligoni senza spostarli. Ho dimenticato qualcosa?


MODIFICA 2

inserisci qui la descrizione dell'immagine

Ho provato a ridimensionare la mesh lungo le sue normali. Questo è quello che ho ottenuto. Penso che mi manchi qualcosa. Devo ridimensionare solo alcune normali?


1
Perché non guardi come lo fanno le implementazioni open source esistenti? dai un'occhiata a come Three.js lo fa usando le mesh, per esempio.
brice,

3
Come piccola nota: a meno che tu non debba fare latitudine / longitudine quasi sicuramente non vuoi , perché i triangoli che ottieni saranno molto più lontani dall'uniforme di quelli che ottieni con altri metodi. (Confronta i triangoli vicino al polo nord con quelli vicino all'equatore: stai usando lo stesso numero di triangoli per aggirare una linea di latitudine in entrambi i casi, ma vicino al polo quella linea di latitudine ha una circonferenza molto piccola mentre all'equatore è la circonferenza completa del tuo globo.) Tecniche come quella nella risposta di David Lively sono generalmente molto migliori.
Steven Stadnicki,

1
Non stai normalizzando le posizioni dei vertici dopo la suddivisione. Non ho incluso quella parte nel mio esempio. La normalizzazione li rende tutti equidistanti dal centro, il che crea l'approssimazione della curva che stai cercando.
3Daveva il

Pensa di gonfiare un palloncino al centro dell'icosaedro. Quando il palloncino spinge la nostra maglia, corrisponde alla forma del palloncino (sfera).
3Daveva il

4
"Normalizzare" significa impostare la lunghezza di un vettore su 1. Devi fare qualcosa di simile vertices[i] = normalize(vertices[i]). Per inciso, questo ti dà anche le tue nuove normali corrette, quindi dovresti farlo in normals[i] = vertices[i]seguito.
sam hocevar,

Risposte:


31

Per ottenere qualcosa del genere:

inserisci qui la descrizione dell'immagine

Crea un icosaedro (solido regolare a 20 facce) e suddividi le facce per ottenere una sfera (vedi codice sotto).

L'idea è fondamentalmente:

  • Crea un n-edro normale (un solido in cui ogni faccia ha le stesse dimensioni). Uso un icosaedro perché è il solido con il maggior numero di facce in cui ogni faccia ha le stesse dimensioni. (C'è una prova per questo da qualche parte là fuori. Sentiti libero di Google se sei davvero curioso.) Questo ti darà una sfera in cui quasi ogni faccia ha le stesse dimensioni, rendendo le trame un po 'più facili.

inserisci qui la descrizione dell'immagine

  • Suddividere ciascuna faccia in quattro facce di uguali dimensioni. Ogni volta che lo fai, quadruplicherà il numero di facce nel modello.

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

i0, i1e i2sono i vertici del triangolo originale. (In realtà, indici nel buffer dei vertici, ma questo è un altro argomento). m01è il punto medio del bordo (i0,i1), m12 è il punto medio del bordo (i1,12)ed m02è, ovviamente, il punto medio del bordo (i0,i2).

Ogni volta che suddividi una faccia, assicurati di non creare vertici duplicati. Ogni punto medio sarà condiviso da un'altra faccia di origine (poiché i bordi sono condivisi tra le facce). Il codice seguente spiega ciò mantenendo un dizionario di punti medi nominati che sono stati creati e restituendo l'indice di un punto medio precedentemente creato quando è disponibile anziché crearne uno nuovo.

  • Ripeti fino a quando non hai raggiunto il numero desiderato di facce per il tuo cubo.

  • Al termine, normalizza tutti i vertici per appianare la superficie. Se non lo fai, otterrai solo un icosaedro ad alta risoluzione invece di una sfera.

  • Ecco! Hai finito. Convertire il vettore risultante e i buffer di indice in un VertexBuffere IndexBuffere disegnare con Device.DrawIndexedPrimitives().

Ecco cosa useresti nella classe "Sphere" per creare il modello (tipi di dati XNA e C #, ma dovrebbe essere abbastanza chiaro):

        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]);

E la GeometryProviderclasse

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) 
            }
        );


    }



}

Bella risposta. Grazie. Non posso dirlo ma è questo codice di unità? Oh, e lat / long non ha importanza, purché sia ​​possibile impostare la risoluzione.
Daniel Pendergast,

Non è Unity (XNA) ma ti darà le coordinate del vertice e l'elenco degli indici. Sostituisci Vector3 con qualsiasi equivalente di Unity. È possibile impostare la risoluzione regolando il numero di iterazioni di suddivisione. Ogni ciclo moltiplica il numero di facce per 4. 2 o 3 iterazioni daranno una bella sfera.
3Daveva il

Ah capisco È quasi identico a Unity C #. Solo alcune domande ... Perché quando gli indici sono definiti, li metti all'interno di un intarray? E cosa fa .Select(i => i + vertices.Count)?
Daniel Pendergast,

Non .Select(i => i + vertices.Count)funziona affatto per me. È solo una funzionalità XNA?
Daniel Pendergast,

1
Assicurati di includere 'using System.Linq' come definito.Seleziona, ecc.
3Dave

5

Consideriamo la definizione parametrica di una sfera:

definizione parametrica di una sfera

dove theta e phi sono due incremento angoli, che chiameremo come var te var ue Rx, Ry e Rz sono i raggi indipendenti (raggi) in tutte e tre le direzioni cartesiani, che, nel caso di una sfera, viene definito come un unico raggio var rad.

Consideriamo ora il fatto che il ...simbolo indica un'iterazione che suggerisce l'uso di un ciclo. Il concetto di stacksed rowsè "quante volte ripeterai". Poiché ogni iterazione aggiunge il valore di t, u più iterazioni, minore è il valore, quindi più precisa è la curvatura della sfera.

Il presupposto della funzione di 'disegno sfera' è quello di avere i seguenti dati parametri: int latitudes, int longitudes, float radius. Le condizioni di post (output) è di tornare o applicare i vertici calcolati. A seconda di come si intende utilizzarlo, la funzione potrebbe restituire una matrice di vector3(vettori tridimensionali) o, se si utilizza una sorta di OpenGL semplice, prima della versione 2.0, è possibile applicare direttamente i vertici al contesto.

NB L'applicazione di un vertice in openGL richiede la seguente funzione glVertex3f(x, y, z). Nel caso in cui memorizzassimo i vertici, aggiungeremmo un nuovo vector3(x, y, z)per una facile memorizzazione.

Inoltre, il modo in cui hai richiesto il funzionamento del sistema di latitudine e longitudine richiedeva un adattamento alla definizione della sfera (fondamentalmente cambiando zey), ma ciò dimostra che la definizione è molto malleabile e che sei libero di cambiare parametri x, ye z per modificare la direzione in cui viene disegnata la sfera (dove sono le latitudini e le longitudini).

Ora diamo un'occhiata a come faremo le latitudini e le lunghezze. Le latitudini sono rappresentate dalla variabile u, ripetono da 0 a 2π radianti (360 gradi). Possiamo quindi codificare la sua iterazione in questo modo:

float latitude_increment = 360.0f / latitudes;

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

Ora le lunghezze sono rappresentate dalla variabile te scorre da 0 a π (180 gradi). pertanto il seguente codice è simile al precedente:

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 ...
    }
}

(Si noti che i loop sono inclusivi delle condizioni terminali lì, perché l'intervallo per l'integrazione parametrica è compreso tra 0 e 2π inclusi. Otterrai una sfera parziale se le tue condizioni non sono inclusive.)

Ora, seguendo la semplice definizione della sfera, possiamo derivare la definizione della variabile come segue (supponiamo 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)));

Un altro avvertimento importante! Nella maggior parte dei casi utilizzerai una qualche forma di OpenGL e, anche se non così, potresti ancora aver bisogno di farlo. Un oggetto tridimensionale necessita di definire diversi vertici. Questo è generalmente ottenuto fornendo il prossimo vertice che è calcolabile.

come vengono usati più vertici per definire una forma (primitiva)

Come nella figura sopra sono le diverse coordinate x+∂e y+∂, possiamo facilmente generare altri tre vertici per qualsiasi uso desiderato. Gli altri vertici sono (assumono 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)));

Infine, ecco una funzione completa funzionante che restituirebbe tutti i vertici di una sfera e la seconda mostra un'implementazione OpenGL funzionante del codice (questa è sintassi in stile C e non JavaScript, dovrebbe funzionare con tutti i linguaggi in stile C, incluso C # quando si utilizza Unity).

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;

}

Codice 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.

}

PS Potresti aver notato questa affermazione rad = radius;. Ciò consente di modificare il raggio nel loop, in base alla posizione o all'angolo. Ciò significa che puoi applicare rumore alla sfera per irruvidirla, rendendola più naturale se l'effetto desiderato è simile a un pianeta. Per esempiofloat rad = radius * noise[x][y][z];

Claude-Henry.


La riga `float z = (float) (rad * Math.sin (Math.toRadians (t)) * Math.cos (Math.toRadians (u)));` non è corretta. Hai già calcolato una X, Y con un'ipotenusa di rad. Ora stai costruendo quella gamba di un triangolo e insinuando che anche l'ipotenusa di detto triangolo sia rad. Questo ti dà effettivamente un raggio di rad * sqrt(2).
3Dave il

@DavidLively grazie per averlo sottolineato, l'ho scritto qualche tempo fa, quindi non mi sorprende se è brutto o addirittura sbagliato.
Claudehenry,

è sempre divertente quando trovo un errore in uno dei MIEI post di anni fa. Succede. :)
3Dave il

4

Ho creato qualcosa del genere qualche tempo fa per creare una sfera di cubi, per divertimento e scienza. Non è troppo difficile. Fondamentalmente, si prende una funzione che crea un cerchio di vertici, quindi si passa attraverso gli incrementi di altezza desiderati creando cerchi ad ogni altezza nel raggio richiesto per creare una sfera. Qui ho modificato il codice per non essere per i cubi:

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));
}

Ora questo codice creerebbe solo punti per la latitudine. Tuttavia, puoi quasi usare lo stesso codice per creare le linee di longitudine. Tranne che dovrai ruotare tra ogni iterazione e creare un cerchio completo su ciascuna degreeStep.

Spiacenti, questa non è una risposta completa o specifica per Unity, ma spero che ti possa iniziare.


Questo è abbastanza buono se hai bisogno di una sfera lat / long, ma potresti semplificarla un po 'lavorando in coordinate sferiche fino all'ultimo passaggio.
3Daveva il

1
Grazie @ David. Sono d'accordo, se riesco a scrivere una versione usando i cordoni sferici, la posterò qui.
MichaelHouse

3

Non potresti semplicemente iniziare con una forma semplice, potrebbe essere una scatola con distanza r dal centro all'angolo. Per creare una sfera più dettagliata, suddividere tutti i poligoni e quindi spostare i vertici a una distanza r dal centro, facendo passare il vettore nella posizione corrente.

Continua a ripetere fino a quando non è abbastanza sferico per i tuoi gusti.


Questo è essenzialmente lo stesso dell'approccio icosaedrico, solo con una diversa forma iniziale. Un vantaggio di iniziare con un cubo che non credo sia stato menzionato: è sostanzialmente più semplice costruire mappe UV decenti perché puoi usare una mappa cubica e sapere che le cuciture della trama si allineeranno perfettamente con i bordi nella mesh della tua sfera.
Steven Stadnicki,

@StevenStadnicki l'unico problema che ho con i cubi è che le facce tendono a finire per essere di dimensioni molto diverse dopo alcune suddivisioni.
3Daveva il

@DavidLively Dipende molto da come si suddivide - se tagli le facce quadrate del tuo cubo in una griglia uniforme e poi proietti verso l'esterno / normalizzi, allora è vero, ma se griglia le tue facce in modo non uniforme, puoi effettivamente realizzare la proiezione deve essere uniformemente distanziata lungo gli archi dei bordi; che risulta funzionare abbastanza bene.
Steven Stadnicki,

@StevenStadnicki nifty!
3Daveva il

@EricJohansson a proposito, come insegnante mi sento costretto a menzionare che questa è una visione abbastanza significativa per qualcuno che apparentemente non ha mai visto il metodo di suddivisione prima. Hai rinnovato la mia fiducia nell'umanità per le prossime 12 ore.
3Daveva il

2

Hai davvero bisogno della geometria 3D o solo della forma?

Puoi creare una sfera "falsa" usando un solo quad. Basta mettere un cerchio su di esso e ombreggiarlo correttamente. Questo ha il vantaggio di avere esattamente la risoluzione richiesta indipendentemente dalla distanza dalla telecamera o dalla risoluzione.

C'è un tutorial qui .


1
Bello hack, ma fallisce se devi strutturarlo.
3Daveva il

@DavidLively Dovrebbe essere possibile calcolare le coordinate della trama per pixel in base alla sua rotazione, a meno che non sia necessario strutturare i poligoni singolarmente.
David C. Bishop

@DavidCBishop Dovresti tenere conto del "riflesso" della superficie - i cordoncini di texel sono schiacciati vicino al bordo del cerchio a causa della prospettiva - a quel punto stai falsificando la rotazione. Inoltre, ciò comporta lo spostamento di molto più lavoro nel pixel shader che potrebbe essere eseguito nel vertice shader (e sappiamo tutti che i VS sono molto più economici!).
3Daveva il

0

ecco un codice per un numero qualsiasi di vertici equidistanti di una sfera, è come una buccia d'arancia che avvolge una linea di punti attorno a una sfera in una spirale. successivamente, come ti unisci ai vertici dipende da te. puoi usare i punti vicini nel ciclo come 2 di ogni triangolo e poi scoprire che il terzo sarebbe un giro proporzionale attorno alla sfera più in alto o più in basso ... puoi anche fare triangoli per ciclo e il vicino più vicino su di esso, qualcuno conosci un modo migliore?

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

Sebbene David abbia assolutamente ragione nella sua risposta, voglio offrire una prospettiva diversa.

Per il mio incarico per la generazione di contenuti procedurali, ho esaminato (tra le altre cose) gli icosaedri rispetto alle sfere suddivise più tradizionali. Guarda queste sfere generate proceduralmente:

Sfere fantastiche

Entrambi sembrano sfere perfettamente valide, giusto? Bene, diamo un'occhiata ai loro wireframe:

Wow, è denso

Wow, cosa è successo lì? La versione wireframe della seconda sfera è così densa che sembra strutturata! Ti farò conoscere un segreto: la seconda versione è un icosaedro. È una sfera quasi perfetta, ma ha un prezzo elevato.

La sfera 1 utilizza 31 suddivisioni sull'asse x e 31 suddivisioni sull'asse z, per un totale di 3.844 facce.

Sphere 2 utilizza 5 suddivisioni ricorsive, per un totale di 109.220 volti.

Ma va bene, non è proprio giusto. Riduciamo notevolmente la qualità:

granuloso

La sfera 1 utilizza 5 suddivisioni sull'asse x e 5 suddivisioni sull'asse z, per un totale di 100 facce.

Sphere 2 utilizza 0 suddivisioni ricorsive, per un totale di 100 facce.

Usano la stessa quantità di facce, ma secondo me la sfera a sinistra sembra migliore. Sembra meno grumoso e molto più rotondo. Diamo un'occhiata a quante facce generiamo con entrambi i metodi.

icosaedro:

  • Livello 0 - 100 volti
  • Livello 1 - 420 facce
  • Livello 2 - 1.700 facce
  • Livello 3 - 6.820 volti
  • Livello 4 - 27.300 volti
  • Livello 5 - 109.220 facce

Sfera suddivisa:

  • YZ: 5 - 100 facce
  • YZ: 10 - 400 facce
  • YZ: 15-900 facce
  • YZ: 20 - 1.600 facce
  • YZ: 25 - 2.500 facce
  • YZ: 30 - 3.600 facce

Come puoi vedere, l'icosaedro aumenta in facce a una velocità esponenziale, fino a una terza potenza! Questo perché per ogni triangolo, dobbiamo suddividerli in tre nuovi triangoli.

La verità è che non hai bisogno della precisione che ti darà un icosaedro. Perché entrambi nascondono un problema molto più difficile: texturing un piano 2D su una sfera 3D. Ecco come appare il top:

Top fa schifo

In alto a sinistra, puoi vedere la trama in uso. Per coincidenza, viene anche generato proceduralmente. (Ehi, è stato un corso sulla generazione procedurale, giusto?)

Sembra terribile, vero? Bene, questo è buono come sarà. Ho ottenuto il massimo dei voti per la mia mappatura delle texture, perché la maggior parte delle persone non la capisce nemmeno così.

Quindi, per favore, considera l'uso di coseno e seno per generare una sfera. Genera molte meno facce per la stessa quantità di dettagli.


6
Temo di poter solo sottovalutare questo. L'icosfera si espande in modo esponenziale? Questo è solo perché hai deciso che il tuo dovrebbe ridimensionarsi in modo esponenziale. Una sfera UV genera meno volti di un'icosfera per la stessa quantità di dettagli? Questo è sbagliato, assolutamente sbagliato, totalmente all'indietro.
sam hocevar,

4
La suddivisione non deve essere ricorsiva. Puoi dividere il bordo di un triangolo in tutte le parti uguali che desideri. L'uso delle Nparti ti darà N*Nnuovi triangoli, che sono quadratici, esattamente come quello che fai con la sfera UV.
Sam Hocevar,

6
Devo anche aggiungere che la sfera che dici sembra "meno grumosa e molto più rotonda" è vista dalla migliore angolazione, rendendo anche questa affermazione disonesta. Basta fare lo stesso screenshot con le sfere viste dall'alto per vedere cosa intendo.
Sam Hocevar,

4
Inoltre, i tuoi numeri di icosaedro non sembrano corretti. Il livello 0 è di 20 facce (per definizione), quindi 80, 320, 1280, ecc. È possibile suddividere in qualsiasi numero e ogni modello desiderato. La levigatezza del modello sarà in definitiva determinata dal numero e dalla distribuzione delle facce nel risultato finale (indipendentemente dal metodo utilizzato per generarle) e vogliamo mantenere le dimensioni di ciascuna faccia il più uniforme possibile (nessuna polarità spremitura) per mantenere un profilo coerente indipendentemente dall'angolo di visualizzazione. Aggiungete a ciò il fatto che il codice di suddivisione è molto più semplice (imho) ...
3Dave

2
È stato inserito del lavoro in questa risposta, il che mi fa sentire un po 'in colpa nel ridimensionarlo. Ma è completamente e assolutamente sbagliato, quindi devo farlo. Un'Icosfera dall'aspetto perfettamente rotondo che riempie l'intero schermo in FullHD ha bisogno di 5 suddivisioni, con un icosaedro di base senza suddivisioni. Un icosaedro senza suddivisioni non ha 100 facce, ha 20. Icosa = 20. È il nome! Ogni suddivisione moltiplica il numero di facce per 4, quindi 1-> 80, 2-> 320, 3-> 1280, 4-> 5120, 5-> 20.480. Con una geosfera abbiamo bisogno di almeno 40'000 facce per ottenere una sfera ugualmente rotonda.
Peter - Unban Robert Harvey,

-1

Lo script seguente creerà un Icosaedro con n poligoni ... base 12. Suddividerà inoltre i poligoni in maglie separate e calcolerà i verte duplicati duplicati e poligoni.

Non sono riuscito a trovare nulla di simile, quindi l'ho creato. Basta collegare lo script a GameObject e impostare le suddivisioni nell'editor. Lavorando sulla modifica del rumore successivo.


/* 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;
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.