Bagaimana sebenarnya texturing virtual menjadi efisien?

27

Sebagai referensi, yang saya maksud adalah "nama generik" untuk teknik yang pertama kali (saya percaya) diperkenalkan dengan teknologi MegaTexture idTech 5 . Lihat videonya di sini untuk melihat sekilas cara kerjanya.

Saya telah membaca beberapa makalah dan publikasi yang berkaitan dengannya belakangan ini, dan yang saya tidak mengerti adalah bagaimana hal itu bisa menjadi efisien. Bukankah itu membutuhkan perhitungan ulang konstan koordinat UV dari ruang "halaman tekstur global" ke dalam koordinat tekstur virtual? Dan bagaimana itu tidak membatasi sebagian besar upaya batching geometri? Bagaimana ini memungkinkan pembesaran sewenang-wenang? Bukankah pada suatu saat memerlukan pengelompokan poligon?

Ada begitu banyak yang tidak saya mengerti, dan saya tidak dapat menemukan sumber daya yang sebenarnya mudah didekati tentang topik ini.

Llamageddon
sumber

Jawaban:

20

Ikhtisar

Alasan utama untuk Virtual Texturing (VT), atau Sparse Virtual Textures , seperti yang kadang-kadang disebut, adalah sebagai optimasi memori. Inti masalahnya adalah hanya memindahkan ke dalam memori video texels yang sebenarnya (digeneralisasi sebagai halaman / ubin) yang mungkin Anda perlukan untuk bingkai yang diberikan. Jadi itu akan memungkinkan Anda untuk memiliki lebih banyak data tekstur dalam penyimpanan offline atau lambat (HDD, Optical-Disk, Cloud) daripada yang bisa muat di memori video atau bahkan memori utama. Jika Anda memahami konsep Memori Virtual yang digunakan oleh Sistem Operasi modern, itu adalah hal yang sama pada intinya (nama tidak diberikan secara tidak sengaja).

VT tidak memerlukan penghitungan ulang UVs dalam arti bahwa Anda akan melakukan itu setiap frame sebelum rendering mesh, kemudian kirim kembali data vertex, tetapi itu memang memerlukan beberapa pekerjaan substansial dalam shader Vertex dan Fragmen untuk melakukan pencarian tidak langsung dari UV yang masuk. Namun dalam implementasi yang baik, itu harus benar-benar transparan untuk aplikasi jika menggunakan tekstur virtual atau tradisional. Sebenarnya, sebagian besar waktu aplikasi akan mencampur kedua jenis tekstur, virtual dan tradisional.

Secara teori, batch dapat bekerja dengan sangat baik, meskipun saya belum pernah melihat detailnya. Karena kriteria umum untuk pengelompokan geometri adalah tekstur, dan dengan VT, setiap poligon dalam adegan dapat berbagi tekstur "tak terhingga besar" yang sama, secara teoritis, Anda dapat mencapai gambar adegan penuh dengan 1 panggilan draw. Namun dalam kenyataannya, faktor-faktor lain ikut berperan membuat ini tidak praktis.

Masalah dengan VT

Memperbesar / memperkecil tampilan dan gerakan kamera secara mendadak adalah hal yang paling sulit untuk ditangani dalam pengaturan VT. Ini mungkin terlihat sangat menarik untuk adegan statis, tetapi begitu segala sesuatu mulai bergerak, lebih banyak halaman tekstur / ubin akan diminta daripada streaming untuk penyimpanan eksternal. File Async IO dan threading dapat membantu, tetapi jika itu adalah sistem waktu nyata, seperti dalam game, Anda hanya perlu membuat beberapa frame dengan ubin resolusi lebih rendah sampai yang hi-res tiba, setiap saat dan kemudian , menghasilkan tekstur buram. Tidak ada peluru perak di sini dan itu masalah terbesar dengan tekniknya, IMO.

Texturing Virtual juga tidak menangani transparansi dengan cara yang mudah, sehingga poligon transparan membutuhkan jalur render tradisional yang terpisah untuk mereka.

Semua dalam semua, VT menarik, tetapi saya tidak akan merekomendasikannya untuk semua orang. Ini dapat bekerja dengan baik, tetapi sulit untuk menerapkan dan mengoptimalkan, ditambah ada terlalu banyak kasus sudut dan penyesuaian khusus kasus yang diperlukan untuk seleraku. Tetapi untuk game dunia besar atau aplikasi visualisasi data, itu mungkin satu-satunya pendekatan yang mungkin untuk memasukkan semua konten ke dalam perangkat keras yang tersedia. Dengan banyak pekerjaan, dapat dibuat untuk berjalan cukup efisien bahkan pada perangkat keras yang terbatas, seperti yang dapat kita lihat di PS3 dan XBOX360 versi id Rage .

Pelaksanaan

Saya telah berhasil membuat VT bekerja di iOS dengan OpenGL-ES, sampai tingkat tertentu. Implementasi saya bukan "shippable", tapi saya bisa membuatnya jadi jika saya ingin dan memiliki sumber daya. Anda dapat melihat kode sumber di sini , mungkin membantu mendapatkan ide yang lebih baik tentang bagaimana potongan-potongan tersebut cocok. Berikut adalah video demo yang berjalan di iOS Sim. Ini terlihat sangat lamban karena simulator ini sangat buruk dalam mengemulasi shader, tetapi itu berjalan dengan lancar di perangkat.

Diagram berikut menguraikan komponen utama sistem dalam implementasi saya. Ini sedikit berbeda dari demo SVT Sean (tautan di bawah), tetapi lebih dekat dalam arsitektur dengan yang disajikan oleh makalah Mempercepat Tekstur Virtual Menggunakan CUDA , ditemukan dalam buku GPU Pro pertama (tautan di bawah).

sistem tekstur virtual

  • Page Filesadalah tekstur virtual, yang sudah dipotong menjadi ubin (halaman AKA) sebagai langkah preprocessing, sehingga siap untuk dipindahkan dari disk ke memori video kapan pun diperlukan. File halaman juga berisi seluruh set mipmaps, juga disebut mipmaps virtual .

  • Page Cache Managermenyimpan representasi sisi aplikasi Page Tabledan Page Indirectiontekstur. Karena memindahkan halaman dari penyimpanan offline ke memori itu mahal, kami memerlukan cache untuk menghindari memuat ulang apa yang sudah tersedia. Cache ini adalah cache LRU ( Least Terakhir Digunakan ) yang sangat sederhana . Cache juga merupakan komponen yang bertanggung jawab untuk menjaga agar tekstur fisik tetap mutakhir dengan representasi data lokalnya sendiri.

  • Ini Page Provideradalah antrian pekerjaan async yang akan mengambil halaman yang diperlukan untuk pemandangan yang diberikan dan mengirimkannya ke cache.

  • The Page Indirectiontekstur tekstur dengan satu pixel untuk setiap halaman / ubin di tekstur virtual, yang akan memetakan UVs masuk ke Page Tabletekstur cache yang memiliki data Texel yang sebenarnya. Tekstur ini bisa menjadi cukup besar, sehingga harus menggunakan beberapa format yang ringkas, seperti RGBA 8: 8: 8: 8 atau RGB 5: 6: 5.

Tapi kami masih kehilangan bagian kunci di sini, dan itulah cara menentukan halaman mana yang harus dimuat dari penyimpanan ke dalam cache dan konsekuensinya ke dalam Page Table. Di situlah Umpan Balik Lulus dan Page Resolvermasuk.

Umpan Umpan merupakan pra-render tampilan, dengan shader khusus dan pada resolusi yang jauh lebih rendah, yang akan menulis id halaman yang diperlukan ke framebuffer warna. Tambalan warna-warni dari kubus dan bola di atas adalah indeks halaman aktual yang disandikan sebagai warna RGBA. Render pra-lintasan ini kemudian dibaca ke dalam memori utama dan diproses oleh Page Resolveruntuk mendekodekan indeks halaman dan menjalankan permintaan baru dengan Page Provider.

Setelah umpan balik Pra-pass, adegan dapat ditampilkan secara normal dengan VT lookup shaders. Tetapi perhatikan bahwa kami tidak menunggu permintaan halaman baru selesai, itu akan mengerikan, karena kami hanya akan memblokir IO file sinkron. Permintaan tidak sinkron dan mungkin atau mungkin tidak siap pada saat tampilan akhir diberikan. Jika mereka siap, manis, tetapi jika tidak, kami selalu menyimpan halaman terkunci dari mipmap beresolusi rendah dalam cache sebagai cadangan, jadi kami memiliki beberapa data tekstur di sana untuk digunakan, tetapi itu akan menjadi buram.

Sumber daya lain yang layak dicoba

VT masih merupakan topik yang agak panas di Komputer Grafik, jadi ada banyak bahan bagus yang tersedia, Anda harus dapat menemukan lebih banyak. Jika ada hal lain yang dapat saya tambahkan ke jawaban ini, jangan ragu untuk bertanya. Saya agak berkarat pada topik, belum membaca banyak tentang hal itu selama setahun terakhir, tetapi itu selalu baik untuk memori untuk meninjau kembali hal-hal :)

Glampert
sumber
Hei, terima kasih atas jawaban yang bagus. Saya tahu ini biasanya tidak disukai, tetapi saya memiliki berbagai masalah, jadi saya kebanyakan hanya membaca sekilas tentang hal-hal - untuk mendapatkan gambaran intuitif dari topik untuk masa depan (saya khawatir mempelajari dan menerapkan hal-hal di luar jangkauan saya untuk saat ini. ) - lagi pula, jika mungkin, dapatkah Anda memposting contoh kodesemu menguraikan proses itu sendiri, idealnya, tetapi tidak harus, diilustrasikan?
Llamageddon
1
@Lamageddon, kebetulan bahwa saya masih memiliki diagram di tangan;) Saya khawatir pseudo-code akan sedikit sulit untuk diberikan, karena ada sedikit kode nyata untuk itu. Tetapi saya berharap jawaban yang diperluas membantu memberikan gambaran umum tentang teknik ini.
glampert
3
Perlu dicatat bahwa sebagian besar perangkat keras modern sekarang memperlihatkan tabel halaman yang dapat diprogram, menghilangkan kebutuhan akan tekstur pengalihan. Hal ini terpapar melalui misalnya directx12 sumber dilindungi undang-undang , yang dibangun pada DirectX11 ubin sumber daya , atau OpenGL tekstur jarang .
MooseBoys
1
@Llamageddon, umpan balik umpan balik dapat dilakukan pada resolusi yang lebih rendah untuk menghemat sebanyak mungkin komputasi dan memori, karena piksel untuk halaman biasanya akan berulang (Anda dapat melihat kotak berwarna besar di demo saya). Anda benar bahwa pada akhirnya mungkin akan melewatkan halaman yang terlihat seperti itu, tetapi itu biasanya tidak akan memiliki dampak visual yang besar karena sistem harus selalu menyimpan setidaknya mipmap terendah dari seluruh VT yang tersedia dalam cache. Makalah kedua yang saya tautkan memiliki semua contoh shader di lampiran, Anda juga dapat merujuk ke repo untuk proyek saya sendiri, mereka serupa.
glampert
1
@ Glampert Ahh, saya mengerti; itu masuk akal. Namun, saya pikir ada banyak opsi untuk menangani transparansi; di halaman ID pass, Anda bisa gentar (sehingga histogram akan melihat semua halaman, kecuali ada sejumlah besar lapisan transparan), atau menggunakan pendekatan k-buffer , atau bahkan hanya mendasarkan residensi tekstur transparan di mana objek berada di dekat kamera (sebagai lawan merendernya dalam umpan balik).
Nathan Reed
11

Virtual Texturing adalah ekstrim logis dari atlas tekstur.


Sebuah atlas tekstur adalah tekstur raksasa tunggal yang berisi tekstur untuk jerat individual di dalamnya:

Contoh Atlas Tekstur

Atlas Tekstur menjadi populer karena fakta bahwa mengubah tekstur menyebabkan flush penuh pada GPU. Saat membuat jerat, UVs dikompresi / digeser sehingga mewakili 'bagian' yang benar dari seluruh atlas tekstur.

Seperti @ nathan-reed yang disebutkan dalam komentar, salah satu kelemahan utama atlas tekstur adalah kehilangan mode bungkus seperti pengulangan, penjepit, batas, dll. Selain itu, jika tekstur tidak memiliki batas yang cukup di sekelilingnya, Anda dapat secara tidak sengaja sampel dari tekstur yang berdekatan saat melakukan penyaringan. Ini dapat menyebabkan artefak yang berdarah.

Texture Atlases memang memiliki satu batasan utama: ukuran. API Grafik menempatkan batas lunak pada seberapa besar tekstur dapat. Konon, memori grafis hanya sebesar itu. Jadi ada juga batasan keras pada ukuran tekstur, yang diberikan oleh ukuran v-ram Anda. Tekstur virtual menyelesaikan masalah ini, dengan meminjam konsep dari memori virtual .

Tekstur virtual mengeksploitasi fakta bahwa di sebagian besar adegan, Anda hanya melihat sebagian kecil dari semua tekstur. Jadi, hanya subset dari tekstur yang perlu di vram. Sisanya bisa di RAM utama, atau di disk.

Ada beberapa cara untuk mengimplementasikannya, tetapi saya akan menjelaskan implementasi yang dijelaskan oleh Sean Barrett dalam pembicaraan GDC- nya . (yang sangat saya sarankan tonton)

Kami memiliki tiga elemen utama: tekstur virtual, tekstur fisik, dan tabel pencarian.

Tekstur Virtual

Tekstur virtual mewakili atlas mega teoritis yang akan kita miliki jika kita memiliki cukup vram agar sesuai dengan segalanya. Sebenarnya tidak ada di memori di mana pun. Tekstur fisik mewakili data piksel apa yang sebenarnya kita miliki dalam vram. Tabel pencarian adalah pemetaan antara keduanya. Untuk kenyamanan, kami memecah ketiga elemen menjadi ubin berukuran sama, atau halaman.

Tabel pencarian menyimpan lokasi sudut kiri atas ubin dalam tekstur fisik. Jadi, mengingat UV untuk seluruh tekstur virtual, bagaimana kita mendapatkan UV yang sesuai untuk tekstur fisik?

Pertama, kita perlu menemukan lokasi halaman dalam tekstur fisik. Maka kita perlu menghitung lokasi UV di dalam halaman. Akhirnya kita bisa menambahkan dua offset ini bersama-sama untuk mendapatkan lokasi UV dalam tekstur fisik

float2 pageLocInPhysicalTex = ...
float2 inPageLocation = ...
float2 physicalTexUV = pageLocationInPhysicalTex + inPageLocation;


Menghitung pageLocInPhysicalTex

Jika kita membuat tabel pencarian dengan ukuran yang sama dengan jumlah ubin dalam tekstur virtual, kita bisa mengambil sampel tabel pencarian dengan sampling tetangga terdekat dan kita akan mendapatkan lokasi sudut kiri atas halaman dalam tekstur fisik.

float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);


Menghitung inPageLocation

inPageLocation adalah koordinat UV yang relatif ke kiri atas halaman, bukan ke kiri atas seluruh tekstur.

Salah satu cara untuk menghitung ini adalah dengan mengurangi UV dari kiri atas halaman, kemudian menskala ke ukuran halaman. Namun, ini sedikit matematika. Sebagai gantinya, kita dapat memanfaatkan bagaimana floating point IEEE direpresentasikan. IEEE floating point menyimpan bagian pecahan dari suatu bilangan dengan serangkaian pecahan basa 2.

masukkan deskripsi gambar di sini

Dalam contoh ini, angkanya adalah:

number = 0 + (1/2) + (1/8) + (1/16) = 0.6875

Sekarang mari kita lihat versi sederhana dari tekstur virtual:

Tekstur Virtual Sederhana

Bagian 1/2 memberitahu kita jika kita berada di bagian kiri dari tekstur atau bagian kanan. Bit 1/4 memberi tahu kita bagian mana dari setengah kita berada. Dalam contoh ini, karena tekstur dibagi menjadi 16, atau 4 ke samping, dua bit pertama ini memberi tahu kita di halaman mana kita berada. Sisanya bit memberi tahu kami lokasi di dalam halaman.

Kita bisa mendapatkan bit yang tersisa dengan menggeser float dengan exp2 () dan menghapusnya dengan fract ()

float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
inPageLocation = fract(inPageLocation);

Di mana numTiles adalah int2 yang memberikan jumlah ubin per sisi tekstur. Dalam contoh kita, ini akan menjadi (4, 4)

Jadi mari kita hitung inPageLocation untuk titik hijau, (x, y) = (0,6875, 0,375)

inPageLocation = float2(0.6875, 0.375) * exp2(sqrt(int2(4, 4));
               = float2(0.6875, 0.375) * int2(2, 2);
               = float2(1.375, 0.75);

inPageLocation = fract(float2(1.375, 0.75));
               = float2(0.375, 0.75);

Satu hal terakhir yang harus dilakukan sebelum kita selesai. Saat ini, inPageLocation adalah koordinat UV dalam 'ruang' tekstur virtual. Namun, kami ingin koordinat UV dalam 'ruang' tekstur fisik. Untuk melakukan ini, kita hanya perlu skala inPageLocation dengan rasio ukuran tekstur virtual dengan ukuran tekstur fisik

inPageLocation *= physicalTextureSize / virtualTextureSize;



Jadi fungsi yang selesai adalah:

float2 CalculatePhysicalTexUV(float2 virtTexUV, Texture2D<float2> lookupTable, uint2 physicalTexSize, uint2 virtualTexSize, uint2 numTiles) {
    float2 pageLocInPhysicalTex = lookupTable.Sample(virtTexUV, nearestNeighborSampler);

    float2 inPageLocation = virtTexUV * exp2(sqrt(numTiles));
    inPageLocation = fract(inPageLocation);
    inPageLocation *= physicalTexSize / virtualTexSize;

    return pageLocInPhysicalTex + inPageLocation;
}
RichieSams
sumber
Saya tidak, saya mengacu pada texturing virtual, paling dikenal sebagai teknologi MegaTexture idTech 5 . Juga lihat ini dan ini . Saya telah melihatnya disebutkan dalam ikhtisar pipa render banyak mesin modern, dan dalam beberapa makalah yang menggunakan pendekatan serupa untuk shadowmaps. Itu memang memiliki banyak kesamaan dengan tekstur atlas, ya, ia menggunakannya, dengan cara tertentu, tapi saya tidak bingung dengan tekstur atlas.
Llamageddon
Ahh. Terima kasih atas tautannya. Bisakah Anda menambahkannya ke pertanyaan. Saya akan memperbarui jawaban saya sesuai
RichieSams
3
IMO, kelemahan utama atlas tekstur sederhana (bukan tekstur virtual) adalah Anda kehilangan mode bungkus seperti pengulangan dan penjepit, dan pendarahan terjadi karena penyaringan / pemetaan - tidak presisi floating-point. Saya akan terkejut melihat presisi float menjadi masalah untuk tekstur biasa (non-virtual); bahkan tekstur 16K (maks yang diizinkan oleh API saat ini) tidak cukup besar untuk benar-benar memaksakan ketelitian float.
Nathan Reed
@RichieSams Btw, saya pikir jawaban Anda adalah jawaban yang bagus, meskipun untuk pertanyaan yang berbeda. Anda harus membuat posting T&J.
Llamageddon
Hmm, ini menjelaskan dengan cukup baik, meskipun saya tidak benar-benar mengerti cara kerjanya dengan level mip. Saya berharap saya bisa menuliskan masalah spesifik saya dengan memahaminya, tetapi itu agak menghindarkan saya ...
Llamageddon