No Minecraft, quando você olha para a água, quanto mais profunda você vê, mais escura fica. Alguém sabe como codificar algo assim?
Minecraft com efeito
jogo semelhante sem efeito
No Minecraft, quando você olha para a água, quanto mais profunda você vê, mais escura fica. Alguém sabe como codificar algo assim?
Minecraft com efeito
jogo semelhante sem efeito
Respostas:
Existem essencialmente duas abordagens diferentes para iluminar a água com base na profundidade:
O Minecraft usa iluminação baseada em voxel, que funciona propagando a luz para cubos adjacentes, diminuindo o brilho dependendo do tipo de bloco. Oceanos escuros são um efeito colateral desse sistema.
A água bloqueia a luz solar e reduz a luz em 3 níveis por bloco (em vez do nível 1 padrão), o que significa que o brilho no oceano para cada distância da superfície é:
0 (surface): 15 (direct sunlight)
1: 12
2: 9
3: 6
4: 3
5 and below: 0 (darkness)
Fonte: Minecraft Wiki - Light
Em jogos com um modelo de iluminação tradicional, esse efeito pode ser criado medindo a quantidade de água que fica entre a fonte de luz e o fundo do oceano. A luz é então apagada com base nessa distância. Existem alguns métodos para fazer isso:
Se você tiver uma superfície plana, poderá calcular facilmente a distância que a luz percorre na água se afastar a superfície normal da massa de água e do produto escalar dessa normal e uma posição da superfície no sombreador de geometria.
A distância efetiva da água é
onde é a posição do vértice e é o ângulo entre a direção da luz abaixo da superfície e a superfície da água normal em relação ao corpo de água.
Ao pôr do sol, atinge apenas um pouco menos de 50 ° porque a luz é refratada ao entrar na água.
Aqui está uma postagem no blog com uma boa explicação: A câmera digital: reflexão interna total
Outro post com mais detalhes: A câmera digital: lei da refração de Snell
Se você estiver usando um mapa de altura em uma superfície paralela à água, torna-se . O fator certo é igual a 1 se o sol estiver diretamente acima da superfície da água.
Com uma luz pontual, é necessário calcular para cada vértice com base na posição relativa da fonte de luz.
Com um nível de água fixo ou uma direção de luz fixa, partes da equação são constantes e não devem ser calculadas no sombreador por razões de desempenho.
Se você renderizar a superfície da água em um mapa de profundidade separado (como visto da fonte de luz), poderá usar essa textura de profundidade para calcular a distância que a luz percorre na água antes de atingir a superfície.
Para fazer isso, você projeta cada vértice na projeção de exibição da fonte de luz no shader de vértice e faz a pesquisa de textura no shader de pixel.
Se a superfície for relativamente plana, use uma origem de luz refratada para obter melhores resultados.
* Você pode determinar a quantidade de água na frente da superfície sólida mais próxima contando a profundidade do ponto de vista da luz da seguinte maneira:
A textura do resultado agora contém a quantidade de água na frente da luz no espaço de visualização da luz, portanto, o valor deve ser transformado novamente antes de você usá-lo. Este método funciona para calcular a luz direcional (menos refração), mas levará a luz ambiente incorreta se as superfícies forem muito irregulares e houver uma grande quantidade de ar entre os corpos de água afetando os mesmos fragmentos.
Os prós e os contras são os mesmos do mapeamento de sombra normal, exceto que você precisa de mais um buffer ao calcular a profundidade e o desempenho é pior porque é necessário desenhar mais geometria.
O traçado de raios é de longe a solução mais precisa, mas também a mais cara, para renderizar volumes transparentes. Há duas maneiras de fazer isso: 1. Rastreamento do fundo do oceano em direção à superfície e 2. Rastreamento da fonte de luz em direção à água. São necessários vários raios para cada ponto no chão para calcular o brilho.
Há mais algumas coisas a serem levadas em consideração ao renderizar água:
A luz na água é espalhada novamente enquanto viaja para o observador, portanto você deve misturá-la para obter uma cor sólida.
Se o observador estiver submerso , você poderá renderizar a névoa com base no resultado final do buffer de profundidade. A cor do nevoeiro, mas não sua densidade, deve mudar com a distância do observador da superfície! (O Minecraft usa apenas essa parte do efeito.)
Se o observador olhar para a água de cima , é necessário calcular a neblina com base na diferença de profundidade entre a superfície e a geometria sob a água. A cor do nevoeiro deve ficar um pouco mais escura com diferenças de profundidade maiores, mas deve mudar apenas para o ponto em que o nevoeiro é totalmente opaco.
A cor do nevoeiro também deve depender da direção da visualização de cada pixel, por isso é um pouco mais escura ao olhar para baixo nos dois casos.
Se você usar uma Textura 3D lado a lado em vez de um decalque para cáusticos falsos, poderá evitar esticar em superfícies verticais. A força da luz dispersa perto da superfície varia em três dimensões; portanto, o uso de uma textura 2D geralmente produz alongamentos em algum lugar da cena. Você pode modelar os ângulos de mudança de luz projetando as posições dos vértices do piso em um sistema de coordenadas diferente.
Outra possibilidade é calcular a densidade da luz com base na posição da superfície no sistema de coordenadas da luz, embora isso provavelmente custe algum desempenho.
Os cáusticos devem desaparecer mais rapidamente que a luz difusa com o aumento da profundidade.
As cores são espalhadas de maneira diferente, portanto a cor da luz deve mudar com o aumento da profundidade. Isso também evita arestas abruptas onde, por exemplo, uma praia cruza a superfície da água.
Por causa da refração, a luz atinge o fundo do oceano muito mais íngreme do que normalmente. O artigo da Wikipedia sobre a lei de Snell possui fórmulas para ângulos e vetores.
Acredito que o efeito de iluminação do céu no Minecraft seja direto - as coisas ficam sombreadas pelo que está acima deles, não importa onde esteja o sol. Em seguida, a iluminação local de tochas etc. é aplicada com um efeito de queda - quanto mais longe a fonte de luz, menos luz um cubo recebe.
Se feito dessa maneira, cada camada de água sombrearia cumulativamente a camada abaixo dela, tornando-se progressivamente mais escura. A folhagem das árvores fornece sombra assim, porém não é cumulativa. Você fica com a mesma sombra debaixo de uma árvore, seja 1 ou 100 cubos de folhagem.
Uma pista de que este é o método usado é que a água não fica mais escura quando está mais longe do espectador - apenas quando você desce. Sim, o efeito de neblina entra à distância, mas não o efeito escuro da água.
Portanto, a fórmula básica para calcular a iluminação seria algo parecido com isto no pseudo-código ...
light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
if cube being examined is tree foliage
light_on_cube = 0.5
else if cube being examined is water
light_on_cube = light_on_cube - 0.1
else if cube being examined is solid
light_on_cube = 0
}
Isso não é perfeito para calcular a iluminação sob saliências ou cavernas, pois seria muito escuro sob uma saliência usando esse método. Mas pode-se adicionar fontes de luz locais (tochas, incêndios etc.), além de tratar os blocos iluminados pelo sol como fontes de luz. Algo assim pode fazer isso ...
A idéia aqui é que, se um cubo for aceso pelo sol ou uma tocha, o cubo ao lado dele também será aceso de alguma maneira. E quanto mais longe você estiver daquele cubo aceso, menos luz haverá. É uma maneira de estimar a iluminação difusa, mas acho que (?) Funcionaria.
Talvez eu esteja entendendo mal a pergunta, mas por que você não pode simplesmente mudar a cor dos blocos, dependendo da profundidade deles?
Se você tiver a profundidade d (em blocos, começando em 0), uma equação razoável para o brilho seria:
L = (1- m ) e - kd + m
Código: L = (1.0 - m) * exp(-k * d) + m;
k controla o quão rápido fica mais escuro (mais alto = mais rápido). Um valor razoável seria 0,5.
m é o brilho mínimo que você deseja.
L varia de 0 a 1.
Se você não souber como alterar a cor dos blocos em qualquer API gráfica que estiver usando, faça isso como uma pergunta separada (informando qual API você usa e se está usando shaders).
e^-kd
bit é apenas uma deterioração exponencial, que é uma função padrão para coisas que tendem gradualmente a zero em algum valor (profundidade). A multiplicação (1-m)
e a adição de m
são apenas para escalar e compensar o decaimento, para que ele termine no mínimo, m
mas ainda comece às 1
. en.wikipedia.org/wiki/Exponential_decay