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.