C ++ 'da bir voksel Motoru için vokselleri saklama


9

Biraz voksel motoru yazmaya çalışıyorum çünkü eğlenceli, ama gerçek vokselleri saklamanın en iyi yolunu bulmak için mücadele ediyorum. Bir çeşit parçaya ihtiyacım olacağının farkındayım, bu yüzden tüm dünyayı hafızaya almam gerekmiyor ve onları makul performansla işlemem gerektiğinin farkındayım.

Oktrees hakkında okudum ve anladığım kadarıyla 1 küp ile başlıyor ve bu küpte 8 küp daha olabilir ve tüm bu 8 küpte 8 küp daha olabilir. Ama bunun voksel motoruma uyduğunu sanmıyorum çünkü voksel küplerim / öğelerim tamamen aynı boyutta olacak.

Yani başka bir seçenek sadece 16 * 16 * 16 boyutunda bir dizi oluşturmak ve bu bir yığın olmak ve onu öğelerle doldurmaktır. Ve herhangi bir öğenin bulunmadığı kısımlar değer olarak 0 olacaktır (0 = hava). Ama korkarım bu çok fazla bellek harcayacak ve çok hızlı olmayacak.

Sonra başka bir seçenek her bir yığın için bir vektördür ve küplerle doldurun. Ve küp, yığın içindeki konumunu korur. Bu, bellek tasarrufu sağlar (hava blokları yoktur), ancak belirli bir yerde bir küpü aramayı çok daha yavaş hale getirir.

Bu yüzden gerçekten iyi bir çözüm bulamıyorum ve birinin bana bu konuda yardımcı olabileceğini umuyorum. Peki ne kullanıyorsunuz ve neden?

Ama başka bir sorun renderleme. Her bir yığını okumak ve OpenGL kullanarak GPU'ya göndermek kolaydır, ancak çok yavaştır. Parça başına bir ağ oluşturmak daha iyi olurdu, ancak bu, bir bloğu her kırdığımda, tüm parçayı yeniden inşa etmek zorunda kalıyorum, bu da biraz zaman alan küçük ama fark edilir bir hıçkırmaya neden oluyor, ki ben de istemiyorum. Yani bu daha zor olurdu. Peki küpleri nasıl hazırlarım? Tüm küpleri yığın başına bir köşe arabelleğinde oluşturun ve bunu oluşturun ve belki de başka bir iş parçacığına koymaya çalışın, yoksa başka bir yol var mı?

Teşekkürler!


1
Küplerinizi oluşturmak için örneklemeyi kullanmalısınız. Burada bir öğretici bulabilirsiniz learnopengl.com/Advanced-OpenGL/Instancing . Küpleri saklamak için: donanımda güçlü bellek kısıtlamalarınız var mı? 16 ^ 3 küp çok fazla bellek gibi görünmüyor.
Turms

@Turms Yorumunuz için teşekkürler! Güçlü bellek kısıtlamalarım yok, sadece normal bir bilgisayar. Ama düşündüm ki, en büyük her yığın% 50 hava ise ve dünya çok büyükse, o zaman biraz boşa harcanmış olmalı. Ama muhtemelen söylediğin gibi değil. Statik miktarda blok içeren 16 * 16 * 16 parçalarına mı gitmeliyim? Ayrıca, örneklemeyi kullanmam gerektiğini söylüyorsun, bu gerçekten gerekli mi? Benim fikrim her bir yığın için bir kafes oluşturmaktı, çünkü bu şekilde tüm görünmez üçgenleri dışarıda bırakabiliyorum.

6
Turms'un açıkladığı gibi küpler için örneklemeyi kullanmanızı önermiyorum.Bu sadece çizim çağrılarınızı azaltacaktır, ancak aşırı çekilme ve gizli yüzler için hiçbir şey yapmaz - aslında tüm küpleri çalıştırmak için ellerinizi bu sorunu çözmekten bağlar. aynı olması gerekir - bazı küplerin gizli yüzlerini silemez veya eş düzlemli yüzleri daha büyük tek çokgenlerle birleştiremezsiniz.
DMGregory

En iyi voksel motorunu seçmek zor olabilir. Kendinize sormanız gereken en büyük soru "voksellerimde hangi işlemleri yapmam gerekiyor?" Bu operasyonları yönlendirir. Örneğin, bir okt ağacında hangi vokselin nerede olduğunu bulmanın ne kadar zor olduğu konusunda endişelisiniz. Ekim ağacı algoritmaları, ağacı yürüdükçe (genellikle yinelemeli bir şekilde) bu bilgileri gerektiği gibi oluşturabilen sorunlar için mükemmeldir. Bunun çok pahalı olduğu belirli sorunlarınız varsa, diğer seçeneklere bakabilirsiniz.
Cort Ammon

Başka bir büyük soru, voksellerin ne sıklıkla güncellendiği. Bazı algoritmalar, verileri verimli bir şekilde saklamak için verileri önceden işleyebilirlerse harika, ancak veriler sürekli olarak güncelleniyorsa daha az verimlidir (veriler partikül bazlı bir sıvı simülasyonunda olabileceğinden)
Cort Ammon

Yanıtlar:


23

Blokları konumlar ve değerler olarak saklamak aslında çok verimsizdir. Kullandığınız yapı veya nesnenin neden olduğu herhangi bir ek yük olmadan bile, blok başına 4 ayrı değer depolamanız gerekir. "Blokları sabit dizilerde saklamak" yöntemi (daha önce açıkladığınız yöntem) üzerinde kullanmak, blokların sadece dörtte birinin sağlam olduğu ve bu şekilde başka optimizasyon yöntemlerini kullanmamanız mantıklı olacaktır. hesap.

Octrees aslında voksel tabanlı oyunlar için harikadır, çünkü daha büyük özelliklere sahip verileri saklama konusunda uzmanlaşmıştır (örneğin, aynı bloğun yamaları). Bunu göstermek için, bir dörtlü (temelde 2d octrees) kullandım:

Bu 1024 değerleri eşit 32x32 fayans içeren benim başlangıç ​​kümesidir: resim açıklamasını buraya girin

Bunu 1024 ayrı değer olarak depolamak verimsiz görünmez, ancak Terraria gibi oyunlara benzer harita boyutlarına ulaştığınızda , ekranların yüklenmesi birkaç saniye sürer. Ve eğer üçüncü boyuta yükseltirseniz, sistemdeki tüm alanı kullanmaya başlar.

Quadtrees (veya 3d octrees) duruma yardımcı olabilir. Bir tane oluşturmak için, fayanslardan gidebilir ve birlikte gruplayabilir veya büyük bir hücreden gidebilir ve fayanslara ulaşana kadar bölebilirsiniz. İlk yaklaşımı kullanacağım, çünkü görselleştirmek daha kolay.

Bu nedenle, ilk yinelemede her şeyi 2x2 hücrelere gruplandırırsınız ve bir hücre yalnızca aynı türde karolar içeriyorsa, karoları bırakır ve türü depolarsınız. Bir yinelemeden sonra, haritamız şöyle görünecektir:

resim açıklamasını buraya girin

Kırmızı çizgiler neyi sakladığımızı gösterir. Her kare sadece 1 değerdir. Bu, boyutu 1024 değerlerden 439'a düşürdü, bu% 57'lik bir azalma.

Ama mantrayı biliyorsun . Bir adım daha ileri gidelim ve bunları hücrelere gruplayalım:

resim açıklamasını buraya girin

Bu, depolanan değerlerin miktarını 367'ye düşürdü. Bu, orijinal boyutun yalnızca% 36'sı.

Bu bölünmeyi, bir yığın içindeki her 4 bitişik hücre (3D'de 8 bitişik blok) bir hücre içinde saklanana kadar yapmanız gerekir;

Bunun, özellikle çarpışma yaparken başka faydaları da vardır, ancak bunun için ayrı bir oktree oluşturmak isteyebilirsiniz, bu da sadece tek bir bloğun sağlam olup olmadığını umursar. Bu şekilde, bir yığın içindeki her blok için çarpışmaya karşı kontrol etmek yerine, bunu hücrelere karşı yapabilirsiniz.


Cevabın için teşekkürler! Oktree'nin gitmek için bir yol gibi görünüyor. (Voksel motorum 3D olacağından) Sormak istediğim bir soru var: Son resminiz siyah parçaların daha büyük karelere sahip olduğunu gösteriyor, çünkü minecraft gibi voksel arazisini değiştirebileceğiniz bir motor, bloğu olan her şeyi aynı boyutta tutmayı tercih ederim, aksi takdirde işleri çok karmaşık hale getirir, bu doğru mu? (Hala boş / hava yuvalarını basitleştireceğim.) İkinci , bir oktree nasıl programlanacağı hakkında bir tür öğretici var mı? Teşekkürler!

7
@ appmaker1358 bu hiç sorun değil. Oyuncu büyük bir bloğu değiştirmeye çalışırsa , o anda daha küçük bloklara ayırırsınız . Bunun yerine "bu bütün yığın katı kayadır" diyebildiğiniz zaman 16x16x16 "rock" değerlerini saklamanıza gerek yoktur.
DMGregory

1
@ appmaker1358 DMGregory'nin dediği gibi, bir oktree'de depolanan verileri güncellemek nispeten kolaydır. Yapmanız gereken tek şey, her bir alt hücre yalnızca tek bir blok türü içerene kadar, değişimin gerçekleştiği hücreyi bölmektir. İşte dörtlü bir interaktif örnek . Bir tane oluşturmak da basit. Tamamen yığın içeren bir büyük hücre oluşturursunuz, daha sonra her yaprak hücresinden (çocuk içermeyen hücreler) yinelemeli olarak geçersiniz, temsil ettiği arazinin bir kısmının birden fazla blok türü içerip içermediğini kontrol edin, evetse, hücre
Bálint

@ appmaker1358 Daha büyük sorun aslında tersidir - oktree'nin sadece bir blokla yapraklarla dolu olmadığından emin olmak, Minecraft tarzı bir oyunda kolayca gerçekleşebilir. Ancak, soruna birçok çözüm var - sadece uygun olanı seçmekle ilgilidir. Ve bu sadece çok fazla bina olduğunda gerçek bir problem haline gelir.
Luaan

Octrees mutlaka en iyi seçim değildir. ilginç bir okuma: 0fps.net/2012/01/14/an-analysis-of-minecraft-like-engines
Polygnome

7

Büyük arama süreleri olmadan seyrek verilerin yoğun bir şekilde depolanmasına izin vererek tam olarak tanımladığınız sorunu çözmek için ekstreler var.

Voksellerinizin aynı boyutta olması, oktree'nizin sabit bir derinliğe sahip olduğu anlamına gelir. Örneğin. 16x16x16 yığın için en fazla 5 ağaç seviyesine ihtiyacınız vardır:

  • yığın kökü (16x16x16)
    • birinci katman oktan (8x8x8)
      • ikinci katman oktan (4x4x4)
        • üçüncü katman oktan (2x2x2)
          • tek voksel (1x1x1)

Bu, yığında belirli bir konumda bir voksel olup olmadığını bulmak için en fazla 5 adımınız olduğu anlamına gelir:

  • yığın kökü: bütün yığın aynı değer midir (örn. tüm hava)? Eğer öyleyse, işimiz bitti. Değilse ...
    • ilk katman: bu pozisyonu içeren oktan aynı değer midir? Değilse ...
      • ikinci kademe...
        • üçüncü katman ...
          • şimdi tek bir vokselle ilgileniyoruz ve değerini döndürebiliriz.

4096 voksele kadar bir dizi boyunca% 1 bile taramaktan çok daha kısa!

Bunun, verileri ister hava ister tüm kaya veya başka bir şey olsun, aynı değerin tam bir oktanının olduğu her yerde sıkıştırmamıza izin verdiğine dikkat edin. Sadece oktantlar, tek voksel yaprak düğümleri sınırına kadar daha fazla alt bölümlere ayırmamız gereken karışık değerler içerir.


Bir yığının çocuklarına hitap etmek için, genellikle Morton düzeninde ilerleyeceğiz , böyle bir şey:

  1. X- Y- Z-
  2. X- Y- Z +
  3. X- Y + Z-
  4. X- Y + Z +
  5. X + Y- Z-
  6. X + Y- Z +
  7. X + Y + Z-
  8. X + Y + Z +

Yani, Octree düğümü navigasyonumuz şöyle görünebilir:

GetOctreeValue(OctreeNode node, int depth, int3 nodeOrigin, int3 queryPoint) {
    if(node.IsAllOneValue)
        return node.Value;

    int childIndex =  0;
    childIndex += (queryPoint.x > nodeOrigin.x) ? 4 : 0;
    childIndex += (queryPoint.y > nodeOrigin.y) ? 2 : 0;
    childIndex += (queryPoint.z > nodeOrigin.z) ? 1 : 0;

    OctreeNode child = node.GetChild(childIndex);

    return GetOctreeValue(
                child, 
                depth + 1,
                nodeOrigin + childOffset[depth, childIndex],
                queryPoint
    );
}

Cevabın için teşekkürler! Görünüşe göre oktree'nin yolu var. Ama ben 2 soru var, octree en doğru bir dizi üzerinden tarama daha hızlı olduğunu söylüyorlar. Ama dizi statik anlamı olabilir gibi yapmam gerekmezdi Ben ihtiyacım küp nerede hesaplayabilirsiniz. Öyleyse neden taramam gerekiyor? İkinci soru, oktree'nin son katmanında (1x1x1), hangi küpün nerede olduğunu nasıl bilebilirim, çünkü doğru anlarsam ve octree düğümü 8 daha fazla düğüme sahipse, hangi düğümün hangi 3d konuma ait olduğunu nasıl bilebilirsiniz? ? (Yoksa kendimi hatırlamam gerekiyor mu?)

Evet, sorunuzdaki 16x16x16 vokselden oluşan kapsamlı bir diziyi zaten ele aldınız ve yığın bellek alanı başına 4K'yı (her bir voksel kimliğinin bir bayt olduğu varsayılarak) aşırı reddettiğini görünüyordu. Bahsettiğiniz arama, bir konumdaki voksel listesini saklarken, vokseli hedef konumunuzda bulmak için listede taramaya zorlar. 4096, bu liste uzunluğunun üst sınırıdır - tipik olarak bundan daha küçük olacaktır, ancak genellikle karşılık gelen bir oktree aramasından daha derin olacaktır.
DMGregory
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.