Quais técnicas de renderização eu usaria para desenhar um efeito de sombra projetada para cartas em um jogo de cartas?

13

Que tipo de algoritmo de sombreamento pode ser usado para criar sombras como essas? insira a descrição da imagem aqui

o que eu estou fazendo é semelhante, mas tudo é feito com uma API de desenho 2D fornecida pelo OpenGL, portanto não há coordenada Z.

Além disso, para a mão em si, eu realmente gostaria de ter uma sensação sombreada como a vista aqui: insira a descrição da imagem aqui

Só não tenho certeza de como obter uma aparência sombreada perto disso.

O número de cartas deve mudar e as cartas são jogadas na mesa, então não posso usar nenhum tipo de mapa de luz.

Em que tipos de algoritmos devo analisar (além do borrão que sei que precisa fazer?)

obrigado

Atualizar

Estou fazendo um jogo de cartas em 2D. Quero adicionar dropshadows offset dos cartões, um pouco como:

insira a descrição da imagem aqui

O jeito que eu estou pensando em fazer isso é:

  • Mantenha uma textura que seja do mesmo tamanho do backbuffer.
  • Desenhe retângulos escuros como cartões improvisados ​​para essa textura.

  • Desfoque essa textura.

  • Desenhe minhas cartas para essa textura.
  • Faça iluminação adicional nos cartões.
  • Desenhe essa textura no backbuffer.

Minhas perguntas são:

  • Essa é a maneira correta de fazer isso?

  • Existe uma maneira de fazer isso sem renderizar para textura (mantendo um bitmap
    tão grande quanto o backbuffer)?

  • É seguro assumir que o tamanho máximo da textura não será
    excedido pelo tamanho do backbuffer? (O que quero dizer é que, se o backbuffer
    for 2000x3000, é seguro dizer que posso criar uma textura na
    memória de vídeo desse tamanho?

obrigado

jmasterx
fonte
1
Um ponto estético na sua segunda questão: isso não é realmente possível se você estiver segurando essas cartas na mão. Os cartões pressionados um ao lado do outro não se sombreiam sensivelmente. A menos que você esteja segurando muitos deles. Você realmente não pode segurar cartões da maneira que eles estão representados nessa imagem; esses cartões parecem separados por vários milímetros; eles não são planos um contra o outro. Eu simplesmente não me preocuparia com isso.
Nicol Bolas

Respostas:

18

Acho que todo mundo está dando soluções muito complicadas para esse problema ..

insira a descrição da imagem aqui

Então, primeiro, temos a carta (ou o que você quiser desenhar), representada aqui por (a). Em seguida, tiramos uma cópia, preenchemos em preto e executamos um borrão gaussiano (b). Tudo isso acontece no photoshop ou seja qual for a sua ferramenta de arte favorita.

Em seguida, no jogo, quando queremos desenhar o cartão, primeiro desenhe a imagem preta borrada com uma mistura multiplicativa (ou alfa), desloque um pouco em alguma direção e depois desenhe o cartão em cima disso. Voilá.

Para mais truques, você pode alternar entre sombra e renderização de cartas para obter efeito como (d), ou primeiro desenhar todas as sombras e depois as cartas, como em (e) (colori as cartas no caso (e) para mostrar que elas ainda está separado =)

Também é melhor não usar preto puro (ou alfa completo) na sombra para criar sombras mais sutis e transparentes.

Jari Komppa
fonte
2
+1 por fazer as sombras fora do jogo. Você também pode dar um passo adiante e modificar onde a sombra é colocada se houver uma luz na cena.
FrenchyNZ
+1, boa ilustração. Além disso, no caso E, você pode usar renderizar sombras escuras na mesa e semi-brilhante em cartões com estêncil. Isso será mais complicado, mas o efeito será ainda melhor :)
Kromster diz apoio Monica
2

O primeiro não é muito difícil. Se você renderizar um canal alfa para sua mão de cartas (que pode ser tão simples quanto preto para um pixel com um cartão e branco para transparente), você poderá executar qualquer tipo de desfoque que desejar no canal alfa e desloque-o e use-o para controlar a iluminação da mesa. (Presumivelmente, se você não tiver um buffer Z, primeiro terá que exibir o canal alfa fora da tela em algum lugar, depois fazer a mesa com a máscara e depois renderizar as cartas reais.)

O segundo se parece um pouco com o SSAO (oclusão ambiental no espaço da tela). Essa técnica requer uma coordenada Z. No entanto, se você não tiver os ângulos variados entre as cartas (o que eu acho que se você não tiver um buffer Z), provavelmente poderá fazer uma aproximação muito boa renderizando uma sombra projetada (como a primeira) para cada cartão. Porém, o desempenho pode ser um pouco complicado - todo avião precisaria de um canal alfa desfocado para todos os planos acima dele, e se houver um monte de cartas na mesa, isso pode significar algumas passagens de renderização.

John Calsbeek
fonte
O SSAO é um exagero =) É muito difícil dizer qual técnica de sombreamento é usada sem ver o exemplo animado. Se as cartas sempre caírem nessa ordem e espaçadas umas sobre as outras, seria mais fácil pré-renderizar as sombras, mas você nunca saberia sem ver o jogo em ação.
Patrick Hughes
0

Você não poderia fazer um mapa de sombras? Renderize a cena para um fbo. Salve apenas os valores de profundidade e verifique o valor normal de profundidade no sombreador para ver se uma sombra deve ser renderizada ou não.

Joey Green
fonte
Não estou usando um buffer Z, não há 'profundidade'.
jmasterx
0

O processo a seguir não requer um destino de renderização fora da tela. No entanto, ao fazê-lo, ganha a exigência de que a tabela seja desenhada primeiro . Ou pelo menos, nada é desenhado nos pixels que a tabela cobrirá antes de ser desenhada. Também exige que a própria mesa seja uma peça única e não sobreposta: uma imagem.

Além disso, seu framebuffer precisa de um componente alfa.

  1. Obtenha uma imagem em escala de cinza de um cartão. Torne-o impreciso nas bordas, possivelmente expandindo seu tamanho. Esta é a sua imagem do cartão de sombra. Observe que esta é uma imagem de canal único. Ao acessar essa textura em seu sombreador, certifique-se de colocar o valor único no componente alfa. Isso pode ser feito no seu sombreador ou em outro lugar.

    Um valor de 0,0 na imagem significa que não há sombra. Um valor de 1,0 na imagem significa sombra total . Ou seja, completamente escuro como breu. Você provavelmente deseja um valor de 0,5 ou mais como a sua cor mais escura.

  2. Limpe a tela para que o alfa esteja definido como zero .

  3. Antes de renderizar qualquer coisa (ou pelo menos qualquer coisa que será renderizada "embaixo" da tabela), para cada cartão, renderize a textura difusa, deslocada da posição real do cartão (mas com a mesma orientação). A saída de cor pelo sombreador deve ser A configuração da mistura para isso deve ser (na linguagem OpenGL):

    glBlendEquation(GL_MAX);

    Esse modo de mesclagem é usado para impedir que as sombras sobrepostas se tornem cada vez mais escuras ou mais claras. Simplesmente leva o valor mais escuro gravado para esse pixel. Deve parecer bom o suficiente.

    Você também deve usar a máscara de gravação em cores para desativar as gravações em cores.

  4. Renderize a tabela. Ao fazer isso A mistura deve ser configurada da seguinte maneira:

    glBlendEquation(GL_ADD);
    glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ZERO);

Se as restrições forem muito restritivas para você, será necessário usar um destino de renderização. Isto se faz do seguinte modo:

  1. Crie a imagem do cartão de sombra como antes.

  2. Primeiro, renderize as sombras. Ligue o buffer de estrutura de sombra (que só precisa ser uma imagem de canal único, se o seu hardware puder renderizar um deles). Limpe seu valor para zero.

  3. Para cada cartão, renderize a imagem do cartão de sombra, deslocada como antes. Seu sombreador deve gravar o mesmo valor nos quatro componentes da cor de saída. O modo de mesclagem novamente deve ser glBlendEquation(GL_MAX).

  4. Volte para o buffer de quadros comum. Desenhe tudo o que você deseja ser sombreado.

  5. Agora desenhe um quad em tela cheia com a imagem de sombra que renderizamos. O sombreador deve armazenar o texel obtido da imagem de sombra no alfa da saída; o RGB é irrelevante. O modo de mesclagem deve ser:

    glBlendEquation(GL_ADD);
    glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
Nicol Bolas
fonte
No primeiro método, acho que você esqueceu a etapa 5: desabilite a mistura e processe os cartões. Além disso, vale ressaltar que o primeiro método não permite obter sombras entre cartões, como visto na segunda captura de tela do OP.
Nathan Reed
@ NathanReed: achei que ele sabia como desligar as coisas. Além disso, as "sombras entre cartas" não fazem nenhum sentido, como apontei no meu comentário na pergunta.
Nicol Bolas
1
Pode não representar realisticamente como você seguraria as cartas, mas parece legal! Realmente não "fazem sentido" para os cartões de flutuar sobre a mesa como eles fazem no primeiro tiro, ou ...
Nathan Reed
0

Eu usaria o buffer de estêncil. Limpe-o para 1 (de preferência ao mesmo tempo em que limpa a profundidade), desenhe sua mesa. Em seguida, ative o teste de estêncil, desenhe as sombras usando uma textura de retângulo embaçado, aumentando se o estêncil e a profundidade passarem, não fazendo nada se o estêncil falhar, nada se a profundidade falhar e defina sua função de estêncil para GL_EQUAL (ref 1, mascara 0xff), desative o teste de estêncil . (Eu não testei completamente isso, mas é derivado de um código semelhante e deve funcionar bem; talvez seja necessário ajustar os parâmetros). Então desenhe todo o resto.

As chamadas seriam:

glEnable (GL_STENCIL_TEST);
glStencilFunc (GL_EQUAL, 1, 2);
glStencilOp (GL_KEEP, GL_KEEP, GL_INCR);

// draw shadow textures

glDisable (GL_STENCIL_TEST);

A única vez que isso pode causar problemas é se você tiver duas bordas desfocadas que se sobrepõem ligeiramente; caso contrário, ele não precisa de nada sofisticado além do que o OpenGL 1.1 básico oferece e funcionará em todo o hardware (talvez placas 3DFX antigas, que eu assumo que você não esteja realmente preocupado em oferecer suporte ...)

Uma segunda alternativa, que deve funcionar muito bem em um jogo não crítico para o desempenho, é o glCopyTexSubImage2D. Isso também funcionará com uma textura menor que o backbuffer e também é suportado em todo o hardware (faz parte do OpenGL 1.1 e do Doom 3 a utilizou, portanto, você pode esperar que o suporte seja muito bom).

A configuração básica seria assim:

  • Cor clara para preto.
  • Desative a gravação em profundidade (embora esse método não precise de um buffer de profundidade, você poderá ignorar essa parte porque não está usando um).
  • Defina uma viewport com as mesmas dimensões que sua textura.
  • Desenhe cartões como geometria preenchida em branco.
  • glCopyTexSubImage2D à sua textura.
  • Alterne a janela de visualização para o tamanho total da janela.
  • Desenhe a tabela normalmente.
  • Desenhe a textura como um quad misturado em tela cheia, usando um shader para desfocá-la e inverter a cor.
  • Desenhe todo o resto.

Isso também deve funcionar muito bem, é um pouco mais intensivo em GPU do que o método de buffer de estêncil, mas lida com a caixa sobreposta corretamente e - como eu disse - acho que um jogo como o seu terá potência de GPU para queimar, mesmo em um nível mais baixo cartão.

Maximus Minimus
fonte