Nos últimos meses, o Facebook inundou
fotos em 3D . Se você não conseguiu vê-las, explicarei: fotos 3D são imagens dentro da postagem que alteram suavemente o ângulo ao rolar a página ou quando você passa o mouse sobre elas.
Alguns meses antes desse recurso aparecer, o Facebook testou um recurso semelhante nos modelos 3D. Embora você possa entender facilmente como o Facebook pode renderizar modelos 3D e girá-los de acordo com a posição do mouse, nas fotos em 3D a situação pode não ser tão intuitiva.
A técnica usada pelo Facebook para criar tridimensionalidade de imagens bidimensionais às vezes é chamada de
deslocamento do mapa de elevação . Ele usa um fenômeno óptico chamado
paralaxe .
Exemplo de uma foto 3D do Facebook (GIF) O que é paralaxe
Se você jogou Super Mario, sabe exatamente o que é paralaxe. Embora Mario corra na mesma velocidade, parece que objetos distantes ao fundo se movem mais devagar (veja abaixo).
Esse efeito cria a ilusão de que alguns elementos, como montanhas e nuvens, estão localizados mais longe. É eficaz porque nosso cérebro usa paralaxe (junto com outras pistas visuais) para estimar a distância a objetos distantes.
Como o cérebro avalia a distância?Supõe-se que o cérebro humano use vários mecanismos para estimar a distância. Em faixas curtas e médias, as distâncias são calculadas comparando-se as diferenças na posição do objeto visível com o olho direito e esquerdo. Isso é chamado de
visão estereoscópica e é generalizada na natureza.
No entanto, para objetos suficientemente distantes, uma visão estereoscópica não é suficiente. Montanhas, nuvens e estrelas diferem muito pouco para olhos diferentes perceberem uma diferença significativa. Portanto, a paralaxe relativa entra em jogo. Os objetos em segundo plano movem-se menos que os objetos em primeiro plano. É o movimento relativo deles que permite definir a distância relativa.
Na percepção da distância, muitos outros mecanismos são usados. O mais famoso deles é a neblina atmosférica, que dá aos objetos distantes uma tonalidade azul. Em outros mundos, a maioria dessas pistas atmosféricas não existe, por isso é tão difícil avaliar a escala de objetos em outros planetas e na lua. O usuário do YouTube Alex McCulgan explica isso em seu canal
Astrum , mostrando o quão difícil é determinar o tamanho dos objetos lunares mostrados no vídeo.
Paralaxe como uma mudança
Se você conhece a álgebra linear, provavelmente sabe como a matemática das rotações 3D pode ser complicada e não trivial. Portanto, existe uma maneira muito mais simples de entender a paralaxe, que requer apenas mudanças.
Vamos imaginar que estamos olhando para um cubo (veja abaixo). Se estivermos precisamente alinhados com o centro, as faces frontal e traseira parecerão dois quadrados de tamanhos diferentes para nossos olhos. Essa é a
perspectiva .
No entanto, o que acontece se abaixarmos a câmera ou elevarmos o cubo? Aplicando os mesmos princípios, podemos ver que as faces frontal e traseira mudaram em relação à sua posição anterior. Ainda mais interessante é que eles se mudaram em relação um ao outro. A face traseira, que está mais longe de nós, como se se movesse menos.
Se quisermos calcular as posições verdadeiras desses vértices do cubo em nosso escopo projetado, teremos que levar a sério a trigonometria. No entanto, isso não é realmente necessário. Se o movimento da câmera for pequeno o suficiente, podemos aproximar o deslocamento dos vértices, movendo-os proporcionalmente à sua distância.
A única coisa que precisamos determinar é escala. Se movermos X metros para a direita, deve parecer que o objeto Y metros de distância se moveu Z metros. Se X permanecer pequeno, a paralaxe se tornará a tarefa de
interpolação linear em vez de trigonometria. Em essência, isso significa que podemos simular pequenas rotações 3D deslocando os pixels, dependendo da distância da câmera.
Gere mapas de profundidade
Em princípio, o que o Facebook faz não é muito diferente do que está acontecendo no Super Mario. Para uma determinada imagem, certos pixels são deslocados na direção do movimento com base na distância da câmera. Para criar uma foto 3D do Facebook, você só precisa da foto e de um mapa informando a distância de cada pixel da câmera. Esse mapa tem o nome esperado -
"mapa de profundidade" . Também é chamado de
mapa de altura, dependendo do contexto.
Tirar uma foto é bem simples, mas gerar o mapa de profundidade certo é uma tarefa muito mais difícil. Os dispositivos modernos usam várias técnicas. Na maioria das vezes, use duas câmeras; cada um tira uma foto do mesmo assunto, mas com uma perspectiva ligeiramente diferente. O mesmo princípio é usado na
visão estereoscópica , usada pelas pessoas para avaliar a profundidade em distâncias curtas e médias. A imagem abaixo mostra como o iPhone 7 pode criar mapas de profundidade a partir de duas imagens muito próximas.
Os detalhes da implementação dessa reconstrução estão descritos no artigo
Instant 3D Photography , apresentado por
Peter Hedman e
Johannes Kopf no SIGGRAPH2018.
Após criar um mapa de profundidade de alta qualidade, simular a tridimensionalidade se torna uma tarefa quase trivial. A verdadeira limitação dessa técnica é que, mesmo que você possa recriar um modelo 3D bruto, ela não possui informações sobre como renderizar peças invisíveis na foto original. No momento, esse problema não pode ser resolvido e, portanto, todos os movimentos visíveis nas fotografias em 3D são bastante insignificantes.
Nós nos familiarizamos com o conceito de fotografias em 3D e falamos brevemente sobre como os smartphones modernos podem criá-las. Na segunda parte, aprenderemos como as mesmas técnicas podem ser usadas para implementar fotos 3D no Unity usando shaders.
Parte 2. Shaders de paralaxe e mapas de profundidade
Modelo de sombreador
Se queremos recriar fotos em 3D do Facebook usando um shader, devemos primeiro decidir o que exatamente faremos. Como esse efeito funciona melhor com imagens 2D, seria lógico implementar uma solução compatível com os sprites do Unity. Criaremos um shader que pode ser usado com o
Sprite Renderer .
Embora esse sombreador possa ser criado do zero, geralmente é preferível começar com um modelo pronto. É melhor começar a avançar copiando o shader difuso existente de sprites, que o Unity usa por padrão para todos os sprites. Infelizmente, o mecanismo não vem com um arquivo
shader que você pode editar por conta própria.
Para obtê-lo, você precisa ir ao
arquivo de download do
Unity e baixar o pacote
Built-in shaders (veja abaixo) para a versão do mecanismo que você está usando.
Após extrair o pacote, você pode visualizar o código-fonte de todos os shaders que acompanham o Unity. Estamos interessados no arquivo
Sprites-Diffuse.shader , que é usado por padrão para todos os sprites criados.
Imagens
O segundo aspecto que precisa ser formalizado são os dados que temos. Imagine que temos a imagem que queremos animar e seu mapa de profundidade. A última será uma imagem em preto e branco, na qual os pixels em preto e branco indicam a que distância ou distância estão da câmera.
As imagens usadas neste tutorial foram tiradas do
projeto de gato Pickle de
Dennis Hotson , e este é sem dúvida o melhor que você verá hoje.
O mapa de altitude associado a esta imagem reflete a distância do focinho do gato da câmera.
É fácil ver como bons resultados podem ser alcançados com um mapa de profundidade tão simples. Isso significa que é fácil criar seus próprios mapas de profundidade para imagens existentes.
As propriedades
Agora que temos todos os recursos, podemos começar a escrever o código do sombreador de paralaxe. Se importarmos a imagem principal como um sprite, o Unity a passará automaticamente para o shader por meio da propriedade
_MainTex
. No entanto, precisamos disponibilizar o mapa de profundidade para o shader. Isso pode ser implementado usando uma nova
propriedade shader chamada
_HeightTex
. Decidi intencionalmente não chamá-lo
_DepthTex
para não confundi-lo com a
textura de profundidade (este é um conceito similar do Unity usado para renderizar o mapa de profundidade da cena).
Para alterar a força do efeito, também adicionaremos a propriedade
_Scale
.
Properties { ... _HeightTex ("Heightmap (R)", 2D) = "gray" {} _Scale ("Scale", Vector) = (0,0,0,0) }
Essas duas novas propriedades também devem corresponder a duas variáveis com o mesmo nome que precisam ser adicionadas à seção
CGPROGRAM
/
ENDCG
:
sampler2D _HeightTex; fixed2 _Scale;
Agora está tudo pronto, e podemos começar a escrever o código que executará o deslocamento.
O primeiro passo é amostrar o valor do mapa de profundidade, o que pode ser feito usando a função
tex2D
. Como
_HeightTex
é uma textura em preto e branco, podemos apenas pegar seu canal vermelho e descartar o restante. O valor resultante mede a distância em algumas unidades arbitrárias do pixel atual até a câmera.
O valor da profundidade está entre
antes
mas vamos esticá-lo para o intervalo de
antes
. Isso permite que você forneça paralaxe positivo (cor branca) e negativo (cor preta).
Teoria
Para simular o efeito de paralaxe nesta fase, precisamos usar as informações de profundidade para mudar os pixels da imagem. Quanto mais próximo o pixel, mais forte ele precisa ser mudado. Este processo é explicado no diagrama abaixo. O pixel vermelho da
imagem original, de acordo com as informações do mapa de profundidade, deve deslocar dois pixels para a esquerda. Da mesma forma, o pixel azul deve deslocar dois pixels para a direita.
Embora
teoricamente isso deva funcionar, não há maneiras fáceis de implementar isso no shader. O fato é que um sombreador, por seu princípio, só pode mudar a cor do pixel
atual . Ao executar o código de sombreador, ele deve desenhar um pixel específico na tela; não podemos simplesmente mover esse pixel para outro local ou alterar a cor do vizinho. Essa
restrição de localidade fornece uma operação paralela muito eficiente de shaders, mas não nos permite implementar todos os tipos de efeitos que seriam triviais, desde que haja
acesso aleatório para gravação de cada pixel na imagem.
Se quisermos ser precisos, precisamos amostrar o mapa de profundidade de todos os pixels vizinhos para descobrir qual deles deve (se deve) mover para a posição atual. Se vários pixels estiverem no mesmo local, podemos calcular a influência deles. Embora esse sistema funcione e forneça o melhor resultado possível, é extremamente ineficiente e potencialmente centenas de vezes mais lento que o shader difuso original com o qual começamos.
A melhor alternativa seria a seguinte solução: obtemos a profundidade do pixel atual no mapa de profundidade; se precisarmos deslocá-lo
para a direita , substitua a cor atual pelo pixel
à esquerda (veja a imagem abaixo). Aqui assumimos que, se você deseja mover o pixel para a direita, os pixels vizinhos à esquerda também devem se mover da mesma maneira.
É fácil ver que essa é apenas uma aproximação de baixo custo do que realmente queríamos alcançar. No entanto, é muito eficaz, porque os mapas de profundidade geralmente são suaves.
Código
Seguindo o algoritmo descrito na seção anterior, podemos implementar o sombreador de paralaxe com um
deslocamento simples
das coordenadas UV .
Isso leva ao seguinte código:
void surf (Input IN, inout SurfaceOutput o) {
Essa técnica funciona bem com objetos quase planos, como pode ser visto na animação abaixo.
Mas ele realmente funciona muito bem com modelos 3D, porque é muito fácil renderizar a textura de profundidade para uma cena 3D. Abaixo está uma imagem renderizada em 3D e seu mapa de profundidade.
Os resultados finalizados são mostrados aqui: