Bordas ruidosas, suavizando as bordas entre as faces por meio de sombreadores de fragmentos

8

Eu tenho um terreno gerado, com geometria hexagonal, conforme a captura de tela abaixo:

insira a descrição da imagem aqui

Eu então gero biomas, mas como você pode ver, as fronteiras entre eles são realmente feias e retas. Para esconder essa origem hexagonal, eu precisaria suavizar as fronteiras entre os biomas. É assim que fica agora em estrutura de arame com faces tringulares reais:

insira a descrição da imagem aqui

O que estou buscando é algo mais ou menos assim:

insira a descrição da imagem aqui

Cada vértice possui um atributo que contém o tipo de bioma. Também posso adicionar atributos especiais aos vértices na borda entre dois biomas, mas simplesmente não consigo descobrir como fazer isso no código de sombreamento, obviamente ruído. está envolvido aqui, mas como faço para torná-lo contínuo através de múltiplas faces e toda a borda de múltiplos biomas?

Estou renderizando com WebGL usando THREE.js

user1617735
fonte
Você tentou o MSAA?
Milo Lu
@Milo Você já tentou ler a pergunta?
Bálint
Como você envia as informações do bioma para o shader?
Bálint
@ Bioma Bálint No momento, estou passando a cor via atributo de vértice, quando vou realmente provar cores de texturas reais, em vez de cores simples, passarei o tipo de bioma como inteiro.
user1617735
Os atributos de vértice não podem ser usados ​​como você deseja, se você alternar para texturas, isso será muito mais fácil. Carregar as informações bioma como uma textura, então você vai ser capaz de obter os biomas próximas
Bálint

Respostas:

9

Outras respostas aqui sugerem o uso de uma textura. Aqui está uma técnica que não usa texturas.

Você deseja que os limites entre hexágonos sejam interessantes. É mais fácil estabelecer limites interessantes quando você os move para o centro do que está desenhando. Em vez de desenhar os ladrilhos diretamente, você desenha o "duplo" do ladrilho. Essa técnica é chamada de "esquadrias" ( aqui e aqui e aqui ). O dual de um hexágono é um triângulo, então desenhávamos esses triângulos em vez dos hexágonos:

duplos triângulo de um hexágono

Os limites entre hexágonos agora estão no meio dos triângulos renderizados, de modo que vamos fazer coisas mais interessantes com eles. Bônus: você só precisa desenhar dois triângulos por hexágono, em vez de seis (ou vinte e quatro como você está fazendo agora).

Dentro de cada um desses triângulos, queremos que o shader de fragmento desenhe os hexágonos. Podemos fazer isso com coordenadas baricêntricas . Coloque (1,0,0), (0,1,0) e (0,0,1) em cada vértice do triângulo. Dentro do triângulo, essas coordenadas serão interpoladas. O shader de fragmento receberá (a, b, c) e pode ver qual valor é maior - isso nos dirá qual dos três hexágonos deve ser desenhado neste momento.

float max_n = max(barycentric.r, max(barycentric.g, barycentric.b)); if (max_n == barycentric.r) { color = v_color0; } else if (max_n == barycentric.g) { color = v_color1; } else if (max_n == barycentric.b) { color = v_color2; }

Isso é para linhas retas.

Se você deseja bordas ruidosas, pode adicionar ruído às coordenadas baricêntricas:

hexágono com bordas barulhentas

Ao brincar com o comprimento de onda da amplitude / frequência do ruído, você pode obter alguns efeitos interessantes:

hexágono com bordas ainda mais ruidosas

Você precisa ter cuidado com o ruído, certificando-se de que seja consistente através dos limites do triângulo. Uma maneira de fazer isso é passar um id hexadecimal e usá-lo como valor inicial para cada um dos três valores de ruído adicionados às coordenadas baricêntricas.

Eu fiz uma demonstração interativa aqui . (Para a demonstração, não implementei o ID hexadecimal ou outras coisas que você pode precisar se estiver fazendo esse trabalho em um projeto real - é apenas uma demonstração rápida e suja)

amitp
fonte
Agora, esse é um material de resposta de alta qualidade. Dica do chapéu
Quentin
Ótima resposta! Correção sutil: polígonos regulares, incluindo hexágonos, são auto-duplos. No entanto, mosaicos de triângulos e hexágonos são duplos um do outro, como ilustra sua resposta.
precisa saber é o seguinte
0

Tenho certeza de que "poderia" ser resolvido com algum algoritmo de imagem, mas se fosse eu, provavelmente o resolveria com texturas. Eu fazia texturas hexagonais, as colocava em um atlas de textura, depois, para cada hexágono, olhava para os vizinhos e decidia qual textura aplicar.

As texturas precisariam ter versões para cada tipo de terreno e versões para cada tipo de transição.

Isso é semelhante a quantos sistemas baseados em ladrilhos fazem terreno. Aqui está um exemplo de jogos 2D .

Outra possibilidade seria apenas ter suas texturas para seus vários tipos de terreno (água, neve, sujeira, grama) e adicionar quantidades de mistura a cada vértice do hexágono para decidir como misturá-las.

Este artigo mostra a ideia de misturar texturas de terreno. Não estou sugerindo seguir a implementação deles, mas isso mostra a ideia.

gman
fonte
Bem, no meu caso, não é simples preparar texturas, pois os hexágonos não são uniformes, a forma e o tamanho diferem em todo o mapa. Infelizmente esse é o preço que tenho que pagar por ter um planeta, não apenas um mapa plano. Além disso, o artigo parece muito interessante, obrigado. Embora no meu caso eu esteja processando o terreno lá de cima, provavelmente de uma altitude onde, por exemplo, você não pode ver uma única árvore e toda a floresta se parece com uma massa verde.
user1617735
0

Primeiro, renderize seus biomas em uma textura. Mapeie os triângulos para cabos de texto. Você pode fazer isso usando uma projeção mercator, ou, melhor, um mapa de cubo . Agora, no shader de fragmento, faça algo assim:

// frequency and magnitude of the noise in texels.
uniform float frequency;
uniform float magnitude;

// This function should just look like random smooth noise in x,y
// This one isn't great, but you can experiment.
vec2 noise(vec3 vertexPos) {
    return vec2(sin(vertexPos.x * frequency + vertexPos.y * frequency) * magnitude, 
                cos(vertexPos.y * frequency * 2 + vertexPos.z * frequency) * magnitude);
}

// Sample the texture and preturb it with noise based on the world
// position of the fragment.
vec4 frag(vec2 texCoord, vec3 fragmentPos) {
   return texture(mySampler, texCoord + noise(fragmentPos));
}

onde noisehá alguma função pseudo-aleatória (usando, por exemplo, sinusóides) na posição 3D do vértice no espaço do modelo, que retorna um deslocamento ruidoso para a coordenada da textura. Prove a textura usando GL_NEARESTpara manter bordas nítidas.

mklingen
fonte