Terreno Voxel suave

13

Como projeto pessoal, estou tentando criar um gerador de terreno que crie um terreno parecido com o terreno liso do Castle Story.

Se você nunca viu isso antes, aqui: insira a descrição da imagem aqui

Então, como você pode ver, é uma combinação de blocos e blocos "suaves".

O que tentei fazer para imitar esse visual é fornecer a cada bloco de superfície um mini mapa de altura. Isso geralmente funciona, mas existem alguns problemas, resultando em um terreno como este:

insira a descrição da imagem aqui

O problema é que cada bloco é 1x1x1, mas às vezes a altura em um local específico é negativa ou> 1. Nesse caso, eu o recorte e defino a altura como 0 ou 1.

Para ilustrar melhor o que quero dizer, aqui está um diagrama: insira a descrição da imagem aqui

Para gerar a altura, eu basicamente faço:

genColumn(int x, int z)
{
    int highestBlockY = (int)noise2d(x, z);

    bool is_surface = true;

    for(int y = max_height - 1; y >= 0; y--)
    {
        Block b;

        if(is_surface)
        {
            b = Block.Grass;
            b.HasHeightMap = true;

            // generate heightmap
            for(int ix = 0; ix < 5; ix++)
            {
                for(int iz = 0; iz < 5; iz++)
                {
                    float heightHere = noise2d(x + ix / 4, z + iz / 4) - y;

                    // clip heights
                    if(heightHere > 1)
                        heightHere = 1;

                    if(heightHere < 0)
                        heightHere = 0;

                    b.HeightMap[ix][iz] = heightHere;
                }
            }

            is_surface = false;
        }
        else
        {
            b = Block.Dirt;
        }

        setBlock(x, y, z, b);
    }
}

Talvez eu esteja abordando isso incorretamente usando o valor de ruído perlin "verdadeiro"?

Qualquer ajuda seria muito apreciada!

sem título
fonte

Respostas:

11

Castle Story se parece com isso devido a restrições técnicas: havia um mapa de altura para cada voxel em todo o volume, em vez de apenas um mapa de altura para cada voxel de superfície , o custo de armazenamento seria muito maior, na ordem de O (n ^ 3 ) que pode ser proibitivo, em oposição a um O (n ^ 2) mais favorável, em que n é o comprimento lateral de um espaço cúbico de voxel que representa seu mundo. Lembre-se de que as informações do mapa de altura para voxels subterrâneos estão implícitas na estrutura da grade; portanto, as informações do mapa de altura precisam ser armazenadas explicitamente apenas para os voxels que estão na superfície. Então os caras do Castle Story usaram vértices nos interstícios da grade para economizar no custo de armazenamento e na complexidade da construção da malha ... continue lendo.

Primeiro, vamos analisar suas opções:

insira a descrição da imagem aqui

(1) vai dificultar as coisas para você, pois em uma única coluna voxel você deseja apenas informações de mini-heightmap para o voxel mais alto - consulte o primeiro parágrafo pelas razões. Olhando para (1), a coluna à direita contém informações do mapa de altura para o topo e o segundo de cima (e isso pode se aplicar ao terceiro de cima e assim por diante, se a inclinação for extrema o suficiente). Isso não é bom.

(2) Provavelmente é uma opção melhor então; isto é, garantir que os vértices dos cantos se ajustem aos interstícios da grade voxel. Mas como então lidamos com a questão da inclinação extrema? Bem, precisamos escolher um gradiente onde simplesmente encaixamos em uma coluna vertical e, assim, garantir que não tenhamos gradientes que possam atravessar n n voxels superiores. Um gradiente de 45 graus é o ponto de corte natural pelas razões que explico abaixo. Então, em vez de (3), teríamos (4):

insira a descrição da imagem aqui

(4) Ajustar o vértice do canto do voxel ao interstício da grade mais próximo é a solução. O efeito visual - alias diagonal - pode ser visto na sua captura de tela do Castle Story. A inclinação de corte para os voxels é um gradiente de 1: 1 ou 45 graus (como visto ortogonalmente). Trabalhando de trás para frente a partir da solução, vejamos os motivos:

  • A única maneira de ter uma inclinação extrema e ininterrupta é se um voxel for verticalmente alongado ...
  • .... Mas a malha do voxel não pode exceder sua área delimitadora, independentemente de sua forma suavizada; um voxel precisa sentar-se em um espaço definido em uma grade 3D que contabilize, se não for a forma exata, pelo menos a caixa delimitadora alinhada ao eixo.

Outro motivo para abordar dessa maneira é que, como você já descobriu, o não uso do snap (discretização) leva a uma variedade de cenários de suavização de superfície geometricamente complexos, que devem ser evitados por completo ... jogos desse tipo geralmente não exigem o grau de precisão que um algoritmo CSG adequado forneceria, razão pela qual estamos usando voxels em primeiro lugar: os voxels facilitam muito o trabalho incremental com volumes, do que os algoritmos de interseção de polígono de ponto flutuante (contínuo) / CSG .

Engenheiro
fonte
Eu apenas tentei implementar isso e me deparei com alguma confusão - o que você quer dizer com "interstícios"? Você quer dizer as coordenadas da grade integral?
sem título
@ThomasBradworth. "Um espaço que intervém entre as coisas." Para 2D, se você tiver uma grade ansn de células, terá (n + 1) x (n + 1) interstícios. Isso se estende diretamente ao 3D. Eles são os "vértices" que ligam cada célula, que são geralmente compartilhados entre várias células / voxels. Se você reduzi-lo a 1D (uma linha), então, se as células e os interstícios são representados como matriz, a célula 0 é delimitada pelo interstício 0 e interstício 1, enquanto a célula 1 é delimitada pelo interstício 1 e interstício 2 ... etc.
Engineer
Obrigado pela resposta! Tentei fazer exatamente isso, mas obtive alguns resultados desfavoráveis. No começo, tentei simplesmente arredondar os valores de altura nas arestas (quando x é 0 ou 4 ou z é 0 ou 4), mas isso me ocorreu : i.imgur.com/eQW7Y.png ( pastebin.com/Lr8vyyHB ) Em seguida, tentei suavizar os pontos no meio e adicionei uma "função de alturas de correção": pastebin.com/AazQ07Xm Isso, no entanto, também me deu um resultado mais angular, mas também ruim: i.imgur.com/q1JVP .png Talvez algo esteja errado com a minha função de ruído?
untitled
@ThomasBradsworth Infelizmente, sinto que você entendeu pela metade a resposta que levei mais de uma hora para escrever e editar. Esqueça o barulho; além disso, se você não entender a função de ruído, remova-a da equação por enquanto. Seu problema está em como os dados individuais da malha voxel são construídos, e o ruído não tem nada a ver com isso, porque você pode criar um mapa de altura na ausência de ruído. Trabalhe com o caso de teste mais simples, ou seja, codifique a matriz de altura. Em seguida, visualize seus resultados e ajuste seu algoritmo de geração de malha. Repita até que ele faça o que é esperado. Em seguida, volte a colocar o ruído e revise.
Engineer
Você está dizendo que eu deveria apenas armazenar os valores de altura dos cantos e não os valores de altura dos pontos "intermediários"?
untitled