Bagaimana saya bisa mendapatkan efek pencahayaan 2D yang halus?

18

Saya membuat game berbasis ubin 2D di XNA.

Saat ini kilat saya terlihat seperti ini .

Bagaimana saya bisa membuatnya terlihat seperti ini ?

Alih-alih setiap blok memiliki warna sendiri, ia memiliki lapisan halus.

Saya mengasumsikan semacam shader, dan untuk memberikan nilai pencahayaan untuk ubin sekitarnya ke shader, tapi saya seorang pemula dengan shader jadi saya tidak yakin.

Pencahayaan saya saat ini menghitung cahaya, dan kemudian meneruskannya ke SpriteBatchdan menggambar dengan parameter tint. Setiap ubin memiliki Coloryang dihitung sebelum menggambar dalam algoritma pencahayaan saya, yang digunakan untuk warna ini.

Berikut adalah contoh bagaimana saya menghitung pencahayaan saat ini (saya melakukan ini dari kiri, kanan, dan bawah juga, tapi saya benar-benar lelah melakukan bingkai ini dengan bingkai ...)

masukkan deskripsi gambar di sini

Jadi mendapatkan dan menggambar cahaya sejauh ini bukanlah masalah !

Saya telah melihat banyak tutorial tentang kabut perang dan menggunakan gradien melingkar overlay untuk membuat kilat halus, tetapi saya sudah memiliki metode yang bagus untuk menetapkan setiap ubin nilai kilat, hanya perlu diperhalus di antara mereka.

Jadi untuk review

  • Hitung Pencahayaan (Selesai)
  • Draw Tiles (Selesai, saya tahu saya harus memodifikasinya untuk shader)
  • Ubin naungan (Cara meneruskan nilai dan menerapkan "gradien)
Cyral
sumber

Jawaban:

6

Bagaimana dengan sesuatu yang seperti ini?

Jangan menggambar pencahayaan Anda dengan mewarnai sprite ubin Anda. Gambar ubin yang belum menyala ke target render, lalu gambar lampu ubin ke target render kedua, yang mewakili masing-masing sebagai persegi panjang abu-abu yang menutupi area ubin. Untuk membuat adegan terakhir, gunakan shader untuk menggabungkan dua target render, menggelapkan setiap piksel yang pertama sesuai dengan nilai yang kedua.

Ini akan menghasilkan apa yang Anda miliki sekarang. Itu tidak membantu Anda, jadi mari kita ubah sedikit.

Ubah dimensi target render lightmap Anda sehingga setiap ubin diwakili oleh satu piksel , bukan area persegi panjang. Saat menyusun adegan akhir, gunakan status sampler dengan penyaringan linier. Kalau tidak biarkan semuanya tetap sama.

Dengan asumsi Anda telah menulis shader Anda dengan benar, lightmap harus secara efektif "ditingkatkan" selama pengomposisian. Ini akan memberi Anda efek gradien yang bagus secara gratis melalui sampler tekstur perangkat grafis.

Anda mungkin juga dapat memotong shader dan melakukan ini lebih sederhana dengan BlendState 'gelap', tetapi saya harus bereksperimen dengan itu sebelum saya bisa memberi Anda spesifik.

MEMPERBARUI

Saya punya waktu hari ini untuk benar-benar mengejek ini. Jawaban di atas mencerminkan kebiasaan saya menggunakan shader sebagai jawaban pertama saya untuk semuanya, tetapi dalam kasus ini mereka sebenarnya tidak perlu dan penggunaannya akan mempersulit hal-hal.

Seperti yang saya sarankan, Anda dapat mencapai efek yang persis sama menggunakan custom BlendState. Secara khusus, BlendState khusus ini:

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

Persamaan pencampuran adalah

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

Jadi dengan BlendState kebiasaan kami, itu menjadi

result = (lightmapColor * destinationColor) + (0)

Yang berarti bahwa sumber warna putih murni (1, 1, 1, 1) akan mempertahankan warna tujuan, sumber warna hitam murni (0, 0, 0, 1) akan menggelapkan warna tujuan menjadi hitam murni, dan naungan abu-abu di antaranya akan menggelapkan warna tujuan dengan jumlah lumayan.

Untuk mempraktikkannya, pertama-tama lakukan apa yang perlu Anda lakukan untuk membuat peta cahaya Anda:

var lightmap = GetLightmapRenderTarget();

Kemudian gambarkan adegan yang tidak terang langsung ke backbuffer seperti biasa:

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

Kemudian gambar lightmap menggunakan custom BlendState:

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();

Ini akan melipatgandakan warna tujuan (ubin yang tidak dinyalakan) dengan warna sumber (lightmap), secara tepat menggelapkan ubin yang tidak diterangi, dan menciptakan efek gradien sebagai akibat dari tekstur lightmap yang ditingkatkan ke ukuran yang diperlukan.

Cole Campbell
sumber
Ini tampaknya cukup sederhana, saya akan melihat apa yang bisa saya lakukan nanti. Saya mencoba sesuatu yang simlar sebelumnya (saya membuat lightmap lebih dari yang lain dan menggunakan blur shader, tapi saya tidak bisa membuat renderTarget "transparan" memiliki overlay ungu, tapi saya ingin melihat melalui lapisan ubin yang sebenarnya)
Cyral
Setelah menetapkan target render Anda pada perangkat, panggil device.Clear (Color.Transparent);
Cole Campbell
Meskipun, dalam hal ini, ada baiknya menunjukkan bahwa peta cahaya Anda mungkin harus dibersihkan menjadi putih atau hitam, yang masing-masing terang penuh dan gelap gulita.
Cole Campbell
Saya pikir itulah yang saya coba sebelumnya tetapi masih ungu, saya akan memeriksanya besok ketika saya punya waktu untuk mengerjakan ini.
Cyral
Hampir semua ini berhasil, tetapi memudar dari hitam menjadi putih, bukan hitam menjadi transparan. Bagian shader adalah apa yang saya bingung, bagaimana menulis satu untuk meredupkan adegan asli yang belum diterangi dengan yang baru.
Cyral
10

Oke di sini adalah salah satu metode yang sangat sederhana untuk membuat beberapa petir 2D sederhana dan halus, render dalam tiga lintasan:

  • Lulus 1: dunia game tanpa kilat.
  • Pass 2: Petir tanpa dunia
  • Kombinasi pass 1. dan 2. yang ditampilkan di layar.

Dalam lulus 1 Anda menggambar semua sprite dan medan.

Dalam lulus 2 Anda menggambar kelompok sprite kedua yang merupakan sumber cahaya. Mereka harus terlihat mirip dengan ini:
masukkan deskripsi gambar di sini
Inisialisasi target render dengan warna hitam dan gambar sprite ke atasnya dengan pencampuran Maksimum atau Aditif.

Dalam pass 3 Anda menggabungkan dua pass sebelumnya. Ada beberapa cara berbeda bagaimana mereka dapat digabungkan. Tetapi metode paling sederhana dan paling berseni adalah untuk menggabungkan mereka melalui Multiply Ini akan terlihat seperti ini:
masukkan deskripsi gambar di sini

API-Beast
sumber
Masalahnya, saya sudah memiliki sistem pencahayaan, dan lampu point tidak berfungsi di sini karena beberapa benda memerlukan cahaya untuk dilewati dan beberapa tidak.
Cyral
2
Nah itu lebih rumit. Anda harus melakukan ray-cast untuk mendapatkan bayangan. Teraria menggunakan blok besar untuk medan mereka sehingga tidak ada masalah di sana. Anda dapat menggunakan pengecoran sinar yang tidak tepat tetapi itu akan terlihat tidak lebih baik daripada terraria dan mungkin lebih cocok jika Anda belum memblokir gambar. Untuk teknik canggih dan tampan ada artikel ini: gamedev.net/page/resources/_/technical/…
API-Beast
2

Tautan kedua yang Anda poskan tampak seperti kabut perang , ada beberapa pertanyaan lain tentang ini

Bagi saya itu terlihat seperti tekstur , di mana Anda "menghapus" bit overlay hitam (dengan mengatur alpha dari piksel tersebut ke 0) saat pemain bergerak maju.

Saya akan mengatakan orang itu harus menggunakan jenis sikat "hapus" di mana pemain telah dieksplorasi.

bobobobo
sumber
1
Ini bukan kabut perang, Ini menghitung kilat setiap frame. Game yang saya tunjukkan mirip dengan Terraria.
Cyral
Yang saya maksudkan adalah itu mungkin diterapkan mirip dengan kabut perang
bobobobo
2

Vertikal ringan (sudut antara ubin) bukan ubin. Padukan pencahayaan di setiap ubin berdasarkan empat simpulnya.

Lakukan tes garis pandang ke setiap titik untuk menentukan seberapa terang itu (Anda bisa memiliki blok menghentikan semua cahaya atau mengurangi cahaya, misalnya menghitung berapa banyak persimpangan ke setiap titik dikombinasikan dengan jarak untuk menghitung nilai cahaya daripada menggunakan uji biner murni terlihat / tidak terlihat).

Saat merender ubin, kirim nilai lampu untuk setiap simpul ke GPU. Anda dapat dengan mudah menggunakan shader fragmen untuk mengambil nilai cahaya interpolasi pada setiap fragmen dalam sprite ubin untuk menerangi setiap piksel dengan lancar. Sudah cukup lama sejak saya menyentuh GLSL sehingga saya tidak akan merasa nyaman memberikan sampel kode yang nyata, tetapi dalam kode pseudo akan sesederhana:

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;
}

Vertex shader hanya perlu meneruskan nilai input ke shader fragmen, tidak ada yang rumit bahkan jauh.

Anda bahkan akan mendapatkan pencahayaan yang lebih halus dengan lebih banyak simpul (katakanlah, jika setiap ubin diberikan sebagai empat sub-ubin), tetapi itu mungkin tidak sepadan dengan usaha tergantung pada kualitas yang Anda inginkan. Cobalah dulu dengan cara yang lebih sederhana.

Sean Middleditch
sumber
1
line-of sight tests to each vertex? Itu akan mahal.
ashes999
Anda dapat mengoptimalkan dengan beberapa kepintaran. Untuk gim 2D, Anda dapat melakukan sejumlah tes ray secara mengejutkan dengan grid ketat dengan cukup cepat. Alih-alih tes LOS langsung Anda bisa "mengalir" (saya tidak bisa untuk seumur hidup saya memikirkan istilah yang tepat sekarang; bir malam) nilai-nilai cahaya atas simpul daripada melakukan tes ray juga.
Sean Middleditch
Menggunakan tes garis penglihatan akan mempersulit ini lagi, saya sudah menemukan pencahayaan. Sekarang ketika saya mengirim 4 vertikal ke shader, bagaimana saya bisa melakukannya? Saya tahu Anda tidak merasa nyaman memberikan sampel kode yang nyata, tetapi jika saya bisa mendapatkan sedikit informasi lagi, itu akan menyenangkan. Saya juga mengedit pertanyaan dengan info lebih lanjut.
Cyral