Criando transições de iluminação suaves usando blocos no jogo HTML5 / JavaScript

7

Estou tentando implementar um efeito de iluminação em um jogo HTML5 / JavaScript usando a substituição de blocos. O que tenho agora é meio que trabalho, mas as transições não parecem suficientemente suaves / naturais à medida que a fonte de luz se move. Aqui está onde estou agora:

  1. No momento, tenho um mapa de plano de fundo que possui uma planilha PNG de espectro de luz / sombra aplicada, passando do azulejo mais escuro ao transparente. Por padrão, o bloco mais escuro é desenhado em todo o nível no lançamento, cobrindo todas as outras camadas, etc.
  2. Estou usando meus tamanhos predeterminados de bloco (40 x 40px) para calcular a posição de cada bloco e armazenar suas coordenadas xey em uma matriz.
  3. Em seguida, estou gerando uma entidade "bloco de grade" transparente de 40 x 40px em cada posição na matriz
  4. O mecanismo que estou usando (ImpactJS) permite calcular a distância da minha entidade de fonte de luz a todas as instâncias dessa entidade de bloco de grade.
  5. Posso então substituir o ladrilho embaixo de cada um desses ladrilhos de bloco de grade por um ladrilho da transparência apropriada.

Atualmente, estou fazendo o cálculo como este em cada instância da entidade do bloco de grade que é gerada no mapa:

var dist = this.distanceTo( ig.game.player );
var percentage = 100 * dist / 960;

if (percentage < 2) {
    // Spawns tile 64 of the shadow spectrum tilesheet at the specified position
    ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 64 ); 
}       
else if (percentage < 4) {
    ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 63 );
}
else if (percentage < 6) {
    ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, 62 );
}       
// etc...

O problema é que, como eu disse, esse tipo de cálculo não faz a fonte de luz parecer muito natural. A mudança de lado a lado parece muito nítida, enquanto, idealmente, eles entrariam e sairiam suavemente usando a folha de espetro (copiei a folha de outro jogo que consegue fazer isso, por isso sei que não há problema com as tonalidades dos ladrilhos.) como o outro jogo está fazendo isso). Eu estou pensando que talvez meu método de usar porcentagens para trocar de lado a lado possa ser substituído por um fórum de proximidade melhor / mais dinâmico de algum tipo que permita transições mais suaves? Alguém pode ter alguma idéia do que eu posso fazer para melhorar o visual aqui, ou uma maneira melhor de calcular a proximidade com as informações que estou coletando sobre cada bloco?

(PS: Estou reeditando isso do Stack Overflow por sugestão de alguém, desculpe pela duplicação!)

spectralbat
fonte
11
Você pode postar capturas de tela do que você tem e do que deseja?
Michaelhouse
Claro, vou postar uma captura de tela hoje à noite.
spectralbat
11
Você esqueceu de adicionar uma captura de tela ...
MichaelHouse

Respostas:

5

Antes de tudo, desculpe-me por não poder ajudá-lo com sua plataforma específica, pois nunca fiz esse tipo de trabalho em HTML5. Então, tentarei abordar isso de uma maneira independente da linguagem.

Eu não acho que você conseguirá transições suaves se estiver calculando a luz em uma base por peça . Isso é análogo às diferenças entre o uso de sombreamento simples, sombreamento gouraud ou sombreamento phong:

insira a descrição da imagem aqui

O que você está fazendo é semelhante ao sombreamento plano, ou seja, você está atribuindo uma luz diferente por bloco, o que resulta em transições nítidas entre os blocos, conforme demonstrado na figura acima.

Para obter melhores resultados, você terá que calcular suas luzes por vértice (ou seja, gouraud). Não sei quais são as capacidades do seu mecanismo, mas o caso ideal seria se cada um de seus blocos fosse um quad e você tivesse acesso direto aos seus vértices. Então você seria capaz de calcular a distância da luz para cada vértice e armazenar sua cor de tonalidade bem dentro do vértice. Finalmente, o pipeline de renderização cuidaria automaticamente da interpolação das sombras nas superfícies do ladrilho, resultando em uma aparência mais suave (também conhecida como sombreamento de gouraud ).

Para obter resultados ainda melhores, embora um pouco mais caros, você pode calcular a luz por pixel (isto é, phong) dentro do shader de fragmento. A abordagem básica funciona bem se você tiver apenas algumas luzes para provar, como parece ser o caso do seu exemplo.

Por fim, outra alternativa que pode funcionar muito bem é renderizar todas as suas luzes (e somente suas luzes) em um destino de renderização separado primeiro, opcionalmente embaçar o resultado e misturá-lo com o restante da cena no final. Verifique esta pergunta para algumas idéias sobre isso.

David Gouveia
fonte
A alternativa final parece ser uma boa opção para o HTML5 Canvas. Seus outros não estão expostos ao Canvas, a menos que o ImpactJS implemente especificamente shaders e vértices, o que duvido muito.
dman
@DMan Você está certo, se for baseado em tela, provavelmente não suporta essas alterações. Eu estava pensando que talvez fosse um mecanismo WebGL; nesse caso, seria simples.
David Gouveia
Muito obrigado pela sua resposta detalhada. Analisarei sua última sugestão esta noite, juntamente com outras idéias que estou tentando.
spectralbat
4

Você parece estar iluminando com uma atenuação linear (sua intensidade de luz está diminuindo linearmente). Você pode tentar usar a lei do quadrado inverso . Ou uma simples queda exponencial.

float maxLightDistance = 64;
float fallOff = 1.4f;
float intensity = Math.pow(distanceFromSource / maxLightDistance, fallOff);
int tileToUse = (int)intensity;
ig.game.backgroundMaps[2].setTile( this.pos.x, this.pos.y, tileToUse);

O código acima também ajuda você a se livrar da if else ifcoisa atroz que está acontecendo.

Uso apenas uma queda exponencial básica e obtenho resultados como este (posso postar uma imagem melhor posteriormente de uma única fonte de luz, só não tenho uma no momento):

luz da lava

Também devo mencionar que um programa como o Excel é ótimo para coisas como essa. Você pode testar as alterações de fórmula rapidamente e fazer um gráfico delas ou usar regras condicionais para alterar a cor de uma célula.

MichaelHouse
fonte
Gostaria de observar que alterar a fórmula de atenuação não fará com que a luz seja interpolada suavemente sobre a superfície dos ladrilhos. Cada ladrilho ainda terá uma única cor que muda repentinamente ao atingir o próximo ladrilho. Usar variações muito pequenas na luminosidade pode ajudar a ocultar o problema e torná-lo bom, mas ainda não é uma transição suave. Também argumento que a escolha da atenuação tem muito a ver com a estética, pois tive projetos em que preferia a aparência de uma atenuação linear (com um raio de efeito máximo e mínimo) em vez da lei do quadrado inverso.
David Gouveia
Minha sugestão é baseada no que o OP já possui. É para ser uma solução fácil de tentar, sem a necessidade de recriar o sistema de renderização. E isso pode fazer a diferença se o método OPs estiver colocando blocos com mais de uma diferença de tonalidade um ao lado do outro.
Michaelhouse
@spectralbat Mas eu definitivamente concordo em me livrar dessa if else ifcadeia. Crie um método que mapeie a porcentagem para indexar o bloco e substitua todas as ramificações por uma única chamada, setTilecomo no exemplo do Byte56.
David Gouveia
Claro, é bom que você apontou, especialmente considerando o quão simples é a mudança. Pode parecer melhor do que o OP atualmente. :) Estou apenas apontando que não acho que tenha algo a ver com o problema real de transições suaves entre blocos. Mas talvez seja exatamente o que o OP estava pedindo e eu fosse a pessoa que não entendia. ;-)
David Gouveia
Obrigado! Mesmo que isso não resolva meu problema original, tentarei de tudo para torná-lo melhor. Vou dar essa e algumas outras opções que estive procurando hoje à noite.
spectralbat