Algoritma untuk membuat bola?


27

Adakah yang punya algoritma untuk membuat prosedur sphere dengan lajumlah garis lintang, lojumlah garis bujur, dan jari-jari r? Saya membutuhkannya untuk bekerja dengan Unity, jadi posisi vertex perlu didefinisikan dan kemudian, segitiga didefinisikan melalui indeks ( info lebih lanjut ).


EDIT

masukkan deskripsi gambar di sini

Saya berhasil membuat kode bekerja dalam kesatuan. Tapi saya pikir saya mungkin telah melakukan sesuatu yang salah. Ketika saya mengaktifkannya detailLevel, Yang perlu dilakukan adalah menambahkan lebih banyak simpul dan poligon tanpa memindahkannya. Apakah saya lupa sesuatu?


EDIT 2

masukkan deskripsi gambar di sini

Saya mencoba menskalakan mesh di sepanjang normalnya. Ini yang saya dapat. Saya pikir saya kehilangan sesuatu. Apakah saya seharusnya hanya skala normals tertentu?


1
Mengapa Anda tidak melihat bagaimana implementasi open source yang ada melakukannya? lihat bagaimana Three.js menggunakan jerat, misalnya.
brice

3
Sebagai catatan kecil: kecuali Anda harus melakukan lintang / bujur Anda hampir pasti tidak mau , karena segitiga yang Anda dapatkan akan jauh lebih jauh dari seragam daripada yang Anda dapatkan dengan metode lain. (Bandingkan segitiga di dekat kutub utara dengan yang di dekat khatulistiwa: Anda menggunakan jumlah segitiga yang sama untuk berkeliling satu garis lintang dalam kedua kasus, tetapi di dekat kutub yang garis lintangnya memiliki keliling sangat kecil sedangkan di khatulistiwa ini adalah lingkaran penuh bola dunia Anda.) Teknik seperti yang ada dalam jawaban David Lively umumnya jauh lebih baik.
Steven Stadnicki

1
Anda tidak menormalkan posisi vertex setelah pengelompokan. Saya tidak memasukkan bagian itu dalam contoh saya. Normalisasi membuat mereka semua berjarak sama dari pusat, yang menciptakan perkiraan kurva yang Anda cari.
3Dave

Pikirkan menggembungkan balon di pusat icosahedron. Ketika balon mendorong jala kita, itu cocok dengan bentuk balon (bola).
3Dave

4
"Normalisasi" berarti menetapkan panjang vektor ke 1. Anda perlu melakukan sesuatu seperti vertices[i] = normalize(vertices[i]). Kebetulan, ini juga memberi Anda yang baru, normals yang benar, sehingga Anda harus melakukannya normals[i] = vertices[i]sesudahnya.
sam hocevar

Jawaban:


31

Untuk mendapatkan sesuatu seperti ini:

masukkan deskripsi gambar di sini

Buat icosahedron (solid 20-sisi biasa) dan bagi wajah-wajah untuk mendapatkan bola (lihat kode di bawah).

Idenya pada dasarnya:

  • Buat n-hedron biasa (padatan di mana setiap wajah memiliki ukuran yang sama). Saya menggunakan icosahedron karena merupakan padatan dengan jumlah wajah terbesar di mana setiap wajah memiliki ukuran yang sama. (Ada bukti untuk itu di suatu tempat di luar sana. Jangan ragu untuk Google jika Anda benar-benar ingin tahu.) Ini akan memberi Anda sebuah bola di mana hampir setiap wajah memiliki ukuran yang sama, membuat tekstur menjadi sedikit lebih mudah.

masukkan deskripsi gambar di sini

  • Bagi setiap wajah menjadi empat wajah berukuran sama. Setiap kali Anda melakukan ini, itu akan melipatgandakan jumlah wajah dalam model.

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

i0,, i1dan i2merupakan simpul dari segitiga asli. (Sebenarnya, indeks ke buffer vertex, tapi itu topik lain). m01adalah titik tengah dari tepi (i0,i1), m12 adalah titik tengah dari tepi (i1,12), dan m02, jelas, adalah titik tengah dari tepi (i0,i2).

Setiap kali Anda membagi wajah, pastikan Anda tidak membuat simpul rangkap. Setiap titik tengah akan dibagikan oleh satu sumber wajah lainnya (karena ujungnya dibagi di antara wajah). Kode di bawah ini menjelaskan hal itu dengan memelihara kamus titik tengah bernama yang telah dibuat, dan mengembalikan indeks titik tengah yang sebelumnya dibuat saat tersedia daripada membuat yang baru.

  • Ulangi sampai Anda mencapai jumlah wajah yang diinginkan untuk kubus Anda.

  • Setelah selesai, normalkan semua simpul untuk menghaluskan permukaan. Jika Anda tidak melakukan ini, Anda hanya akan mendapatkan icosahedron beresolusi tinggi daripada bola.

  • Voila! Kamu sudah selesai. Ubah vektor dan indeks buffer yang dihasilkan menjadi VertexBufferdan IndexBuffer, dan menggambar dengan Device.DrawIndexedPrimitives().

Inilah yang akan Anda gunakan di kelas "Sphere" Anda untuk membuat model (tipe data XNA dan C #, tetapi harus cukup jelas):

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

Dan GeometryProviderkelasnya

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


    }



}

Jawaban yang bagus Terima kasih. Saya tidak tahu tetapi apakah ini kode kesatuan? Oh, dan lat / long tidak masalah, selama saya dapat mengatur resolusi.
Daniel Pendergast

Ini bukan Unity (XNA) tetapi itu akan memberi Anda koordinat titik dan daftar indeks. Ganti Vector3 dengan apa pun yang setara dengan Unity. Anda mengatur resolusi dengan menyesuaikan jumlah iterasi Subdivide. Setiap loop mengalikan jumlah wajah dengan 4. iterasi 2 atau 3 akan memberikan bola yang bagus.
3Dave

Ah saya mengerti. Ini hampir identik dengan Unity C #. Hanya beberapa pertanyaan ... Mengapa ketika indeks ditentukan, Anda menempatkannya di dalam intarray? Dan apa fungsinya .Select(i => i + vertices.Count)?
Daniel Pendergast

Sama .Select(i => i + vertices.Count)sekali tidak bekerja untuk saya. Apakah ini fitur XNA saja?
Daniel Pendergast

1
Pastikan Anda menyertakan 'using System.Linq' seperti yang ditentukan. Pilih, dll.
3Dave

5

Mari kita perhatikan definisi parametrik dari bola:

definisi parametrik dari bola

di mana theta dan phi adalah dua sudut yang bertambah, yang akan kita sebut sebagai var tdan var udan Rx, Ry dan Rz adalah jari-jari independen (jari-jari) di ketiga arah kartesius, yang, dalam kasus bola, akan didefinisikan sebagai satu tunggal jari-jari var rad.

Mari kita perhatikan fakta bahwa ...simbol menunjukkan iterasi yang mengisyaratkan penggunaan loop. Konsep stacksdan rowsadalah "berapa kali Anda akan mengulanginya". Karena setiap iterasi menambahkan nilai t atau u, semakin banyak iterasi, semakin kecil nilainya, oleh karena itu semakin tepat kelengkungan bola.

The 'lingkup menggambar' prasyarat fungsi adalah untuk memiliki parameter yang diberikan sebagai berikut: int latitudes, int longitudes, float radius. Kondisi posting (output) adalah untuk kembali, atau menerapkan simpul yang dihitung. Bergantung pada bagaimana Anda ingin menggunakan ini, fungsi tersebut dapat mengembalikan array vector3(vektor tiga dimensi) atau, jika Anda menggunakan semacam OpenGL sederhana, sebelum versi 2.0, Anda mungkin ingin menerapkan simpul ke konteks secara langsung.

NB Menerapkan simpul dalam openGL memanggil fungsi berikut glVertex3f(x, y, z). Dalam kasus di mana kami akan menyimpan simpul, kami akan menambahkan yang baru vector3(x, y, z)untuk penyimpanan mudah.

Juga, cara Anda meminta sistem garis lintang dan bujur untuk bekerja membutuhkan penyesuaian pada definisi bola (pada dasarnya beralih z dan y), tetapi ini hanya menunjukkan bahwa definisi tersebut sangat mudah ditempa, dan bahwa Anda bebas untuk beralih di sekitar x, y dan z parameter untuk mengubah arah di mana bola ditarik (di mana lintang dan bujur).

Sekarang mari kita lihat bagaimana kita akan melakukan garis lintang dan bujur. Lintang diwakili oleh variabel u, mereka beralih dari 0 hingga 2 to radian (360 derajat). Karena itu kami dapat mengkode iterasinya seperti:

float latitude_increment = 360.0f / latitudes;

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

Sekarang bujur diwakili oleh variabel tdan beralih dari 0 ke π (180 derajat). oleh karena itu kode berikut terlihat mirip dengan yang sebelumnya:

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

(Perhatikan bahwa loop termasuk dalam kondisi terminal, karena interval untuk integrasi parametrik adalah dari 0 hingga 2π Termasuk . Anda akan mendapatkan bola parsial jika kondisi Anda tidak inklusif.)

Sekarang, mengikuti definisi sederhana dari sphere kita dapat memperoleh definisi variabel sebagai berikut (asumsikan 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)));

Satu lagi peringatan penting! Dalam kebanyakan kasus Anda akan menggunakan beberapa bentuk OpenGL, dan bahkan jika tidak, Anda mungkin masih perlu melakukan ini. Objek dalam tiga dimensi perlu beberapa simpul untuk didefinisikan. Ini umumnya dicapai dengan menyediakan simpul berikutnya yang dapat dihitung.

bagaimana banyak simpul digunakan untuk menentukan bentuk (primitif)

Hanya seperti pada gambar di atas koordinat yang berbeda x+∂dan y+∂, kita dapat dengan mudah menghasilkan tiga simpul lainnya untuk penggunaan yang diinginkan. Verteks lainnya adalah (menganggap 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)));

Akhirnya, di sini adalah fungsi kerja penuh yang akan mengembalikan semua simpul bola, dan yang kedua menunjukkan implementasi OpenGL yang berfungsi dari kode (ini adalah sintaks gaya-C dan bukan JavaScript, ini harus bekerja dengan semua bahasa gaya-C, termasuk C # saat menggunakan 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;

}

Kode 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 Anda mungkin telah memperhatikan pernyataan ini rad = radius;. Ini memungkinkan jari-jari untuk dimodifikasi dalam loop, berdasarkan lokasi atau sudut. Ini berarti Anda dapat menerapkan noise ke bola untuk mengeraskannya, membuatnya terlihat lebih alami jika efek yang diinginkan adalah seperti planet. Misalnyafloat rad = radius * noise[x][y][z];

Claude-Henry.


Baris `float z = (float) (rad * Math.sin (Math.toRadians (t)) * Math.cos (Math.toRadians (u)));` tidak benar. Anda sudah menghitung X, Y dengan sisi miring dari rad. Sekarang Anda membuat satu kaki segitiga, dan menyiratkan bahwa sisi miring dari segitiga tersebut juga rad. Ini secara efektif memberi Anda jari-jari rad * sqrt(2).
3Dave

@DavidLively terima kasih untuk menunjukkannya, saya menulis ini beberapa waktu lalu jadi saya tidak terkejut jika itu buruk atau bahkan salah.
claudehenry

itu selalu menyenangkan ketika saya menemukan kesalahan di salah satu posting SAYA dari tahun lalu. Itu terjadi. :)
3Dave

4

Saya membuat sesuatu seperti ini beberapa waktu lalu untuk membuat bulatan kubus, untuk kesenangan dan sains. Itu tidak terlalu sulit. Pada dasarnya, Anda mengambil fungsi yang membuat lingkaran simpul, kemudian melangkah melalui kenaikan tinggi yang ingin Anda buat lingkaran pada setiap ketinggian pada radius yang diperlukan untuk membuat bola. Di sini saya telah memodifikasi kodenya menjadi bukan untuk kubus:

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

Sekarang kode ini hanya akan membuat titik untuk garis lintang. Namun, Anda hampir dapat menggunakan kode yang sama untuk membuat garis bujur. Kecuali Anda harus memutar di antara setiap iterasi dan membuat lingkaran penuh di setiap iterasi degreeStep.

Maaf ini bukan jawaban lengkap atau khusus untuk Unity, tapi semoga ini akan membantu Anda memulai.


Ini cukup bagus jika Anda membutuhkan bola lat / panjang, tetapi Anda bisa menyederhanakannya sedikit dengan bekerja dalam koordinat bola hingga langkah terakhir.
3Dave

1
Terima kasih @vid. Saya setuju, jika saya menulis versi menggunakan koordis bola, saya akan mempostingnya di sini.
MichaelHouse

3

Tidak bisakah Anda memulai dengan bentuk sederhana, bisa berupa kotak dengan jarak r dari pusat ke sudut. Untuk membuat bola yang lebih detail, bagi semua poligon dan pindahkan simpul ke jarak dari pusat, buat vektor melewati posisinya saat ini.

Terus ulangi sampai cukup bulat untuk selera Anda.


Ini pada dasarnya sama dengan pendekatan icosahedral, hanya dengan bentuk awal yang berbeda. Satu keuntungan dari memulai dengan sebuah kubus yang menurut saya tidak disebutkan: secara substansial lebih mudah untuk membangun peta UV yang layak karena Anda dapat menggunakan cubemap dan tahu bahwa jahitan tekstur Anda akan sejajar sempurna dengan tepi-tepi pada jaring bola Anda.
Steven Stadnicki

@StevenStadnicki satu-satunya masalah yang saya miliki dengan kubus adalah bahwa wajah-wajah cenderung menjadi ukuran yang sangat berbeda setelah beberapa subdivison.
3Dave

@DavidLively Itu sangat tergantung pada bagaimana Anda membagi - jika Anda memotong wajah kuadrat dari kubus Anda ke dalam sebuah grid genap dan kemudian memproyeksikan ke luar / menormalkan maka itu benar, tetapi jika Anda memasang wajah Anda secara tidak seragam maka Anda benar-benar dapat membuat proyeksi ditempatkan secara merata di sepanjang lengkungan tepi; yang ternyata bekerja dengan cukup baik.
Steven Stadnicki

@StevenStadnicki bagus!
3Dave

@EricJohansson btw, sebagai guru saya merasa harus menyebutkan bahwa ini adalah wawasan yang cukup signifikan bagi seseorang yang tampaknya belum melihat metode pembagian sebelumnya. Anda telah memperbarui keyakinan saya pada kemanusiaan selama 12 jam ke depan.
3Dave

2

Apakah Anda benar-benar membutuhkan geometri 3D atau hanya bentuknya?

Anda bisa membuat bola 'palsu' menggunakan quad tunggal. Letakkan lingkaran di atasnya dan beri warna dengan benar. Ini memiliki keuntungan bahwa resolusi yang dibutuhkan akan tepat terlepas dari jarak ke kamera atau resolusi.

Ada tutorial di sini .


1
Retas yang bagus, tetapi gagal jika Anda perlu teksturnya.
3Dave

@DavidLively Harus dimungkinkan untuk menghitung koordinat tekstur per-pixel berdasarkan rotasi kecuali jika Anda perlu tekstur poligon secara individual.
David C. Bishop

@DavidCBishop Anda harus memperhitungkan "pelensaan" dari permukaan - texel coords ditekan dekat dengan batas lingkaran karena perspektif - pada titik mana Anda memalsukan rotasi. Juga, itu melibatkan memindahkan lebih banyak pekerjaan ke pixel shader yang dapat dilakukan di vertex shader (dan kita semua tahu bahwa VS jauh lebih murah!).
3Dave

0

di sini adalah beberapa kode untuk sejumlah titik yang sama spasi dari bola, seperti kulit jeruk yang melilitkan garis titik-titik di sekitar bola dalam spiral. setelah itu, bagaimana Anda bergabung dengan simpul terserah Anda. Anda dapat menggunakan titik-titik tetangga dalam lingkaran sebagai 2 dari setiap segitiga dan kemudian menemukan yang ketiga akan menjadi satu putaran proporsional di sekitar bola lebih tinggi atau lebih rendah ke bawah ... Anda juga dapat melakukan segitiga dengan loop dan tetangga terdekat di atasnya, apakah seseorang tahu cara yang lebih baik?

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

Meskipun David benar-benar benar dalam jawabannya, saya ingin menawarkan perspektif yang berbeda.

Untuk tugas saya untuk menghasilkan konten prosedural, saya melihat (antara lain) icosahedron versus lebih banyak bidang yang dibagi secara tradisional. Lihatlah bola-bola yang dihasilkan secara prosedural ini:

Bola yang luar biasa

Keduanya terlihat seperti bola yang benar-benar valid, bukan? Baiklah, mari kita lihat wireframes mereka:

Wah itu padat

Wow, apa yang terjadi di sana? Versi gambar rangka dari bola kedua sangat padat sehingga terlihat bertekstur! Saya akan memberitahu Anda rahasia: versi kedua adalah icosahedron. Itu adalah bola yang hampir sempurna, tetapi harganya mahal.

Sphere 1 menggunakan 31 subdivisi pada sumbu x dan 31 subdivisi pada sumbu z, dengan total 3.844 permukaan.

Sphere 2 menggunakan 5 subdivisi rekursif, dengan total 109.220 wajah.

Tapi oke, itu tidak terlalu adil. Mari turunkan kualitasnya secara signifikan:

Kental

Sphere 1 menggunakan 5 subdivisi pada sumbu x dan 5 subdivisi pada sumbu z, dengan total 100 wajah.

Sphere 2 menggunakan 0 subdivisi rekursif, dengan total 100 wajah.

Mereka menggunakan jumlah wajah yang sama, tetapi menurut saya, bola di sebelah kiri terlihat lebih baik. Itu terlihat kurang kental dan lebih bulat. Mari kita lihat berapa banyak wajah yang kita hasilkan dengan kedua metode ini.

Icosahedron:

  • Level 0 - 100 wajah
  • Level 1 - 420 wajah
  • Level 2 - 1.700 wajah
  • Level 3 - 6.820 wajah
  • Level 4 - 27.300 wajah
  • Level 5 - 109.220 wajah

Lingkungan terbagi:

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

Seperti yang Anda lihat, icosahedron meningkat di wajah pada tingkat eksponensial, ke kekuatan ketiga! Itu karena untuk setiap segitiga, kita harus membaginya menjadi tiga segitiga baru.

Yang benar adalah: Anda tidak perlu presisi icosahedron akan memberi Anda. Karena mereka berdua menyembunyikan masalah yang jauh lebih sulit: tekstur pesawat 2D pada bola 3D. Seperti apa bentuk puncaknya:

Top menyebalkan

Di kiri atas, Anda dapat melihat tekstur yang digunakan. Secara kebetulan, ini juga dihasilkan secara prosedural. (Hei, itu kursus generasi prosedural, kan?)

Terlihat mengerikan, bukan? Nah, ini sama baiknya dengan yang akan didapat. Saya mendapat nilai tertinggi untuk pemetaan tekstur saya, karena kebanyakan orang bahkan tidak melakukannya dengan benar.

Jadi tolong, pertimbangkan untuk menggunakan cosinus dan sinus untuk menghasilkan bola. Ini menghasilkan wajah yang jauh lebih sedikit untuk jumlah detail yang sama.


6
Saya khawatir saya hanya bisa mengundurkan diri dari hal ini. Timbangan esosfer secara eksponensial? Itu hanya karena Anda memutuskan bahwa Anda harus mengukur secara eksponensial. Bola UV menghasilkan wajah yang lebih sedikit daripada lapisan es dengan jumlah detail yang sama? Itu salah, benar-benar salah, benar-benar mundur.
sam hocevar

4
Subdivisi tidak perlu bersifat rekursif. Anda dapat membagi tepi segitiga menjadi bagian yang sama banyaknya dengan yang Anda inginkan. Menggunakan Nkomponen akan memberi Anda N*Nsegitiga baru, yang kuadratik, persis seperti apa yang Anda lakukan dengan bola-UV.
sam hocevar

6
Saya juga harus menambahkan bahwa bola yang Anda katakan terlihat "kurang kental dan lebih bulat" dilihat dari sudut terbaik, membuat klaim itu juga tidak jujur. Lakukan saja tangkapan layar yang sama dengan bola yang dilihat dari atas untuk melihat apa yang saya maksud.
sam hocevar

4
Juga, nomor icosahedron Anda tidak terlihat benar. Level 0 adalah 20 wajah (menurut definisi), kemudian 80, 320, 1280, dll. Anda dapat membagi dalam jumlah dan pola apa pun yang Anda inginkan. Kelancaran model pada akhirnya akan ditentukan oleh jumlah dan distribusi wajah pada hasil akhir (terlepas dari metode yang digunakan untuk menghasilkan mereka), dan kami ingin menjaga ukuran masing-masing wajah serata mungkin (tidak ada kutub meremas) untuk mempertahankan profil yang konsisten terlepas dari sudut pandang. Tambahkan ke fakta bahwa kode subdivisi jauh lebih sederhana (imho) ...
3Dave

2
Beberapa pekerjaan telah dimasukkan ke dalam jawaban ini, yang membuat saya merasa sedikit tidak enak dengan menurunkannya. Tetapi ini sepenuhnya dan sangat salah, jadi saya harus melakukannya. Icosphere yang tampak bulat sempurna yang memenuhi seluruh layar dalam FullHD membutuhkan 5 subdivisi, dengan icosahedron dasar yang tidak memiliki subdivisi. Sebuah icosahedron tanpa subdivisi tidak memiliki 100 wajah, ia memiliki 20. Icosa = 20. Itu namanya! Setiap subdivisi mengalikan jumlah wajah dengan 4, jadi 1-> 80, 2-> 320, 3-> 1280, 4-> 5120, 5-> 20,480. Dengan geosfer, kita membutuhkan setidaknya 40.000 wajah untuk mendapatkan bola bundar yang sama.
Peter - Unban Robert Harvey

-1

Script di bawah ini akan membuat Icosahedron dengan n Poligon ... basis 12. Ini juga akan membagi poligon menjadi jerat yang terpisah, dan menghitung total duplikat-duplikat dan poligon.

Saya tidak dapat menemukan yang serupa sehingga saya membuat ini. Cukup lampirkan skrip ke GameObject, dan atur subdivisi di Editor. Bekerja pada modifikasi noise selanjutnya.


/* 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;
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.