Bagaimana saya bisa menerapkan renderer yang dapat menarik banyak jenis primitif?

8

Ini agak terkait dengan pertanyaan yang sebelumnya saya tanyakan mengenai gambar primitif yang diindeks .

Masalah saya adalah saya hanya menggambar satu kubus ketika saya ingin menggambar banyak. Saya diberitahu bahwa masalahnya adalah bahwa saya menimpa buffer titik dan indeks dengan setiap instantiasi baru Cubedan bahwa alih-alih saya harus membuat satu di titik asal dan kemudian menggambar banyak, melewati matriks transformasi ke shader yang membuatnya tampak berbeda. tempat Ini bekerja dengan baik.

Saya sekarang memiliki masalah baru: bagaimana saya menggambar berbagai tipe primitif?

Ini kode saya dari pertanyaan sebelumnya:

Cube::Cube(D3DXCOLOR colour, D3DXVECTOR3 min, D3DXVECTOR3 max)
{
// create eight vertices to represent the corners of the cube
VERTEX OurVertices[] =
{
    {D3DXVECTOR3(min.x, max.y, max.z), colour},
    {D3DXVECTOR3(min.x, max.y, min.z), colour},
    {D3DXVECTOR3(min.x, min.y, max.z), colour},
    {min, colour},
    {max, colour},
    {D3DXVECTOR3(max.x, max.y, min.z), colour},
    {D3DXVECTOR3(max.x, min.y, max.z), colour},
    {D3DXVECTOR3(max.x, min.y, min.z), colour},
};

// create the vertex buffer
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(VERTEX) * 8;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;

device->CreateBuffer(&bd, NULL, &pBuffer);

void* pVoid;    // the void pointer

pBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pVoid);    // map the vertex buffer
memcpy(pVoid, OurVertices, sizeof(OurVertices));    // copy the vertices to the buffer
pBuffer->Unmap();

// create the index buffer out of DWORDs
DWORD OurIndices[] = 
{
    0, 1, 2,    // side 1
    2, 1, 3,
    4, 0, 6,    // side 2
    6, 0, 2,
    7, 5, 6,    // side 3
    6, 5, 4,
    3, 1, 7,    // side 4
    7, 1, 5,
    4, 5, 0,    // side 5
    0, 5, 1,
    3, 7, 2,    // side 6
    2, 7, 6,
};

// create the index buffer
// D3D10_BUFFER_DESC bd;    // redefinition
bd.Usage = D3D10_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(DWORD) * 36;
bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
bd.MiscFlags = 0;

device->CreateBuffer(&bd, NULL, &iBuffer);

iBuffer->Map(D3D10_MAP_WRITE_DISCARD, 0, &pVoid);    // map the index buffer
memcpy(pVoid, OurIndices, sizeof(OurIndices));    // copy the indices to the buffer
iBuffer->Unmap();

//this is simply a single call to the update method that sets up the scale, rotation
//and translation matrices, in case the cubes are static and you don't want to have to 
//call update every frame
Update(D3DXVECTOR3(1, 1, 1), D3DXVECTOR3(0, 0, 0), D3DXVECTOR3(0, 0, 0));
}

Jelas jika saya menggandakan dan memodifikasi kode menjadi objek atau bentuk yang berbeda, bentuk terakhir yang akan diinisialisasi akan menimpa buffer vertex, bukan?

Apakah saya menggunakan beberapa buffer vertex? Apakah saya menambahkan buffer vertex baru ke yang lama dan menggunakan indeks yang sesuai untuk menggambar mereka? Bisakah saya melakukan keduanya? Kedua?

SirYakalot
sumber

Jawaban:

12

Mungkin ide yang buruk untuk membuat kelas baru untuk setiap jenis geometri yang akan Anda dukung. Itu tidak terlalu scalable atau dipelihara. Selain itu, desain kelas yang tampaknya Anda inginkan sekarang tampaknya mengacaukan tugas-tugas mengelola geometri itu sendiri dan data instance untuk geometri itu.

Inilah pendekatan yang bisa Anda ambil:

Buat dua kelas, Meshdan MeshInstance. A Meshberisi semua properti dari geometri bersama - pada dasarnya penyangga titik dan penyangga indeks. Jika Anda mau, Anda bisa membuat fungsi pembantu yang membuat jerat yang berisi data titik kubus (atau data titik sudut, atau apa pun yang Anda suka). Anda harus menyesuaikan antarmuka publik Meshkelas untuk memungkinkan fungsi pembantu tersebut diimplementasikan sebagai fungsi non-anggota, non-teman.

MeshInstance, di sisi lain, harus dibangun dengan mengacu pada a Mesh. Ini MeshInstanceberisi properti dari objek individu - itu adalah transformasi dunia, dan penimpaan shader digunakan untuk membuatnya, dan sebagainya.

Dengan cara ini, saat Anda ingin membuat kubus baru, Anda pertama-tama mendapatkan Meshobjek yang mewakili kubus dari beberapa pustaka bersama objek mesh primitif yang Anda buat saat startup. Kemudian Anda membuat yang baru MeshInstance, menugaskannya kubus itu Mesh.

Saat Anda membuat, Anda membuat daftar semua yang MeshInstanceingin Anda gambar, dan mengirimkannya. Jika Anda mengelompokkannya berdasarkan Meshatau tekstur, Anda dapat mengoptimalkan untuk perubahan overhead kondisi (yaitu, Anda menggambar semua instance mesh yang sesuai dengan mesh kubus sekaligus, dan kemudian semua instance mesh yang sesuai dengan sphere mesh, sehingga Anda memiliki lebih sedikit SetVertexBufferpanggilan perangkat D3D). Anda juga dapat mengelompokkan berdasarkan negara lain, seperti tekstur dan shader.

Dengan cara ini, Anda menghindari pemborosan data vertex duplikasi memori, dan Anda memastikan bahwa sistem rendering Anda dapat menskala ke setiap set primitif hanya dengan menerapkan fungsi (a) baru untuk membuat jerat secara terprogram atau (b) fungsi untuk memuat jerat dari file dari format tertentu.

Setelah pipeline render Anda berfungsi dalam hal Meshobjek yang digeneralisasi , akan lebih mudah untuk mengadaptasinya secara global dengan teknik atau optimisasi baru.

Komentar spesifik:

Jelas jika saya menggandakan dan memodifikasi kode menjadi objek atau bentuk yang berbeda, bentuk terakhir yang akan diinisialisasi akan menimpa buffer vertex. bukan?

Tidak. Dalam kode yang Anda posting, satu-satunya cara pBufferdan sejenisnya akan ditimpa adalah jika itu adalah variabel anggota statis. Jika Anda menyalin kelas grosir untuk membuat (misalnya) Spherekelas, itu akan menjadi variabel statis baru. Ini masih ide yang buruk.

Apakah saya menggunakan beberapa buffer vertex? Apakah saya menambahkan buffer vertex baru ke yang lama dan menggunakan indeks yang sesuai untuk menggambar mereka? Bisakah saya melakukan keduanya? Kedua?

Implementasi naif dari teknik yang saya jelaskan di atas melibatkan beberapa buffer (satu untuk setiap set geometri bersama). Jika geometri itu statis, mungkin untuk menyimpan semuanya menjadi satu (atau beberapa, karena ada batas optimal praktis untuk ukuran buffer) buffer untuk meminimalkan perubahan status buffer lebih jauh. Itu harus dianggap sebagai optimasi dan dibiarkan sebagai latihan untuk pembaca; pertama membuatnya bekerja, lalu khawatir membuatnya cepat.


sumber
Saya tahu kita tidak seharusnya memposting komentar 'terima kasih', tetapi ini sangat membantu! Terima kasih!
SirYakalot
saat ini pBuffer dan iBuffer adalah eksternal. Haruskah saya menjadikan ini anggota instance dari setiap objek Mesh?
SirYakalot
1
Ya, itu tempat yang bagus untuk memulai.
Sekarang ketika saya datang untuk mengimplementasikan ini, saya mengalami sedikit kesulitan untuk berpikir tentang bagaimana melakukannya, hanya bagian di mana Anda mengatakan "Jika Anda ingin, Anda dapat membuat fungsi pembantu yang membuat jerat yang berisi data vertex kubus (atau data sphere vertex, atau apa pun yang Anda suka). Anda harus menyesuaikan antarmuka publik dari kelas Mesh untuk memungkinkan fungsi pembantu tersebut diimplementasikan sebagai fungsi non-anggota, non-teman. " apa sebenarnya yang Anda maksud dengan fungsi non-helper, non friend?
SirYakalot
Fungsi yang bukan anggota kelas (jadi fungsi global) yang juga tidak dinyatakan sebagai teman kelas. Fungsi "reguler", dengan kata lain - yang memanipulasi objek mesh hanya melalui API publiknya.