Estou trabalhando em um jogo tipo Megaman , onde preciso alterar a cor de certos pixels em tempo de execução. Para referência : no Megaman, quando você muda sua arma selecionada, a paleta do personagem principal muda para refletir a arma selecionada. Nem todas as cores do sprite mudam, apenas algumas mudam .
Esse tipo de efeito era comum e bastante fácil de executar no NES, pois o programador tinha acesso à paleta e ao mapeamento lógico entre pixels e índices da paleta. No hardware moderno, porém, isso é um pouco mais desafiador, porque o conceito de paletas não é o mesmo.
Todas as minhas texturas são de 32 bits e não usam paletas.
Existem duas maneiras de obter o efeito desejado, mas estou curioso para saber se existem maneiras melhores de obter esse efeito facilmente. As duas opções que conheço são:
- Use um sombreador e escreva um GLSL para executar o comportamento de "troca de paleta".
- Se shaders não estiverem disponíveis (por exemplo, porque a placa gráfica não os suporta), é possível clonar as texturas "originais" e gerar versões diferentes com as alterações de cores pré-aplicadas.
Idealmente, eu gostaria de usar um sombreador, pois parece simples e requer pouco trabalho adicional em oposição ao método de textura duplicada. Eu me preocupo que duplicar texturas apenas para mudar uma cor esteja desperdiçando VRAM - não devo me preocupar com isso?
Edit : Acabei usando a técnica da resposta aceita e aqui está o meu shader para referência.
uniform sampler2D texture;
uniform sampler2D colorTable;
uniform float paletteIndex;
void main()
{
vec2 pos = gl_TexCoord[0].xy;
vec4 color = texture2D(texture, pos);
vec2 index = vec2(color.r + paletteIndex, 0);
vec4 indexedColor = texture2D(colorTable, index);
gl_FragColor = indexedColor;
}
Ambas as texturas são de 32 bits, uma textura é usada como tabela de pesquisa, contendo várias paletas do mesmo tamanho (no meu caso, 6 cores). Eu uso o canal vermelho do pixel de origem como um índice para a tabela de cores. Isso funcionou como um encanto para alcançar a troca de paletas tipo Megaman!
Existem duas maneiras de pensar em fazer isso.
Maneira # 1: GL_MODULATE
Você pode fazer o sprite em tons de cinza e
GL_MODULATE
a textura quando desenhada com uma única cor sólida.Por exemplo,
Isso não permite muita flexibilidade, no entanto, basicamente você pode ficar mais claro e mais escuro da mesma tonalidade.
Caminho # 2:
if
declaração (ruim para o desempenho)Outra coisa que você pode fazer é colorir suas texturas com alguns valores-chave envolvendo R, G e B. Portanto, por exemplo, a primeira cor é (1,0,0), a segunda cor é (0,1,0), a terceira a cor é (0,0,1).
Você declara alguns uniformes como
Em seguida, no sombreador, a cor final do pixel é algo como
color1
,color2
,color3
, São uniformes para que eles possam ser definida antes que os sombreador é executado (dependendo do que megaman "terno" está em), então o shader simplesmente faz o resto.Você também pode usar
if
instruções se precisar de mais cores, mas teria que fazer com que as verificações de igualdade funcionassem corretamente (as texturas geralmente estãoR8G8B8
entre 0 e 255 cada no arquivo de textura, mas no shader, você tem rgba floats)Caminho 3: Armazene de forma redundante as diferentes imagens coloridas no mapa do sprite
Esta pode ser a maneira mais fácil, se você não estiver tentando economizar insanamente a memória
fonte
Eu usaria shaders. Se você não quiser usá-los (ou não puder), eu usaria uma textura (ou parte de uma textura) por cor e usaria apenas branco limpo. Isso permitirá que você tinja a textura (por exemplo, através
glColor()
) sem precisar se preocupar em criar texturas adicionais para cores. Você pode até trocar cores em execução sem precisar trabalhar com textura adicional.fonte
"Nem todas as cores do sprite mudam, apenas algumas mudam."
Jogos antigos faziam truques de paleta, porque era tudo o que tinham. Hoje em dia, você pode usar várias texturas e combiná-las de várias maneiras.
Você pode criar uma textura de máscara em escala de cinza separada usada para decidir quais pixels mudarão de cor. As áreas brancas da textura recebem a modificação completa das cores e as áreas pretas permanecem inalteradas.
Na linguagem shader:
Ou você pode usar multi-texturização de função fixa com vários modos combinadores de textura.
Obviamente, existem muitas maneiras diferentes de você fazer isso; você pode colocar máscaras para cores diferentes em cada um dos canais RGB ou modular as cores mudando de tonalidade em um espaço de cores HSV.
Outra opção, talvez mais simples, é simplesmente desenhar o sprite usando uma textura separada para cada componente. Uma textura para os cabelos, uma para a armadura, uma para as botas, etc. Cada uma pode ser tingida separadamente.
fonte
Em primeiro lugar, geralmente é chamado de ciclismo (ciclismo de paletes), para que possa ajudar seu Google-fu.
Isso dependerá exatamente de quais efeitos você deseja produzir e se estiver lidando com conteúdo 2D estático ou material 3D dinâmico (ou seja, você só deseja lidar com rachaduras / texturas, ou renderizar uma cena 3D inteira, troque de palete). Além disso, se você estiver se limitando a várias cores ou usando cores completas.
Uma abordagem mais completa, usando shaders, seria realmente produzir imagens indexadas em cores com uma textura de palete. Faça uma textura, mas em vez de ter cores, tenha uma cor que represente um índice a ser pesquisado e, em seguida, tenha outra textura de cores que é a placa. Por exemplo, vermelho pode ser a textura t coordenada e verde pode ser a coordenada s. Use um shader para fazer a pesquisa. E Animar / Modificar as cores dessa textura de palete. Isso seria bem próximo do efeito retrô, mas funcionaria apenas para conteúdo 2D estático, como sprites ou texturas. Se você estiver criando gráficos retrô genuínos, poderá produzir sprites de cores indexados reais e escrever algo para importá-los e os paletes.
Você também vai querer descobrir se vai usar um palete 'global'. Assim como o hardware antigo funcionaria, menos memória para paletes, pois você só precisa de um, mas é mais difícil para produzir sua arte, pois precisa sincronizar o palete em todas as imagens) ou dar a cada imagem seu próprio palete. Isso daria mais flexibilidade, mas consumiria mais memória. É claro que você pode fazer as duas coisas e também usar paletes apenas para as coisas que você é ciclista em cores. Também calcule até que ponto você não deve limitar suas cores; os jogos antigos usariam 256 cores, por exemplo. Se você estiver usando uma textura, obterá o número de pixels, de modo que 256 * 256 lhe dará
Se você deseja apenas um efeito falso barato, como água corrente, você pode criar uma segunda textura que contenha uma máscara em escala de cinza da área que deseja modificar, depois use as texturas mastradas para modificar a tonalidade / saturação / brilho pela quantidade na textura de máscara em escala de cinza. Você pode ser ainda mais preguiçoso e apenas alterar a tonalidade / brilho / saturação de qualquer coisa em uma faixa de cores.
Se você precisasse dele em 3D completo, poderia tentar gerar a imagem indexada em tempo real, mas seria lento, pois você teria que procurar no palete, provavelmente também estaria limitado na variedade de cores.
Caso contrário, você pode simplesmente falsificá-lo no sombreador, se color == swapping color, troque-o. Isso seria lento e funcionaria apenas com um número limitado de cores de troca, mas não deve estressar nenhum hardware atual, especialmente se você apenas lida com gráficos 2D e sempre pode marcar quais sprites têm troca de cores e usam um sombreador diferente.
Caso contrário, realmente falsifique-os, faça um monte de texturas animadas para cada estado.
Aqui está uma ótima demonstração do ciclo de paletes feito em HTML5 .
fonte
Acredito que os ifs sejam rápidos o suficiente para que o espaço de cores [0 ... 1] seja dividido em dois:
Dessa maneira, valores de cores entre 0 e 0,5 são reservados para valores de cores normais; e 0,5 - 1,0 são reservados para valores "modulados", em que 0,5..1,0 é mapeado para qualquer intervalo selecionado pelo usuário.
Somente a resolução de cores é truncada de 256 valores por pixel para 128 valores.
Provavelmente, pode-se usar funções internas como max (color-0.5f, 0.0f) para remover os ifs completamente (trocando-os por multiplicações por zero ou um ...).
fonte