Come posso ottenere un effetto di illuminazione 2D uniforme?


18

Sto realizzando un gioco basato su tessere 2D in XNA.

Attualmente il mio fulmine assomiglia a questo .

Come posso farlo sembrare come questo ?

Invece che ogni blocco abbia la sua tinta, ha una sovrapposizione liscia.

Sto assumendo una sorta di shader e passando i valori di illuminazione per le piastrelle circostanti allo shader, ma sono un principiante con gli shader, quindi non ne sono sicuro.

La mia illuminazione attuale calcola la luce, quindi la passa a SpriteBatche disegna con il parametro tint. Ogni piastrella ha un valore Colorcalcolato prima del sorteggio nel mio algoritmo di illuminazione, che viene utilizzato per la tinta.

Ecco un esempio di come attualmente calcolo l'illuminazione (lo faccio anche da sinistra, destra e in basso, ma mi sono davvero stancato di fare questo fotogramma per fotogramma ...)

inserisci qui la descrizione dell'immagine

Quindi effettivamente ottenere e disegnare la luce finora non è un problema !

Ho visto innumerevoli tutorial sulla nebbia di guerra e sull'utilizzo di sovrapposizioni circolari sfumate per creare un fulmine uniforme, ma ho già un buon metodo per assegnare a ciascuna piastrella un valore di fulmine, deve solo essere levigato tra di loro.

Quindi per rivedere

  • Calcola illuminazione (Fine)
  • Disegna tessere (Fatto, so che dovrò modificarlo per lo shader)
  • Ombra tessere (Come passare valori e applicare "gradiente)

Risposte:


6

Che ne dici di questo?

Non disegnare la tua illuminazione colorando gli sprite delle piastrelle. Disegna le tessere non illuminate su una destinazione di rendering, quindi disegna le luci delle tessere su una seconda destinazione di rendering, rappresentandole ognuna come un rettangolo in scala di grigi che copre l'area della piastrella. Per eseguire il rendering della scena finale, utilizzare uno shader per combinare i due target di rendering, scurendo ciascun pixel del primo in base al valore del secondo.

Questo produrrà esattamente quello che hai ora. Questo non ti aiuta, quindi cambiamolo un po '.

Modifica le dimensioni del target di rendering della mappa luminosa in modo che ogni riquadro sia rappresentato da un singolo pixel , anziché da un'area rettangolare. Quando componi la scena finale, usa uno stato campionatore con filtro lineare. Altrimenti lascia tutto il resto uguale.

Supponendo di aver scritto correttamente lo shader, la mappa luminosa dovrebbe essere effettivamente "ingrandita" durante la composizione. Questo ti offrirà un piacevole effetto gradiente gratuitamente tramite il campionatore di texture del dispositivo grafico.

Potresti anche essere in grado di eliminare lo shader e farlo in modo più semplice con un BlendState "oscurante", ma dovrei sperimentarlo prima di poterti dare le specifiche.

AGGIORNARE

Oggi ho avuto un po 'di tempo per prendere in giro questo. La risposta sopra riflette la mia abitudine di usare gli shader come prima risposta a tutto, ma in questo caso non sono effettivamente necessari e il loro uso complica inutilmente le cose.

Come ho suggerito, puoi ottenere esattamente lo stesso effetto usando un BlendState personalizzato. In particolare, questo BlendState personalizzato:

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

L'equazione di fusione è

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

Quindi, con il nostro BlendState personalizzato, diventa

result = (lightmapColor * destinationColor) + (0)

Ciò significa che un colore sorgente di bianco puro (1, 1, 1, 1) preserverà il colore di destinazione, un colore sorgente di nero puro (0, 0, 0, 1) scurirà il colore di destinazione in nero puro e qualsiasi tonalità di grigio in mezzo scurirà il colore di destinazione di una quantità media.

Per metterlo in pratica, prima fai tutto ciò che devi fare per creare la tua mappa luminosa:

var lightmap = GetLightmapRenderTarget();

Quindi disegna la tua scena non illuminata direttamente sul backbuffer come faresti normalmente:

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

Quindi disegna la mappa luminosa usando BlendState personalizzato:

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

Ciò moltiplica il colore di destinazione (riquadri non illuminati) per il colore di origine (mappa luminosa), scurendo opportunamente le tessere non illuminate e creando un effetto gradiente a seguito del ridimensionamento della trama della mappa luminosa alla dimensione necessaria.


Sembra abbastanza semplice, vedrò cosa posso fare dopo. Ho provato qualcosa di simile in precedenza (ho reso la mappa luminosa su tutto il resto e ho usato uno shader di sfocatura, ma non ho potuto rendere il renderTarget "trasparente" aveva una sovrapposizione viola, ma volevo che vedesse attraverso il livello di piastrelle
attuall

Dopo aver impostato la destinazione di rendering sul dispositivo, chiamare device.Clear (Color.Transparent);
Cole Campbell,

Anche se, in questo caso, vale la pena sottolineare che la tua mappa luminosa dovrebbe probabilmente essere chiarita in bianco o nero, rispettivamente in piena luce e in completa oscurità.
Cole Campbell,

Penso che sia quello che ho provato prima ma era ancora viola, beh, lo guarderò domani quando avrò del tempo per lavorarci.
Cyral,

Quasi tutto è stato risolto, ma passa da nero a bianco, anziché da nero a trasparente. La parte shader è ciò su cui sono confuso, come scriverne uno per oscurare la scena originale non illuminata con quella della nuova.
Cyral,

10

Ok, ecco un metodo molto semplice per creare alcuni fulmini 2D semplici e fluidi, renderizzati in tre passaggi:

  • Passa 1: il mondo di gioco senza fulmini.
  • Pass 2: il lampo senza il mondo
  • Combinazione del passaggio 1. e 2. visualizzato sullo schermo.

Nel passaggio 1 si disegna tutti gli sprite e il terreno.

Nel passaggio 2 si disegna un secondo gruppo di folletti che sono le sorgenti luminose. Dovrebbero apparire simili a questo:
inserisci qui la descrizione dell'immagine
inizializza il target di rendering con il nero e disegna quegli sprite su di esso con la fusione massima o additiva.

Nel passaggio 3 si combinano i due passaggi precedenti. Esistono diversi modi per combinarli. Ma il metodo più semplice e meno artistico è fonderli insieme tramite Moltiplica. Questo sarebbe simile a questo:
inserisci qui la descrizione dell'immagine


Il fatto è che ho già un sistema di illuminazione e le luci puntiformi non funzionano qui a causa di alcuni oggetti che necessitano di luce per passare e altri no.
Cyral,

2
Bene, è più complicato. Dovrai eseguire il cast di ray-cast per ottenere le ombre. Teraria usa grandi blocchi per il loro terreno, quindi non c'è problema. Potresti usare un ray-casting impreciso, ma non sarebbe meglio di terraria e potrebbe adattarsi anche meno se non hai bloccato la grafica. Per una tecnica avanzata e di bell'aspetto c'è questo articolo: gamedev.net/page/resources/_/technical/…
API-Beast

2

Il secondo link che hai pubblicato sembra una sorta di nebbia di guerra , ci sono un paio di altre domande su questo

Mi sembra una trama , in cui "cancelli" i frammenti della sovrapposizione nera (impostando l'alfa di quei pixel su 0) mentre il giocatore avanza.

Direi che la persona deve aver usato un tipo di pennello "cancella" dove il giocatore ha esplorato.


1
Non è nebbia di guerra, calcola il fulmine di ogni fotogramma. Il gioco che ho sottolineato è simile a Terraria.
Cyral,

Quello che volevo dire è che potrebbe essere implementato in modo simile a fog of war
bobobobo,

2

Vertici chiari (angoli tra le tessere) anziché tessere. Mescola l'illuminazione su ogni riquadro in base ai suoi quattro vertici.

Esegui test della linea di vista su ciascun vertice per determinare quanto è illuminato (puoi fare in modo che un blocco blocchi tutta la luce o riduca la luce, ad esempio conta quante intersezioni con ciascun vertice combinato con la distanza per calcolare un valore di luce piuttosto che usare un test binario puro visibile / non visibile).

Quando si esegue il rendering di un riquadro, inviare il valore di luce per ciascun vertice alla GPU. Puoi facilmente usare uno shader di frammenti per prendere il valore di luce interpolato su ciascun frammento nello sprite della piastrella per illuminare uniformemente ogni pixel. È passato abbastanza tempo da quando ho toccato GLSL che non mi sentirei a mio agio nel fornire un vero esempio di codice, ma in pseudo codice sarebbe semplice come:

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

Lo shader di vertice deve semplicemente passare i valori di input allo shader di frammento, nulla di complicato a distanza.

Otterrai un'illuminazione ancora più uniforme con più vertici (ad esempio, se ogni riquadro viene renderizzato come quattro sottopiastrelle), ma ciò potrebbe non valere la pena a seconda della qualità che stai cercando. Provalo prima nel modo più semplice.


1
line-of sight tests to each vertex? Sarà costoso.
ashes999,

Puoi ottimizzare con un po 'di intelligenza. Per un gioco 2D è possibile eseguire un numero sorprendente di test di raggio su una griglia rigorosa abbastanza rapidamente. Invece di un semplice test LOS potresti "fluire" (non posso per la vita di me pensare al termine corretto in questo momento; notte della birra) i valori di luce sui vertici piuttosto che fare anche i test dei raggi.
Sean Middleditch

L'uso dei test della linea di vista complicherebbe ulteriormente questo aspetto, ho già capito l'illuminazione. Ora, quando invio i 4 verticies allo shader, come posso farlo? So che non ti senti a tuo agio nel fornire un vero esempio di codice, ma se potessi ottenere qualche informazione in più sarebbe bello. Ho anche modificato la domanda con maggiori informazioni.
Cyral,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.