Java vs C ++ ile ilgili olarak, her ikisinde de bir voksel motoru yazdım (yukarıda gösterilen C ++ sürümü). Ayrıca 2004'ten beri (moda olmadıkları zamanlarda) voksel motorları yazıyorum. :) Çok az tereddütle C ++ performansının çok daha üstün olduğunu söyleyebilirim (ancak kodlaması daha zordur). Hesaplama hızı ve hafıza yönetimi hakkında daha az şey. Eller aşağıdayken, bir voksel dünyasındaki veriler kadar veri tahsis / tahsis ederken, C (++) yenilecek dildir. ancak, amacını düşünmelisin. Performans sizin en yüksek önceliğiniz ise, C ++ ile gidin. Kanama performansı olmayan bir oyun yazmak istiyorsanız, Java kesinlikle kabul edilebilir (Minecraft tarafından kanıtlandığı gibi). Çok sayıda önemsiz / son durum vardır, ancak genel olarak Java'nın (iyi yazılmış) C ++ 'dan yaklaşık 1.75-2.0 kat daha yavaş çalışmasını bekleyebilirsiniz. Motorumun zayıf optimize edilmiş eski versiyonunu burada çalışırken görebilirsiniz (EDIT: burada daha yeni sürüm ). Yığın oluşumu yavaş görünse de, hacimsel olarak 3B voronoi şemaları oluşturduğunu, yüzeysel normalleri, ışıklandırmayı, AO'yu ve CPU üzerindeki gölgeleri hesaplayarak kaba kuvvet yöntemleriyle oluşturduğunu unutmayın. Çeşitli teknikleri denedim ve çeşitli önbellekleme ve deneme teknikleri kullanarak yaklaşık 100 kat daha hızlı yığın üretme elde edebiliyorum.
Sorunuzun geri kalanına cevap vermek için performansı artırmak için yapabileceğiniz birçok şey var.
- Caching. Nerede olursanız olun, verileri bir kez hesaplamanız gerekir. Örneğin, aydınlatmayı sahneye fırlatırım. Dinamik aydınlatmayı kullanabilir (ekran alanında, bir post-proses olarak), ancak aydınlatmayı pişirmek, üçgenler için normalleri geçmek zorunda olmadığım anlamına gelir, yani ...
Video kartına mümkün olduğunca az veri iletin. İnsanların unutmaya meyilli olduğu şeylerden biri GPU'ya ne kadar çok veri aktarırsanız, o kadar fazla zaman harcar. Tek bir renk ve bir tepe pozisyonunda geçiyorum. Gündüz / gece döngüleri yapmak istersem, sadece renk derecelendirmesini yapabilirim veya güneş yavaş yavaş değiştikçe sahneyi yeniden hesaplayabilirim.
Verilerin GPU'ya aktarılması çok pahalı olduğu için, bazı yönlerden daha hızlı olan yazılımda bir motor yazmak mümkündür. Yazılımın avantajı, bir GPU'da mümkün olmayan her türlü veri işleme / bellek erişimini yapabilmesidir.
Toplu boyutu ile oynayın. Bir GPU kullanıyorsanız, performans, aktardığınız her bir köşe dizisinin ne kadar büyük olduğuna bağlı olarak büyük ölçüde değişebilir. Buna göre, topakların boyutuyla oynayın (topak kullanıyorsanız). 64x64x64 parçalarının çok iyi çalıştığını buldum. Ne olursa olsun, topaklarınızı kübik tutun (dikdörtgen prizmalar yok). Bu kodlama ve çeşitli işlemleri (dönüşümler gibi) kolaylaştıracak ve bazı durumlarda daha performans gösterecektir. Her boyutun uzunluğu için yalnızca bir değer saklarsanız, hesaplama sırasında takas edilen daha az iki kayıt olduğunu unutmayın.
Ekran listelerini düşünün (OpenGL için). "Eski" yol olsalar bile, daha hızlı olabilirler. Bir ekran listesini değişkene dönüştürmelisiniz ... eğer ekran listesi oluşturma işlemlerini gerçek zamanlı olarak çağırırsanız, çok yavaş olacaktır. Bir ekran listesi nasıl daha hızlı? Yalnızca durumu, köşe başına öznitelikleri vs güncelleştirir. Bu, altı yüze kadar geçebileceğim anlamına gelir, sonra bir renk (vokselin her bir köşesi için bir renge karşı). GL_QUADS ve kübik voksel kullanıyorsanız, bu voksel başına 20 bayta (160 bit) kadar tasarruf sağlayabilir! (Alfa içermeyen 15 bayt, genellikle 4 baytlık bir şeyleri saklamak istemenize rağmen)
"Parçalar" oluşturmak için kaba kuvvet yöntemini ya da ortak bir teknik olan veri sayfalarını kullanıyorum. Okuyuculardan farklı olarak, verileri okumak / işlemek çok daha kolay / daha hızlıdır, ancak çok daha az hafıza dostu olsa da (ancak bugünlerde 200-300 dolara 64 gigabaytlık bellek alabilirsiniz) ... ortalama bir kullanıcı bu değildir. Açıkçası, tüm dünya için büyük bir dizi tahsis edemezsiniz (1024x1024x1024 voksel seti, voksel başına 32 bitlik bir int kullanıldığı varsayılarak, 4 gigabayt bellekdir). Böylece, izleyiciye olan yakınlığına bağlı olarak birçok küçük diziyi ayırır / devre dışı bırakırsınız. Ayrıca verileri tahsis edebilir, gerekli ekran listesini alabilir, ardından hafızadan tasarruf etmek için veriyi bırakabilirsiniz. İdeal combo'nun octrees ve dizilerin hibrid bir yaklaşımını kullanmak olabileceğini düşünüyorum - dünyanın prosedürel jenerasyonu, aydınlatma vb.
Uzaklara yakın render ... kırpılmış bir piksel zaman kazandırır. Derinlik tamponu testini geçmezse gpu bir piksel atacaktır.
Render sadece görünümdeki parçaları / sayfaları gösterir (kendi kendini açıklayıcı). Gpu, görüntü portunun dışındaki poligonları nasıl yakalayacağını bilse bile, bu verileri geçmek zaman alıyor. Bunun için en verimli yapının ne olacağını bilmiyorum ("utanç verici", hiçbir zaman bir BSP ağacı yazmadım), ancak yığın bazında basit bir raycast bile performansı artırabilir ve açıkça izleyen frustuma karşı test yapabilir. Zamandan tasarruf.
Açıkça bilgi, ancak yeni başlayanlar için: yüzeyde olmayan her bir poligonu çıkarın - yani bir voksel altı yüzden oluşuyorsa, hiç işlenmemiş yüzleri çıkarın (başka bir vokselle temas ediyor).
Programlamada yaptığınız her şeyin genel kuralı olarak: CACHE LOCALITY! İşleri yerel olarak saklayabiliyorsanız (az miktarda bile olsa, çok büyük bir fark yaratacaktır. Bu, verilerinizi uyumlu (aynı bellek bölgesinde) tutmak ve bellek alanlarını çok sık işlem yapmak zorunda bırakmamak anlamına gelir. , ideal olarak, iş parçacığı başına bir öbek üzerinde çalışın ve bu belleği iş parçacığına özgü tutun, bu yalnızca CPU önbelleği için geçerli değildir. Bunun gibi önbellek hiyerarşisini düşünün (en yavaşdan en hızlı): ağ (bulut / veritabanı / vb) -> sabit sürücü (zaten yoksa, bir SSD alın), ram (zaten yoksa üçlü kanal veya daha fazla RAM alın), CPU Önbellek (ler) i, kaydedicilerinizi kaydedin. Sonuncusu, ve gerekenden daha fazla değiştirmeyin.
Threading. Yap. Voxel dünyaları her parçayı diğerlerinden bağımsız olarak hesaplayabildiğinden (çoğunlukla) hesaplanabildiğinden, diş açma için çok uygundur ... Yazarken, yordam dünyasında tam anlamıyla 4x'e yakın bir gelişme (4 çekirdekli, 8 iplik Çekirdekli i7) gördüm. diş açma için rutinler.
Char / byte veri türlerini kullanmayın. Veya şort. Ortalama bir tüketici modern bir AMD veya Intel işlemciye sahip olacak (muhtemelen sizin gibi). Bu işlemcilerin 8 bit kaydı yok. Bayt'ları 32 bitlik bir yuvaya yerleştirip sonra tekrar bellekte (belki) dönüştürerek hesaplarlar. Derleyiciniz her türlü vudu yapabilir, ancak 32 veya 64 bitlik bir sayı kullanmak size en öngörülebilir (ve en hızlı) sonuçları verecektir. Aynı şekilde, bir "bool" değeri 1 bit almaz; derleyici bir bool için genellikle tam bir 32 bit kullanır. Verilerinizde belirli sıkıştırma türleri yapmanız cazip gelebilir. Örneğin, aynı vokal / renkte olsaydı 8 vokseli tek bir numara olarak saklayabilirsiniz (2 ^ 8 = 256 kombinasyon). Bununla birlikte, bunun sonuçları hakkında düşünmeniz gerekir - bu, çok fazla hafıza biriktirebilir, ancak küçük bir dekompresyon süresinde bile performansı engelleyebilir, çünkü bu küçük miktardaki fazladan zaman bile dünyanızın boyutuna göre küp ölçeklenir. Bir raycast hesapladığını düşünün; Rapikastin her basamağı için, dekompresyon algoritmasını çalıştırmanız gerekecektir (bir ışın basamağında 8 voksel için hesaplamayı genelleştirmenin akıllı bir yolunu bulamazsanız).
Jose Chavez'in bahsettiği gibi, flyweight tasarım deseni faydalı olabilir. 2B oyunda bir döşemeyi temsil etmek için bir bitmap kullandığınız gibi, dünyanızı birkaç 3B döşeme (veya blok) türünden oluşturabilirsiniz. Bunun dezavantajı, dokuların tekrarıdır, ancak birbirine uyan değişken dokuları kullanarak bunu iyileştirebilirsiniz. Genel bir kural olarak, nerede olursanız olun, örneklemeden yararlanmak istersiniz.
Geometri çıkarırken gölgelendiricideki köşe ve piksel işlemeden kaçının. Bir voksel motorunda, kaçınılmaz olarak birçok üçgene sahip olacaksınız, böylece basit bir piksel gölgelendirici bile oluşturma sürenizi büyük ölçüde azaltabilir. Tamponun oluşturulması daha iyidir, o zaman bir işlem sonrası piksel gölgelendiriciyi yaparsınız. Bunu yapamıyorsanız, köşe gölgelendiricinizde hesaplamalar yapmayı deneyin. Diğer hesaplamalar mümkünse tepe noktası verilerine yapılmalıdır. Tüm geometrileri (gölge haritalama veya çevre haritalama gibi) yeniden oluşturmanız gerekirse ek geçişler çok pahalı hale gelir. Bazen daha zengin ayrıntılar lehine dinamik bir sahneden vazgeçmek daha iyidir. Oyununuzda değiştirilebilir sahneler varsa (örneğin, yıkılabilir arazi), sahneyi işler yıkıldıkça yeniden hesaplayabilirsiniz. Yeniden derleme pahalı değildir ve bir saniyeden daha az sürmelidir.
Döngülerini çöz ve dizileri düz tut! Bunu yapma:
for (i = 0; i < chunkLength; i++) {
for (j = 0; j < chunkLength; j++) {
for (k = 0; k < chunkLength; k++) {
MyData[i][j][k] = newVal;
}
}
}
//Instead, do this:
for (i = 0; i < chunkLengthCubed; i++) {
//figure out x, y, z index of chunk using modulus and div operators on i
//myData should have chunkLengthCubed number of indices, obviously
myData[i] = newVal;
}
EDIT: Daha kapsamlı testlerle bunun yanlış olabileceğini keşfettim. Senaryonuz için en uygun olanı kullanın. Genel olarak, diziler düz olmalıdır, ancak çok endeksli döngüler kullanılması duruma bağlı olarak genellikle daha hızlı olabilir