Em parte devido à popularidade do
Minecraft , recentemente houve um interesse crescente na idéia de um jogo que ocorre em um mundo em cubos, construído em relevo 3D e repleto de elementos como cavernas, penhascos etc. Esse mundo é um aplicativo ideal para o ruído gerado no estilo da minha biblioteca
ANL . Este artigo surgiu das discussões das minhas
tentativas anteriores
de implementar essa técnica. Desde então, pequenas alterações apareceram na estrutura da biblioteca.
Nos posts anteriores, falei sobre o uso de recursos de ruído 3D para implementar terrenos no estilo Minecraft. Depois disso, a biblioteca evoluiu um pouco, então decidi voltar a esse tópico. Como tive que responder muitas perguntas sobre esse sistema, tentarei falar mais sobre os conceitos envolvidos. Para tornar os conceitos básicos mais claros, começarei com a idéia de gerar um terreno 2D usado em jogos como Terraria e King Arthur's Gold e depois expandir o sistema para exemplos 3D como Minecraft. Isso me permitirá demonstrar conceitos de maneira mais eficaz usando imagens como exemplo.
Este sistema foi desenvolvido levando em consideração o seguinte objetivo abstrato: poderemos passar a coordenada de um determinado ponto ou célula para o sistema e determinar que tipo de bloco deve estar nesse local. Queremos que o sistema seja uma “caixa preta”: passamos um ponto, retornamos o tipo de bloco. Obviamente, isso se aplica apenas à geração inicial do mundo. Blocos nesses jogos podem ser alterados pelas ações do jogador, e será inconveniente tentar descrever essas alterações usando o mesmo sistema. Tais mudanças devem ser rastreadas de alguma outra maneira. Este sistema gera o mundo original, intocado e intocado pelas mãos do jogador e de outros personagens.
Talvez essa técnica não seja adequada para modelar sistemas como grama ou outras entidades biológicas, dado que esses sistemas são entidades complexas que não são tão fáceis de modelar implicitamente. O mesmo se aplica a sistemas como queda de neve, formação de gelo, etc. ... A técnica descrita no artigo é um
método implícito , ou seja, aquele que pode ser estimado em um ponto e cujo valor em um determinado ponto não depende dos valores circundantes. Os tipos de sistemas biológicos e outros para realizar simulações precisas geralmente precisam considerar valores ambientais. Por exemplo: quanta luz solar cai em um bloco? Existe água por perto? Essas e outras perguntas precisam ser respondidas para simular o crescimento e a disseminação de sistemas biológicos, bem como, em menor grau, outros tipos de sistemas relacionados ao clima. Além disso, essa técnica não é adequada para modelagem de água. Nesse sistema, não há conceito de fluxo, conhecimento de mecânica dos fluidos ou gravidade. A água é um tópico complexo que requer muitos cálculos complexos.
Então, estamos apenas modelando a terra e as pedras. Precisamos de uma função que diga qual deve ser o local: terra, areia, ar, ouro, ferro, carvão, etc. ... Mas começaremos do mais simples. Precisamos de uma função que diga se o bloco é sólido ou oco (cheio de ar). Esta função deve simular a terra que nos rodeia. Ou seja, o céu está acima, a terra está abaixo. Então, vamos assumir a tarefa bíblica e separar o céu da terra. Para fazer isso, estudamos a função
Gradiente . A função Gradiente recebe um segmento de linha em um espaço N-dimensional (ou seja, em qualquer espaço de coordenadas, seja 2D, 3D ou superior), e calcula o campo de gradiente ao longo desse segmento. As coordenadas de entrada são projetadas nesse segmento e seu valor de gradiente é calculado dependendo de onde elas se encontram em relação aos pontos finais do segmento. Os pontos projetados recebem valores atribuídos no intervalo (-1,1). E este será um bom começo para nós. Podemos definir a função Gradiente ao longo do eixo Y. No topo do intervalo, comparamos o campo de gradiente com -1 (ar) e na parte inferior com 1 (terra).
terraintree =
{
{name = "ground_gradient", tipo = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1}
}
(Vou explicar o registro brevemente. O código para os exemplos está escrito na tabela de declaração Lua. Para obter mais informações sobre o formato, consulte a seção sobre
integração Lua . Em essência, o formato foi projetado para ser analisado por uma classe especial que lê anúncios e os transforma em árvores de instância de módulo de ruído. o formato é mais detalhado no formato C ++, porque é mais compacto e limpo. Na minha opinião, o código fonte é mais legível e compactado que o código C ++. Na maioria das vezes, as declarações são fáceis de ler e entender. Os módulos têm nomes, as fontes são especificadas nome ou valor. O código Lua usado para analisar as declarações da tabela está incluído no código-fonte, caso você queira usá-las diretamente.)
No caso de 2D, a função Gradiente recebe o segmento de linha no formato (x1, x2, y1, y2) e, no caso de 3D, o formato é expandido para (x1, x2, y1, y2, z1, z2). O ponto formado por (x1, y1) denota o início do segmento de linha associado a 0. O ponto formado (x2, y2) é o final do segmento associado a 1. Ou seja, aqui mapeamos o segmento de linha (0,1) -> ( 0,0) com um gradiente. Portanto, o gradiente estará entre as regiões da função Y = 1 e Y = 0. Ou seja, essa faixa forma as dimensões do mundo em Y. Qualquer parte do mundo estará nessa faixa. Podemos ajustar qualquer região ao longo de X (quase ad infinitum, mas aqui a precisão
double
nos limita), mas tudo é interessante, ou seja, a superfície da terra estará dentro dessa faixa. Esse comportamento pode ser alterado, mas dentro dele temos um grande grau de flexibilidade. Apenas não esqueça que quaisquer valores que estão acima ou abaixo dessa faixa provavelmente não serão interessantes, porque os valores acima provavelmente serão do ar e os valores abaixo do solo. (Como você verá em breve, essa afirmação pode se mostrar errada.) Para a maioria das imagens desta série, corresponderei à região quadrada dada pelo quadrado (0,1) -> (1,0) no espaço 2D. Portanto, no começo, nosso mundo se parece com isso:
Nada interessante até agora; Além disso, esta imagem não responde à pergunta “o ponto dado é sólido ou oco?”. Para responder a essa pergunta, precisamos aplicar a
função Step (função definida por partes). Em vez de um gradiente suave, precisamos de uma separação clara, na qual todos os locais de um lado são ocos e todos os locais do outro lado são sólidos. No
ANL, isso pode ser implementado usando a função
Selecionar . A função Selecionar recebe duas funções ou valores recebidos (nesse caso, eles serão iguais a "sólido" e "Oco" (Aberto)) e os seleciona com base no valor da função de controle (neste caso, Gradiente). O módulo Select possui dois parâmetros adicionais,
threshold e
falloff , que afetam esse processo. Nesse estágio, o
falloff é indesejável, portanto o tornamos igual a 0. O parâmetro
threshold decide para onde a linha divisória entre Solid e Open irá. Tudo o que for maior que esse valor na função Gradiente se tornará Sólido e tudo que for menor que o limite ficará Aberto. Como Gradiente compara o intervalo com valores de 0 e 1, seria lógico colocar o limite em 0,5. Então, dividimos o espaço exatamente ao meio. O valor 1 será um local sólido e o valor 0 será oco. Ou seja, definimos a função do plano terrestre da seguinte maneira:
terraintree =
{
{name = "ground_gradient", digite = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_select", digite = "select", baixo = 0, alto = 1, limite = 0,5, control = "ground_gradient"}
}
Comparando a mesma área da função como antes, obtemos algo semelhante:
Esta figura responde claramente à questão de saber se o ponto dado é sólido ou oco. Podemos chamar a função com qualquer coordenada possível do espaço 2D, e seu resultado será 1 ou 0, dependendo de onde o ponto é relativo à superfície da Terra. No entanto, essa função não é particularmente interessante, é apenas uma linha plana que se estende até o infinito. Para reviver a imagem, usamos uma técnica chamada "turbulência".
"Turbulência" é uma designação complexa do conceito de adição de valores às coordenadas recebidas de uma função. Imagine que chamamos a função da Terra acima com a coordenada (0,1). Está acima do plano do solo, porque em Y = 1 o gradiente tem um valor de 0, que é menor que o limite = 0,5. Ou seja, esse ponto será calculado como Aberto. Mas e se, antes de invocar a função da Terra, de alguma forma transformarmos esse ponto? Suponha que subtraímos um valor aleatório da coordenada Y, por exemplo, 3. Subtraímos 3 e obtemos a coordenada (0, -2). Se agora chamarmos a função ground para esse ponto, o ponto será considerado sólido, porque Y = -2 fica abaixo do segmento Gradiente correspondente a 1. De repente, o ponto oco (0,1) se transforma em sólido. Teremos um bloco de pedra sólida pendurado no ar. Isso pode ser feito com qualquer ponto da função adicionando ou subtraindo um número aleatório da coordenada Y do ponto de entrada antes de chamar a função ground_select. Aqui está uma imagem da função ground_select mostrando isso. Antes de chamar a função ground_select, o valor no intervalo (-0,25, 0,25) é adicionado à coordenada Y de cada ponto.

Isso é mais interessante do que uma linha plana, mas não muito semelhante à Terra, porque cada ponto se move para um valor completamente aleatório, o que cria um padrão caótico. No entanto, se usarmos uma função aleatória contínua, por exemplo,
Fractal da biblioteca
ANL , em vez de um padrão aleatório, obteremos algo mais controlável. Portanto, vamos conectar um fractal ao plano da Terra e ver o que acontece.
terraintree =
{
{name = "ground_gradient", digite = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 6, frequência = 2},
{name = "ground_scale", tipo = "scaleoffset", scale = 0.5, offset = 0, source = "ground_shape_fractal"},
{name = "ground_perturb", tipo = "translatoromain", fonte = "ground_gradient", ty = "ground_scale"},
{name = "ground_select", digite = "select", baixo = 0, alto = 1, limite = 0,5, control = "ground_perturb"}
}
Há alguns aspectos que vale a pena notar. Primeiro, definimos o módulo Fractal e o
associamos ao módulo
ScaleOffset . O módulo ScaleOffset dimensiona os valores fractais de saída para um nível mais conveniente. Parte do relevo pode ser montanhosa e requer uma escala maior, e outra parte - mais plana e com uma escala menor. Falaremos sobre diferentes tipos de terreno mais tarde, mas por enquanto vamos usá-los para demonstração. Os valores de saída da função agora fornecerão a seguinte imagem:
Isso é mais interessante do que apenas ruído aleatório, certo? Pelo menos, parece mais terra, embora parte da paisagem pareça incomum e as ilhas voadoras sejam completamente estranhas. A razão para isso foi que cada ponto individual do mapa de saída é aleatoriamente alterado por um valor diferente determinado pelo fractal. Para ilustrar isso, mostre a saída fractal que executa a distorção:

Na imagem acima, todos os pontos pretos têm um valor de -0,25 e todos os pontos brancos têm um valor de 0,25. Ou seja, onde o fractal é preto, o ponto correspondente da função da Terra será deslocado "para baixo" em 0,25. (0,25 significa 1/4 da tela.) Como um ponto pode ser deslocado levemente e o outro ponto acima no espaço pode ser deslocado mais, existe a possibilidade de saliências de rochas e ilhas voadoras. As saliências na natureza são bastante naturais, em contraste com as ilhas voadoras. (A menos que estejamos no filme "Avatar".) Se o seu jogo precisa de um cenário tão fantástico, é ótimo, mas se você precisar de um modelo mais realista, precisamos ajustar um pouco a função fractal. Felizmente, a função
ScaleDomain pode fazer
isso .
Queremos que a função se comporte como uma função de mapa de altura. Imagine um mapa de altura 2D em que cada ponto no mapa represente a altura de um ponto na grade de pontos da grade que são elevados para cima ou para baixo. Os valores em branco do mapa indicam colinas altas, vales negros - baixos. Precisamos de um comportamento semelhante, mas, para alcançá-lo, precisamos nos livrar essencialmente de uma das dimensões. No caso de um mapa de altura, criamos uma elevação 3D a partir de um mapa de altura 2D. Da mesma forma, no caso de terrenos 2D, precisamos de um mapa de altura 1D. Tendo feito para que todos os pontos de um fractal com a mesma coordenada Y tenham o mesmo valor, podemos mudar todos os pontos com a mesma coordenada X pela mesma quantidade, para que as ilhas voadoras desapareçam. Para fazer isso, você pode usar o ScaleDomain, redefinindo o coeficiente de escala. Ou seja, antes de chamar a função ground_shape_fractal, chamamos ground_scale_y para definir a coordenada y como 0. Isso garante que o valor Y não afete a saída do fractal, transformando-o essencialmente em uma função de ruído 1D. Para fazer isso, faremos as seguintes alterações:
terraintree =
{
{name = "ground_gradient", digite = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 6, frequência = 2},
{name = "ground_scale", tipo = "scaleoffset", scale = 0.5, offset = 0, source = "ground_shape_fractal"},
{name = "ground_scale_y", tipo = "scaledomain", fonte = "ground_scale", scaley = 0},
{name = "ground_perturb", tipo = "translatoromain", fonte = "ground_gradient", ty = "ground_scale_y"},
{name = "ground_select", digite = "select", baixo = 0, alto = 1, limite = 0,5, control = "ground_perturb"}
}
Iremos encadear a função ScaleDomain com ground_scale e, em seguida, modificaremos os dados ground_perturb originais para serem uma função ScaleDomain. Isso mudará o fractal que desloca a Terra e o transforma em algo assim:
Agora, se dermos uma olhada na saída, obtemos o resultado:
Muito melhor As ilhas voadoras desapareceram completamente, e o relevo é mais como montanhas e colinas. Infelizmente, perdemos saliências e falésias. Agora a terra inteira é contínua e inclinada. Se desejar, você pode corrigir isso de várias maneiras.
Primeiro, você pode usar outra função
TranslateDomain , juntamente com outra função
Fractal . Se aplicarmos uma pequena quantidade de turbulência fractal na direção X, podemos distorcer levemente as bordas e superfícies das montanhas, e isso provavelmente será suficiente para formar precipícios e bordas. Vamos ver isso em ação.
terraintree =
{
{name = "ground_gradient", digite = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "ground_shape_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 6, frequência = 2},
{name = "ground_scale", tipo = "scaleoffset", scale = 0.5, offset = 0, source = "ground_shape_fractal"},
{name = "ground_scale_y", tipo = "scaledomain", fonte = "ground_scale", scaley = 0},
{name = "ground_perturb", tipo = "translatoromain", fonte = "ground_gradient", ty = "ground_scale_y"},
{name = "ground_overhang_fractal", tipo = "fractal", tipo de fractal = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 6, frequência = 2},
{name = "ground_overhang_scale", tipo = "scaleoffset", fonte = "ground_overhang_fractal", scale = 0,2, offset = 0},
{name = "ground_overhang_perturb", tipo = "translatoromain", fonte = "ground_perturb", tx = "ground_overhang_scale"},
{name = "ground_select", digite = "select", baixo = 0, alto = 1, limite = 0,5, control = "ground_overhang_perturb"}
}
E aqui está o resultado:
A segunda maneira: você pode simplesmente definir o parâmetro
scaley da função
ground_scale_y para um valor maior que 0. Se você deixar uma pequena escala em Y, obteremos uma fração da variabilidade; no entanto, quanto maior a escala, mais forte o relevo será semelhante à versão anterior sem redimensionar.
Os resultados parecem muito mais interessantes do que montanhas inclinadas comuns. No entanto, por mais interessantes que sejam, o jogador ainda ficará entediado em explorar o relevo com o mesmo padrão, estendendo-se por muitos quilômetros. Além disso, esse alívio seria muito irrealista. No mundo real, há muita variação que aumenta o interesse do alívio. Vamos ver o que pode ser feito para tornar o mundo mais diversificado.
Observando o exemplo de código anterior, você pode ver um padrão específico nele. Temos uma função de gradiente, que é controlada por funções que dão forma à Terra, após a qual uma função definida por partes é aplicada e a Terra fica cheia. Ou seja, será mais lógico complicar o alívio na fase de moldar a terra. Em vez de um fractal se deslocar ao longo de Y e outro se deslocar ao longo de X, podemos alcançar o grau de complexidade necessário (levando em conta o desempenho: cada fractal requer custos computacionais adicionais, portanto, devemos tentar ser conservadores.) Podemos especificar as formas da Terra, que são montanhas, sopé , planícies planas, terrenos baldios, etc ... e use a saída das várias funções
Select encadeadas com fractais de baixa frequência para delinear áreas de cada tipo. Então, vamos ver como você pode implementar diferentes tipos de terreno.
Para ilustrar o princípio, distinguimos três tipos de relevo: planaltos (colinas lisas e inclinadas), montanhas e planícies (principalmente planas). Para alternar entre eles, usamos um sistema baseado em seleção e os combinamos em uma tela complexa. Então aqui vamos nós ...
Contrafortes:Com eles, tudo é simples. Podemos usar o esquema usado acima, reduzir um pouco a amplitude das colinas, talvez até torná-las mais subtrativas do que aditivas. para diminuir as alturas médias. Também podemos reduzir a contagem de oitavas para suavizá-las.
{name = "lowland_shape_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 2, frequência = 1},
{name = "lowland_autocorrect", tipo = "autocorrect", fonte = "lowland_shape_fractal", low = 0, high = 1},
{name = "lowland_scale", tipo = "scaleoffset", fonte = "lowland_autocorrect", scale = 0,2, offset = -0,25},
{name = "lowland_y_scale", tipo = "scaledomain", fonte = "lowland_scale", scaley = 0},
{name = "lowland_terrain", tipo = "translatoromain", fonte = "ground_gradient", ty = "lowland_y_scale"},
Terras Altas:Com eles também tudo é simples. (De fato, nenhum desses tipos de terreno é difícil.) No entanto, usamos uma base diferente para fazer as colinas parecerem dunas.
{name = "highland_shape_fractal", tipo = "fractal", fractaltype = anl.RIDGEDMULTI, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 2, frequência = 2},
{name = "highland_autocorrect", tipo = "autocorrect", fonte = "highland_shape_fractal", baixo = 0, alto = 1},
{name = "highland_scale", tipo = "scaleoffset", fonte = "highland_autocorrect", scale = 0,45, offset = 0},
{name = "highland_y_scale", tipo = "scaledomain", fonte = "highland_scale", scaley = 0},
{name = "highland_terrain", tipo = "translateomain", fonte = "ground_gradient", ty = "highland_y_scale"},
Montanhas: {name = "mountain_shape_fractal", tipo = "fractal", fractaltype = anl.BILLOW, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 4, frequência = 1},
{name = "mountain_autocorrect", tipo = "autocorrect", fonte = "mountain_shape_fractal", baixo = 0, alto = 1},
{name = "mountain_scale", tipo = "scaleoffset", fonte = "mountain_autocorrect", scale = 0,75, deslocamento = 0,25},
{name = "mountain_y_scale", tipo = "scaledomain", fonte = "mountain_scale", scaley = 0.1},
{name = "mountain_terrain", tipo = "translatoromain", fonte = "ground_gradient", ty = "mountain_y_scale"},
Claro, você pode abordar esse processo ainda mais criativamente, mas, em geral, o padrão será assim. Destacamos as características do tipo de relevo e selecionamos as funções de ruído para elas. Por tudo isso, os mesmos princípios se aplicam; As principais diferenças são de escala. Agora, para conectá-los, prepararemos fractais adicionais que controlarão a função
Selecionar . Em seguida, encadeamos os módulos Select para gerar todo o terreno.
{name = "terrain_type_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo básico = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 3, frequência = 0,5},
{name = "terrain_autocorrect", tipo = "autocorrect", fonte = "terrain_type_fractal", baixo = 0, alto = 1},
{name = "terrain_type_cache", digite = "cache", origem = "terrain_autocorrect"},
{name = "highland_mountain_select", digite = "select", low = "highland_terrain", high = "mountain_terrain", control = "terrain_type_cache", limite = 0,55, falloff = 0,15},
{name = "highland_lowland_select", digite = "select", low = "lowland_terrain", high = "highland_mountain_select", control = "terreno_type_cache", limite = 0,25, queda = 0,15},
Então, aqui definimos três tipos principais de terreno: terras baixas, terras altas e montanhas. Usamos um fractal para selecionar um deles, para que haja transições naturais (terras baixas-> terras altas-> montanhas). Em seguida, usamos outro fractal para inserir aleatoriamente ermos no mapa. Veja como é a cadeia de módulos finalizada:
terraintree =
{
{name = "lowland_shape_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 2, frequência = 1},
{name = "lowland_autocorrect", tipo = "autocorrect", fonte = "lowland_shape_fractal", low = 0, high = 1},
{name = "lowland_scale", tipo = "scaleoffset", fonte = "lowland_autocorrect", scale = 0,2, offset = -0,25},
{name = "lowland_y_scale", tipo = "scaledomain", fonte = "lowland_scale", scaley = 0},
{name = "lowland_terrain", tipo = "translatoromain", fonte = "ground_gradient", ty = "lowland_y_scale"},
{name = "ground_gradient", digite = "gradiente", x1 = 0, x2 = 0, y1 = 0, y2 = 1},
{name = "highland_shape_fractal", tipo = "fractal", fractaltype = anl.RIDGEDMULTI, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 2, frequência = 2},
{name = "highland_autocorrect", tipo = "autocorrect", fonte = "highland_shape_fractal", baixo = 0, alto = 1},
{name = "highland_scale", tipo = "scaleoffset", fonte = "highland_autocorrect", scale = 0,45, offset = 0},
{name = "highland_y_scale", tipo = "scaledomain", fonte = "highland_scale", scaley = 0},
{name = "highland_terrain", tipo = "translateomain", fonte = "ground_gradient", ty = "highland_y_scale"},
{name = "mountain_shape_fractal", tipo = "fractal", fractaltype = anl.BILLOW, basistype = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 4, frequência = 1},
{name = "mountain_autocorrect", tipo = "autocorrect", fonte = "mountain_shape_fractal", baixo = 0, alto = 1},
{name = "mountain_scale", tipo = "scaleoffset", fonte = "mountain_autocorrect", scale = 0,75, deslocamento = 0,25},
{name = "mountain_y_scale", tipo = "scaledomain", fonte = "mountain_scale", scaley = 0.1},
{name = "mountain_terrain", tipo = "translatoromain", fonte = "ground_gradient", ty = "mountain_y_scale"},
{name = "terrain_type_fractal", tipo = "fractal", fractaltype = anl.FBM, tipo básico = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 3, frequência = 0,5},
{name = "terrain_autocorrect", tipo = "autocorrect", fonte = "terrain_type_fractal", baixo = 0, alto = 1},
{name = "terrain_type_cache", digite = "cache", origem = "terrain_autocorrect"},
{name = "highland_mountain_select", digite = "select", low = "highland_terrain", high = "mountain_terrain", control = "terrain_type_cache", limite = 0,55, falloff = 0,15},
{name = "highland_lowland_select", digite = "select", low = "lowland_terrain", high = "highland_mountain_select", control = "terreno_type_cache", limite = 0,25, queda = 0,15},
{name = "ground_select", digite = "select", baixo = 0, alto = 1, limite = 0,5, control = "highland_lowland_select"}
}
Aqui estão alguns exemplos dos relevos resultantes:
Você pode perceber que é obtida uma variabilidade bastante alta. Em alguns lugares, montanhas altas e quebradas aparecem; em outros, planícies inclinadas e lisas. Agora precisamos adicionar cavernas para que possamos explorar as maravilhas do submundo.
Para cavernas, eu uso o sistema multiplicativo aplicado ao
ground_select . Ou seja, eu crio uma função que gera 1 ou 0 e multiplico-as pela saída de
ground_select . Graças a isso, qualquer ponto da função se torna oco, para o qual o valor da função das cavernas é 0. Ou seja, onde eu quero obter a caverna, a função das cavernas deve retornar 0 e, onde a caverna não deve estar, a função deve ser 1. Quanto à forma cavernas, quero estabelecer um sistema de cavernas baseado no
Multifractal Ridged de 1 oitava.
{name = "cave_shape", tipo = "fractal", tipo fractal = anl.RIDGEDMULTI, tipo básico = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 1, frequência = 2},
O resultado é algo como isto:
Se aplicarmos a função
Selecionar como uma função definida por partes, como fizemos com o gradiente da Terra, implementando-a para que a parte inferior do limite de seleção seja 1 (não há caverna) e a parte superior seja 0 (há uma caverna), o resultado será algo como isto :
{name = "cave_shape", tipo = "fractal", tipo fractal = anl.RIDGEDMULTI, tipo básico = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 1, frequência = 2},
{name = "cave_select", tipo = "select", baixo = 1, alto = 0, controle = "cave_shape", limite = 0,8, falloff = 0},
Resultado:
Obviamente, parece bastante suave, então adicione algum ruído fractal para distorcer a área.
{name = "cave_shape", tipo = "fractal", tipo fractal = anl.RIDGEDMULTI, tipo básico = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 1, frequência = 2},
{name = "cave_select", tipo = "select", baixo = 1, alto = 0, controle = "cave_shape", limite = 0,8, falloff = 0},
{name = "cave_perturb_fractal", tipo = "fractal", tipo de fractal = anl.FBM, tipo de base = anl.GRADIENT, interptype = anl.QUINTIC, oitavas = 6, frequência = 3},
{name = "cave_perturb_scale", tipo = "scaleoffset", fonte = "cave_perturb_fractal", escala = 0,25, deslocamento = 0},
{name="cave_perturb", type="translatedomain", source="cave_select", tx="cave_perturb_scale"},
:
. , , :
threshold cave_select , . , — , .
highland_lowland_select , , , , . , , . , . ,
highland_lowland_select cave_shape , . —
Cache . , , , . , (
highland_lowland_select ) . . , :
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
Cache,
ground_select , , . , :
{name="cave_shape", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
Primeiro, adicionamos a função Viés . Isso é por conveniência, porque permite ajustar o intervalo da função de atenuação do gradiente. Em seguida, a função cave_shape_attenuate é adicionada , que é um combinador do tipo anl :: MULT . Ela multiplica o gradiente por cave_shape . Em seguida, o resultado dessa operação é passado para a função cave_perturb . O resultado é algo como isto:, . ( , , . — , - , (0,1).) , , , , . , .
terraintree=
{
{name="ground_gradient", type="gradient", x1=0, x2=0, y1=0, y2=1},
{name="lowland_shape_fractal", type="fractal", fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_shape", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape", source_1="cave_attenuate_bias"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
{name="ground_cave_multiply", type="combiner", operation=anl.MULT, source_0="cave_select", source_1="ground_select"}
}
, :
. , . . , . ? ? , , , . , . .
, . threshold
cave_select cave_attenuate_bias ,
cave_attenuate_bias , , . , Y, X ( , X). ,
cave_shape_attenuate , , (, ), . select
terrain_type_fractal , , . , , , , , , . , .
. . , , , . , . , .
ScaleOffset , . 2D , 3D , .
3D
,
Terraria King Arthur's Gold , ,
Minecraft Infiniminer ? ? , . 3D-. 3D-, 3D- , Y , 2D-. - , , . , Ridged Multifractal 2D- , 3D , , . 3D , 1- Ridged Multifractal, seed. Select 1 0, . , , , , .
terraintree3d=
{
{name="ground_gradient", type="gradient", x1=0, x2=0, y1=0, y2=1},
{name="lowland_shape_fractal", type="fractal", fractaltype=anl.BILLOW, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=2, frequency=0.25},
{name="lowland_autocorrect", type="autocorrect", source="lowland_shape_fractal", low=0, high=1},
{name="lowland_scale", type="scaleoffset", source="lowland_autocorrect", scale=0.125, offset=-0.45},
{name="lowland_y_scale", type="scaledomain", source="lowland_scale", scaley=0},
{name="lowland_terrain", type="translatedomain", source="ground_gradient", ty="lowland_y_scale"},
{name="highland_shape_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=4, frequency=2},
{name="highland_autocorrect", type="autocorrect", source="highland_shape_fractal", low=-1, high=1},
{name="highland_scale", type="scaleoffset", source="highland_autocorrect", scale=0.25, offset=0},
{name="highland_y_scale", type="scaledomain", source="highland_scale", scaley=0},
{name="highland_terrain", type="translatedomain", source="ground_gradient", ty="highland_y_scale"},
{name="mountain_shape_fractal", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=8, frequency=1},
{name="mountain_autocorrect", type="autocorrect", source="mountain_shape_fractal", low=-1, high=1},
{name="mountain_scale", type="scaleoffset", source="mountain_autocorrect", scale=0.45, offset=0.15},
{name="mountain_y_scale", type="scaledomain", source="mountain_scale", scaley=0.25},
{name="mountain_terrain", type="translatedomain", source="ground_gradient", ty="mountain_y_scale"},
{name="terrain_type_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=3, frequency=0.125},
{name="terrain_autocorrect", type="autocorrect", source="terrain_type_fractal", low=0, high=1},
{name="terrain_type_y_scale", type="scaledomain", source="terrain_autocorrect", scaley=0},
{name="terrain_type_cache", type="cache", source="terrain_type_y_scale"},
{name="highland_mountain_select", type="select", low="highland_terrain", high="mountain_terrain", control="terrain_type_cache", threshold=0.55, falloff=0.2},
{name="highland_lowland_select", type="select", low="lowland_terrain", high="highland_mountain_select", control="terrain_type_cache", threshold=0.25, falloff=0.15},
{name="highland_lowland_select_cache", type="cache", source="highland_lowland_select"},
{name="ground_select", type="select", low=0, high=1, threshold=0.5, control="highland_lowland_select_cache"},
{name="cave_attenuate_bias", type="bias", source="highland_lowland_select_cache", bias=0.45},
{name="cave_shape1", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_shape2", type="fractal", fractaltype=anl.RIDGEDMULTI, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=1, frequency=4},
{name="cave_shape_attenuate", type="combiner", operation=anl.MULT, source_0="cave_shape1", source_1="cave_attenuate_bias", source_2="cave_shape2"},
{name="cave_perturb_fractal", type="fractal", fractaltype=anl.FBM, basistype=anl.GRADIENT, interptype=anl.QUINTIC, octaves=6, frequency=3},
{name="cave_perturb_scale", type="scaleoffset", source="cave_perturb_fractal", scale=0.5, offset=0},
{name="cave_perturb", type="translatedomain", source="cave_shape_attenuate", tx="cave_perturb_scale"},
{name="cave_select", type="select", low=1, high=0, control="cave_perturb", threshold=0.48, falloff=0},
{name="ground_cave_multiply", type="combiner", operation=anl.MULT, source_0="cave_select", source_1="ground_select"}
}
:
, . , , , , .… , , .