Pürüzsüz bir 2D aydınlatma efekti nasıl elde edebilirim?


18

XNA'da 2B karo tabanlı bir oyun yapıyorum.

Şu anda benim yıldırım gibi görünüyor bu .

Nasıl benziyor alabilirsiniz bu ?

Her bir bloğun kendi renk tonu yerine, düzgün bir katmanı vardır.

Bir çeşit gölgelendiriciyi varsayıyorum ve çevreleyen karoların ışık değerlerini gölgelendiriciye geçiriyorum, ama gölgelendiricilerle yeni başlayan biriyim, bu yüzden emin değilim.

Geçerli ışığım ışığı hesaplar ve ardından a öğesine geçirir SpriteBatchve renk tonu parametresiyle çizer. Her kiremit, Colorrenk tonu için kullanılan aydınlatma algoritmamda çizilmeden önce hesaplanan bir karoya sahiptir .

İşte şu anda aydınlatmayı nasıl hesapladığım üzerine bir örnek (Bunu soldan, sağdan ve alttan da yapıyorum, ancak bu çerçeveyi kare kare yapmaktan gerçekten yoruldum ...)

resim açıklamasını buraya girin

Yani aslında ışığı alıp çizmek hiç sorun değil !

Savaş sisi ve düzgün yıldırım oluşturmak için degrade dairesel bindirmeler kullanarak sayısız öğretici gördüm, ancak zaten her karoya bir yıldırım değeri atamak için güzel bir yöntemim var, sadece aralarında düzeltilmesi gerekiyor.

İncelemek için

  • Aydınlatmayı Hesapla (Tamamlandı)
  • Fayans Çiz (Bitti, gölgelendirici için değiştirmem gerekeceğini biliyorum)
  • Gölge döşemeleri (Değerler nasıl geçirilir ve "gradyan nasıl uygulanır)

Yanıtlar:


6

Böyle bir şeye ne dersin?

Karo spritelarınızı renklendirerek aydınlatmanızı çizmeyin. Senin çizin sönük bir hale hedefe fayans sonra karonun alanı kapsayan bir gri tonlama dikdörtgen olarak her birini temsil eden ikinci bir hale hedefe kiremit ışıkları çizin. Son sahneyi oluşturmak için, iki render hedefini birleştirmek için bir gölgelendirici kullanın ve birincinin her pikselini ikincinin değerine göre koyulaştırın.

Bu tam olarak şimdi sahip olduklarınızı üretecektir. Bu size yardımcı olmuyor, bu yüzden biraz değiştirelim.

Işık haritası oluşturma hedefinizin boyutlarını, her döşemenin dikdörtgen bir alan yerine tek bir pikselle temsil edileceği şekilde değiştirin . Son sahneyi birleştirirken, doğrusal filtrelemeli bir örnekleyici durumu kullanın. Aksi takdirde her şeyi aynı bırakın.

Gölgelendiricinizi doğru yazdığınız varsayılarak, ışık haritasının birleştirme sırasında etkili bir şekilde "ölçeklendirilmesi" gerekir. Bu, grafik cihazının doku örnekleyicisi aracılığıyla ücretsiz olarak güzel bir gradyan efekti sağlayacaktır.

Ayrıca gölgelendiriciyi kesebilir ve bunu daha basit bir 'kararan' BlendState ile yapabilirsiniz, ancak size detayları vermeden önce denemek zorunda kalacağım.

GÜNCELLEME

Bugün bununla alay etmek için biraz zamanım vardı. Yukarıdaki cevap, gölgelendiricileri her şeye ilk cevabım olarak kullanma alışkanlığımı yansıtıyor, ancak bu durumda aslında gerekli değiller ve kullanımları bir şeyleri gereksiz yere karmaşıklaştırıyor.

Önerdiğim gibi, özel bir BlendState kullanarak tam olarak aynı efekti gerçekleştirebilirsiniz. Özellikle, bu özel BlendState:

BlendState Multiply = new BlendState()
{
    AlphaSourceBlend = Blend.DestinationAlpha,
    AlphaDestinationBlend = Blend.Zero,
    AlphaBlendFunction = BlendFunction.Add,
    ColorSourceBlend = Blend.DestinationColor,
    ColorDestinationBlend = Blend.Zero,
    ColorBlendFunction = BlendFunction.Add
}; 

Karıştırma denklemi

result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)

Böylece özel BlendState'imizle,

result = (lightmapColor * destinationColor) + (0)

Bu, saf beyazın (1, 1, 1, 1) kaynak renginin hedef rengi koruyacağı, saf siyahın (0, 0, 0, 1) kaynak renginin hedef rengi saf siyaha koyulaştıracağı anlamına gelir. aradaki gri tonu, hedef rengi orta düzeyde koyulaştırır.

Bunu uygulamaya koymak için önce ışık haritanızı oluşturmak için ne yapmanız gerekiyorsa yapın:

var lightmap = GetLightmapRenderTarget();

Ardından, ışıksız sahnenizi normalde yaptığınız gibi doğrudan backbuffer'a çizin :

spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();

Ardından özel BlendState kullanarak ışık eşlemesini çizin:

var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;

spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();

Bu, hedef rengi (ışıksız döşemeler) kaynak renkle (ışık haritası) çarpar, ışıksız döşemeleri uygun şekilde koyulaştırır ve ışık haritası dokusunun gerekli boyuta yükseltilmesinin bir sonucu olarak degrade efekti oluşturur.


Bu oldukça basit görünüyor, daha sonra ne yapabileceğimi göreceğim. Daha önce bir şey denedim (ışık haritasını her şeyin üzerinde oluşturdum ve bir bulanıklık gölgelendirici kullandım, ancak renderTarget'i mor bir kaplamaya sahip "şeffaf" yapamadım, ancak aktif karo katmanına kadar görmek istedim)
Cyral

Oluşturma hedefinizi cihazda ayarladıktan sonra cihazı arayın.Clear (Renkli Şeffaf);
Cole Campbell

Bu durumda, ışık haritanızın muhtemelen beyaz veya siyah olarak temizlenmesi gerektiği, sırasıyla tam ışık ve tam karanlık olduğu belirtilmelidir.
Cole Campbell

Sanırım daha önce denediğim bu ama yine de mor, yarın bunun üzerinde çalışmak için zamanım olduğunda bakacağım.
Cyral

Neredeyse hepsi işe yaradı, ama siyah yerine şeffaftan siyahtan beyaza kayboluyor. Gölgelendirici kısım, kafamın karıştığı, orijinal ışıksız sahneyi yenisinin sahnesine karartmak için nasıl yazacağım.
Cyral

10

Tamam, bazı basit ve pürüzsüz 2D yıldırım oluşturmak için üç basit bir yöntem:

  • Pass 1: Şimşeksiz oyun dünyası.
  • Pass 2: Dünyasız Yıldırım
  • Ekranda görüntülenen 1. ve 2. geçişlerin kombinasyonu.

1. geçişte tüm spriteları ve araziyi çizersiniz.

2. geçişte ışık kaynakları olan ikinci bir grup sprite çiziyorsunuz. Şuna benzer olmalıdırlar:
resim açıklamasını buraya girin
Oluşturma hedefini siyahla başlatın ve bu spriteları Maksimum veya Katkı harmanlamasıyla üzerine çizin.

3. geçişte önceki iki geçişi birleştirirsiniz. Bunların nasıl birleştirilebileceği birçok farklı yolu vardır. Ancak en basit ve en az iddialı yöntem, bunları Multiply ile karıştırmaktır. Bu şöyle görünecektir:
resim açıklamasını buraya girin


Mesele şu ki, zaten bir aydınlatma sistemim var ve bazı ışıklar geçmesi için ışığa ihtiyaç duyan bazı nesneler nedeniyle nokta ışıkları burada çalışmıyor.
Cyral

2
Bu daha zor. Gölgeleri elde etmek için ışın atmanız gerekir. Teraria, arazileri için büyük bloklar kullanıyor, bu yüzden orada sorun yok. Kesin olmayan ışın dökümü kullanabilirsiniz, ancak bu terraria'dan daha iyi görünmez ve grafikleri engellemediyseniz daha da az sığabilir. Gelişmiş ve iyi görünümlü bir teknik için bu makale var: gamedev.net/page/resources/_/technical/…
API-Beast

2

Gönderdiğiniz ikinci bağlantı bir çeşit savaş sisi gibi görünüyor , bu konuda başka birkaç soru var

Bu bana bir doku gibi görünüyor , burada siyah yer paylaşımının bitlerini (bu piksellerin alfa'sını 0 olarak ayarlayarak) oyuncu ilerledikçe "sildiğiniz" gibi görünüyor .

Bu kişinin oyuncunun keşfettiği bir fırça türü "silme" kullanması gerektiğini söyleyebilirim.


1
Savaş sisi değil, her karenin yıldırımını hesaplar. İşaret ettiğim oyun Terraria'ya benziyor.
Cyral

Demek istediğim, savaş
sisine

2

Fayans yerine açık köşeler (fayanslar arasındaki köşeler). Dört köşeye göre her döşemede aydınlatmayı harmanlayın.

Ne kadar yakıldığını belirlemek için her bir tepe noktasına görüş hattı testleri yapın (bir bloğu tüm ışığı durdurabilir veya ışığı azaltabilirsiniz, örneğin, bir ışık değerini hesaplamak yerine her bir tepe noktasına kaç kavşağın mesafeyle birleştiğini sayın saf ikili görünür / görünmez test).

Bir döşemeyi oluştururken, her tepe noktası için ışık değerini GPU'ya gönderin. Her pikseli düzgün bir şekilde aydınlatmak için döşemenin hareketli grafiğindeki her bir parçadaki enterpolasyonlu ışık değerini almak için kolayca bir parça gölgelendirici kullanabilirsiniz. GLSL'ye dokunduğumdan beri gerçek bir kod örneği vererek rahat hissetmeyecektim, ancak sözde kodda olduğu kadar basit olurdu:

texture t_Sprite
vec2 in_UV
vec4 in_Light // coudl be one float; assuming you might want colored lights though

fragment shader() {
  output = sample2d(t_Sprite, in_UV) * in_Light;
}

Köşe gölgelendirici, giriş değerlerini parça gölgelendiriciye aktarmak zorundadır, hiçbir şey uzaktan bile karmaşık değildir.

Daha fazla köşeye sahip daha pürüzsüz bir aydınlatma elde edersiniz (örneğin, her bir karo dört alt karo olarak işlenmişse), ancak gittiğiniz kaliteye bağlı olarak bu çabaya değmeyebilir. İlk önce daha basit bir şekilde deneyin.


1
line-of sight tests to each vertex? Bu pahalı olacak.
ashes999

Biraz zekayla optimize edebilirsiniz. Bir 2D oyun için, oldukça hızlı bir şekilde katı bir ızgaraya karşı şaşırtıcı sayıda ışın testi yapabilirsiniz. Düz bir LOS testi yerine, "akabilir" (benim hayatım için şu anda doğru terimi düşünemiyorum; bira gecesi) ışın değerlerini yapmaktan ziyade köşelerdeki ışık değerlerini de yapabilirsiniz.
Sean Middleditch

Görüş hattı testlerini kullanmak bunu daha da zorlaştırabilir, zaten ışıklandırdım. Şimdi 4 verticiyi gölgelendiriciye gönderdiğimde, bunu nasıl yapabilirim? Gerçek bir kod örneği vererek rahat hissetmiyorum, ama biraz daha fazla bilgi alabilirdim, bu iyi olurdu. Soruyu daha fazla bilgi ile de düzenledim.
Cyral
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.