Co powiesz na coś takiego?
Nie rysuj swojego oświetlenia, przyciemniając duszki kafelkowe. Narysuj nieoświetlone kafelki na celu renderowania, a następnie narysuj światła kafelków na drugim celu renderowania, reprezentując każdy z nich jako prostokąt w skali szarości pokrywający obszar kafelka. Aby wyrenderować ostatnią scenę, użyj modułu cieniującego do połączenia dwóch celów renderowania, przyciemniając każdy piksel pierwszego zgodnie z wartością drugiego.
To da dokładnie to, co masz teraz. To ci nie pomaga, więc zmieńmy to trochę.
Zmień wymiary celu renderowania lightmapy, tak aby każdy kafelek był reprezentowany przez pojedynczy piksel , a nie prostokątny obszar. Podczas komponowania końcowej sceny użyj stanu próbnika z filtrowaniem liniowym. W przeciwnym razie pozostaw wszystko inne bez zmian.
Zakładając, że poprawnie napisałeś moduł cieniujący, lightmap powinien być skutecznie „powiększany” podczas komponowania. Dzięki temu uzyskasz ładny efekt gradientu za darmo za pomocą próbnika tekstur urządzenia graficznego.
Być może będziesz w stanie wyciąć moduł cieniujący i zrobić to prościej z „zaciemniającym” stanem BlendState, ale musiałbym z nim poeksperymentować, zanim przedstawię ci szczegóły.
AKTUALIZACJA
Miałem dzisiaj trochę czasu, żeby sobie z tego wyśmiewać. Powyższa odpowiedź odzwierciedla mój nawyk używania shaderów jako mojej pierwszej odpowiedzi na wszystko, ale w tym przypadku nie są one faktycznie konieczne, a ich użycie niepotrzebnie komplikuje rzeczy.
Jak zasugerowałem, możesz osiągnąć dokładnie ten sam efekt za pomocą niestandardowego BlendState. W szczególności ten niestandardowy stan BlendState:
BlendState Multiply = new BlendState()
{
AlphaSourceBlend = Blend.DestinationAlpha,
AlphaDestinationBlend = Blend.Zero,
AlphaBlendFunction = BlendFunction.Add,
ColorSourceBlend = Blend.DestinationColor,
ColorDestinationBlend = Blend.Zero,
ColorBlendFunction = BlendFunction.Add
};
Równanie mieszania jest
result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)
Dzięki naszemu niestandardowemu programowi BlendState tak się stanie
result = (lightmapColor * destinationColor) + (0)
Co oznacza, że kolor źródłowy czystej bieli (1, 1, 1, 1) zachowa kolor docelowy, kolor źródłowy czystej czerni (0, 0, 0, 1) przyciemni kolor docelowy do czystej czerni i dowolne odcień szarości pomiędzy nimi przyciemni docelowy kolor o średnią wartość.
Aby zastosować to w praktyce, najpierw zrób wszystko, co musisz zrobić, aby stworzyć lightmap:
var lightmap = GetLightmapRenderTarget();
Następnie po prostu narysuj nieoświetloną scenę bezpośrednio w buforze backbuff, jak zwykle:
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();
Następnie narysuj lightmap za pomocą niestandardowego 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();
Spowoduje to pomnożenie koloru docelowego (nieoświetlone płytki) przez kolor źródłowy (lightmap), odpowiednie przyciemnienie nieoświetlonych płytek i stworzenie efektu gradientu w wyniku skalowania tekstury lightmap do wymaganego rozmiaru.