Oyun Motoru Tasarımı - Ubershader - Gölgelendirici yönetimi tasarımı [kapalı]


18

Ertelenmiş gölgeleme ile esnek bir Ubershader sistemi uygulamak istiyorum. Şu anki fikrim FlatTexture, BumpTexture, Yer Değiştirme Eşleme, vb.Gibi bazı özelliklerle uğraşan modüllerden gölgelendiriciler oluşturmak. Rengin kodunu çözen, ton eşlemesi yapan vb. Gibi küçük modüller de var. GPU desteklemiyorsa belirli modül türlerini değiştirin, böylece mevcut GPU özelliklerine uyum sağlayabiliyorum. Bu tasarımın iyi olup olmadığından emin değilim. Korkarım şimdi kötü bir tasarım seçimi yapabileceğim ve daha sonra ödeyeceğim.

Benim sorum, gölgelendirici yönetim sisteminin etkili bir şekilde nasıl uygulanacağına ilişkin kaynakları, örnekleri, makaleleri nerede bulabilirim? Büyük oyun motorlarının bunu nasıl yaptığını bilen var mı?


3
Gerçek bir cevap için yeterince uzun değil: Küçük başlarsanız ve gölgelendiricilerin önüne MegaCity-One'ı inşa etmek yerine, ihtiyaçlarınız tarafından organik olarak büyümesine izin verirseniz , bu yaklaşımla iyi olacaksınız . Öncelikle, ön tarafta çok fazla tasarım yapma ve daha sonra işe yaramazsa bunun için ödeme yapma konusunda en büyük endişenizi hafifletirsiniz, ikincisi asla kullanılmayan ekstra işler yapmaktan kaçınırsınız.
Patrick Hughes

Ne yazık ki, artık "kaynak talebi" sorularını kabul etmiyoruz.
Gnemlock

Yanıtlar:


23

Yarı yaygın bir yaklaşım, gölgelendirici bileşenleri dediğim şeyi, modül dediğini düşündüğüm gibi yapmaktır .

Fikir, işlem sonrası bir grafiğe benzer. Hem gerekli girişleri, üretilen çıktıları hem de gerçekte üzerinde çalışacak kodu içeren gölgelendirici kod parçaları yazarsınız. Herhangi bir durumda hangi gölgelendiricilerin uygulanacağını gösteren bir listeniz var (bu malzemenin bir yumru haritalama bileşenine ihtiyacı olup olmadığı, ertelenmiş veya ileri bileşen etkin olup olmadığı vb.).

Şimdi bu grafiği alıp gölgelendirici kodu oluşturabilirsiniz. Bu çoğunlukla, parçaların kodunu "yapıştırmak" anlamına gelir; grafik zaten gerekli sırada olduklarından emin olmak ve daha sonra gölgelendirici girişlerini / çıkışlarını uygun şekilde yapıştırmak anlamına gelir (GLSL'de, bu "global" , out ve uniform değişkenler).

Bu bir ubershader yaklaşımı ile aynı şey değildir. Ubershaders, her şey için gereken tüm kodları tek bir gölgelendiriciye koyduğunuz yerdir, belki de #ifdefs ve üniformaları ve benzerlerini kullanarak bunları derlerken veya çalıştırırken açmak ve kapatmak için. Şahsen ubershader yaklaşımını hor görüyorum, ancak bazı oldukça etkileyici AAA motorları bunları kullanıyor (özellikle Crytek akla geliyor).

Gölgelendirici parçalarını çeşitli şekillerde kullanabilirsiniz. En gelişmiş yol - ve GLSL, HLSL ve konsolları desteklemeyi planlıyorsanız, yararlı - bir gölgelendirici dili için bir ayrıştırıcı yazmaktır (muhtemelen geliştiricileriniz tarafından maksimum "anlaşılabilirlik" için olabildiğince HLSL / Cg veya GLSL'ye yakın) ) daha sonra kaynaktan kaynağa çeviriler için kullanılabilir. Başka bir yaklaşım sadece gölgelendirici parçalarını XML dosyalarına veya benzerlerine sarmaktır, ör.

<shader name="example" type="pixel">
  <input name="color" type="float4" source="vertex" />
  <output name="color" type="float4" target="output" index="0" />
  <glsl><![CDATA[
     output.color = vec4(input.color.r, 0, 0, 1);
  ]]></glsl>
</shader>

Bu yaklaşımla farklı API'ler için birden fazla kod bölümü oluşturabileceğinizi ve hatta kod bölümünü güncelleyebileceğinizi unutmayın (böylece bir GLSL 1.20 sürümüne ve bir GLSL 3.20 sürümüne sahip olabilirsiniz). Grafiğiniz, uyumlu kod bölümü olmayan gölgelendirici parçalarını otomatik olarak hariç tutabilir, böylece eski donanımda yarı zarif bir bozulma elde edebilirsiniz (bu nedenle normal haritalama veya programlayıcının ihtiyacı olmadan destekleyemeyen eski donanımlarda hariç tutulan her şey bir sürü açık kontrol yapın).

Daha sonra XMl örneği benzer bir şey üretebilir (bu geçersiz GLSL ise özür dilerim, kendimi bu API'ya maruz bıraktığımdan beri bir süre geçti):

layout (location=0) in vec4 input_color;
layout (location=0) out vec4 output_color;

struct Input {
  vec4 color;
};
struct Output {
  vec4 color;
}

void main() {
  Input input;
  input.color = input_color;
  Output output;

  // Source: example.shader
#line 5
  output.color = vec4(input.color.r, 0, 0, 1);

  output_color = output.color;
}

Biraz daha akıllı olabilir ve daha "verimli" kod üretebilirsiniz, ancak dürüst olmak gerekirse, toplam bok olmayan herhangi bir gölgelendirici derleyicisi sizin için üretilen koddan fazlalıkları kaldıracaktır. Belki daha yeni GLSL, dosya adını #lineşimdi de komutlara koymanızı sağlar , ancak eski sürümlerin çok eksik olduğunu ve bunu desteklemediğini biliyorum.

Birden fazla parçaya sahipseniz, girişleri (ağaçtaki bir ata parçası yığını tarafından çıkış olarak sağlanmaz), çıktılar gibi giriş bloğuna birleştirilir ve kod da birleştirilir. Aşamaların eşleşmesini (tepe noktasıyla parça arası) ve köşe özelliği giriş düzenlerinin "sadece işe yarar" olmasını sağlamak için biraz fazladan çalışma yapılır. Bu yaklaşımın bir başka güzel yararı, GLSL'nin eski sürümlerinde desteklenmeyen açık üniforma ve giriş özniteliği bağlanma indeksleri yazabilmeniz ve bunları gölgelendirici oluşturma / bağlama kütüphanenizde işlemenizdir. Benzer şekilde, glVertexAttribPointeruyumluluk sağlamak ve her şeyin "sadece işe yaraması" için VBO'larınızı ve çağrılarınızı ayarlarken meta verileri kullanabilirsiniz .

Maalesef bunun gibi iyi bir çapraz API kütüphanesi yok. Cg biraz yaklaşıyor, ancak AMD kartlarında OpenGL için bok desteği var ve en temel kod oluşturma özelliklerinden herhangi birini kullanırsanız son derece yavaş olabilir. DirectX efektleri çerçevesi de çalışır, ancak elbette HLSL dışında herhangi bir dil için sıfır desteğe sahiptir. GLXL için DirectX kütüphanelerini taklit eden ancak son kontrol ettiğimde durumlarına bakıldığında sadece kendim yazacağım bazı eksik / buggy kütüphaneleri var.

Ubershader yaklaşımı sadece belirli özellikler için "iyi bilinen" önişlemci direktiflerini tanımlamak ve daha sonra farklı konfigürasyona sahip farklı malzemeler için yeniden derlemek anlamına gelir. Örneğin, normal bir haritaya sahip herhangi bir malzeme için tanımlayabilir USE_NORMAL_MAPPING=1ve daha sonra piksel aşamalı ubershader'ınızda şunları yapabilirsiniz:

#if USE_NORMAL_MAPPING
  vec4 normal;
  // all your normal mapping code
#else
  vec4 normal = normalize(in_normal);
#endif

Burada büyük bir sorun, kullanımda olan tüm kombinasyonları önceden derlemeniz gereken önceden derlenmiş HLSL için bu sorunla başa çıkmaktır. GLSL ile bile, aynı gölgelendiricilerin yeniden derlenmesini / önbelleğe alınmasını önlemek için kullanılan tüm önişlemci yönergelerinin bir anahtarını düzgün bir şekilde oluşturabilmeniz gerekir. Üniforma kullanmak karmaşıklığı azaltabilir, ancak önişlemci üniformalarının aksine talimat sayısını azaltmaz ve yine de performans üzerinde küçük bir etkisi olabilir.

Açık olmak gerekirse, her iki yaklaşım da (sadece bir ton gölgelendiricinin elle yazılmasının yanı sıra) AAA alanında kullanılır. Sizin için en uygun olanı kullanın.

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.