Que tal algo como isso?
Não desenhe sua iluminação pintando seus sprites de azulejos. Desenhe seus blocos apagados em um destino de renderização e depois as luzes do bloco em um segundo alvo de renderização, representando cada um como um retângulo em escala de cinza que cobre a área do bloco. Para renderizar a cena final, use um shader para combinar os dois destinos de renderização, escurecendo cada pixel do primeiro de acordo com o valor do segundo.
Isso produzirá exatamente o que você tem agora. Isso não ajuda, então vamos mudar um pouco.
Altere as dimensões do seu destino de renderização do lightmap para que cada bloco seja representado por um único pixel , em vez de uma área retangular. Ao compor a cena final, use um estado de amostrador com filtragem linear. Caso contrário, deixe tudo o mesmo.
Supondo que você tenha escrito seu shader corretamente, o lightmap deve ser efetivamente "ampliado" durante a composição. Isso proporcionará um bom efeito de gradiente gratuitamente através do amostrador de textura do dispositivo gráfico.
Você também pode cortar o sombreador e fazê-lo de maneira mais simples com um BlendState 'escurecido', mas precisaria experimentar com ele antes que eu pudesse fornecer os detalhes.
ATUALIZAR
Hoje tive tempo para zombar disso. A resposta acima reflete meu hábito de usar shaders como minha primeira resposta a tudo, mas nesse caso eles não são realmente necessários e seu uso complica desnecessariamente as coisas.
Como sugeri, você pode obter exatamente o mesmo efeito usando um BlendState personalizado. Especificamente, este BlendState personalizado:
BlendState Multiply = new BlendState()
{
AlphaSourceBlend = Blend.DestinationAlpha,
AlphaDestinationBlend = Blend.Zero,
AlphaBlendFunction = BlendFunction.Add,
ColorSourceBlend = Blend.DestinationColor,
ColorDestinationBlend = Blend.Zero,
ColorBlendFunction = BlendFunction.Add
};
A equação de mesclagem é
result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)
Portanto, com o nosso BlendState personalizado, isso se torna
result = (lightmapColor * destinationColor) + (0)
O que significa que uma cor de origem em branco puro (1, 1, 1, 1) preservará a cor de destino, uma cor de origem em preto puro (0, 0, 0, 1) escurecerá a cor de destino em preto puro e qualquer um tom de cinza no meio escurecerá a cor de destino em uma quantidade intermediária.
Para colocar isso em prática, primeiro faça o que for necessário para criar seu mapa de luz:
var lightmap = GetLightmapRenderTarget();
Em seguida, basta desenhar sua cena apagada diretamente no backbuffer, como faria normalmente:
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();
Em seguida, desenhe o mapa de luz usando o BlendState personalizado:
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();
Isso multiplicará a cor de destino (ladrilhos apagados) pela cor de origem (mapa de luz), escurecendo adequadamente os ladrilhos apagados e criando um efeito de gradiente como resultado da escala da textura do mapa de luz no tamanho necessário.
Ok, aqui está um método muito simples para criar um raio 2D simples e suave, renderizar em três passagens:
No passe 1, você desenha todos os sprites e o terreno.
No passe 2, você desenha um segundo grupo de sprites que são as fontes de luz. Eles devem ser parecidos com este:
Inicialize o alvo de renderização com preto e desenhe esses sprites nele com a mistura Máxima ou Aditiva.
No passe 3, você combina os dois passes anteriores. Existem várias maneiras diferentes de como elas podem ser combinadas. Mas o método mais simples e menos artístico é combiná-los via Multiply. Isso ficaria assim:
fonte
O segundo link que você postou parece uma espécie de nevoeiro de guerra , existem algumas outras perguntas sobre isso
Isso me parece uma textura , onde você "apaga" os bits da sobreposição preta (configurando o alfa desses pixels como 0) à medida que o player avança.
Eu diria que a pessoa deve ter usado um tipo de pincel "apagar" onde o jogador explorou.
fonte
Vértices claros (cantos entre ladrilhos) em vez de ladrilhos. Misture a iluminação em cada bloco com base em seus quatro vértices.
Faça testes de linha de visão para cada vértice para determinar como está aceso (você pode fazer com que um bloco pare toda luz ou diminua a luz, por exemplo, conte quantas interseções em cada vértice combinam com a distância para calcular um valor de luz em vez de usar um teste binário puro visível / não visível).
Ao renderizar um bloco, envie o valor da luz para cada vértice para a GPU. Você pode usar facilmente um sombreador de fragmento para obter o valor da luz interpolada em cada fragmento no sprite do bloco para iluminar cada pixel suavemente. Já faz muito tempo desde que eu toquei no GLSL que não me sentiria confortável em fornecer um exemplo de código real, mas no pseudo-código seria tão simples quanto:
O sombreador de vértice simplesmente precisa passar os valores de entrada para o sombreador de fragmento, nada complicado nem remotamente.
Você obterá uma iluminação ainda mais suave com mais vértices (por exemplo, se cada bloco for renderizado como quatro sub-blocos), mas isso pode não valer a pena, dependendo da qualidade que você deseja. Experimente da maneira mais simples primeiro.
fonte
line-of sight tests to each vertex
? Isso vai ser caro.