Virtual Texturing adalah ekstrim logis dari atlas tekstur.
Sebuah atlas tekstur adalah tekstur raksasa tunggal yang berisi tekstur untuk jerat individual di dalamnya:
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 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.
Dalam contoh ini, angkanya adalah:
number = 0 + (1/2) + (1/8) + (1/16) = 0.6875
Sekarang mari kita lihat versi sederhana dari tekstur virtual:
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;
}