Bordes ruidosos, suavizando los bordes entre caras a través del sombreador de fragmentos

8

Tengo un terreno generado, con geometría hexagonal, según la captura de pantalla a continuación:

ingrese la descripción de la imagen aquí

Luego genero biomas, pero como pueden ver, los límites entre ellos son realmente feos y rectos. Para ocultar ese origen hexagonal, necesitaría suavizar los bordes entre los biomas. Así es como se ve ahora en estructura metálica con caras tringulares reales:

ingrese la descripción de la imagen aquí

Lo que busco es algo más como esto:

ingrese la descripción de la imagen aquí

Cada vértice tiene un atributo que contiene el tipo de bioma, también puedo agregar atributos especiales a los vértices en el borde entre dos biomas, pero parece que no soy capaz de descubrir cómo lograr esto en el código del sombreador, obviamente ruido está involucrado aquí, pero ¿cómo hago que sea continuo a través de múltiples caras y todo el borde de múltiples biomas?

Estoy renderizando con WebGL usando THREE.js

usuario1617735
fuente
¿Intentaste con MSAA?
Milo Lu
@Milo ¿Has intentado leer la pregunta?
Bálint
¿Cómo se envía la información del bioma al sombreador?
Bálint
@ Bioma Bálint En este momento estoy pasando el color a través del atributo de vértice, cuando realmente pueda muestrear colores de texturas reales, en lugar de colores simples, pasaré el tipo de bioma como entero.
user1617735
Los atributos de vértice no se pueden usar como desea, si cambia a texturas, esto será mucho más fácil. Cargue la información del bioma como una textura, para que pueda obtener los biomas cercanos
Bálint

Respuestas:

9

Otras respuestas aquí sugieren usar una textura. Aquí hay una técnica que no usa texturas.

Desea que los límites entre hexágonos sean interesantes. Es más fácil establecer límites interesantes cuando los mueve al centro de lo que está dibujando. En lugar de dibujar los mosaicos directamente, dibuja el "dual" del mosaico. Esta técnica se llama “azulejos de esquina” ( aquí y aquí y aquí ). El dual de un hexágono es un triángulo, por lo que dibujaríamos estos triángulos en lugar de los hexágonos:

triángulos duales de un hexágono

Los límites entre los hexágonos ahora están en el medio de los triángulos renderizados, por lo que nos permitirá hacer cosas más interesantes con ellos. Bonificación: solo necesitas dibujar dos triángulos por hexágono, en lugar de seis (o veinticuatro como lo estás haciendo ahora).

Dentro de cada uno de esos triángulos queremos que el sombreador de fragmentos dibuje los hexágonos. Podemos hacer eso con coordenadas barcéntricas . Ponga (1,0,0), (0,1,0) y (0,0,1) en cada vértice del triángulo. Dentro del triángulo, esas coordenadas serán interpoladas. El sombreador de fragmentos recibirá (a, b, c) y puede mirar para ver qué valor es el más grande; eso nos dirá cuál de los tres hexágonos se debe dibujar en este punto.

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; }

Eso es para líneas rectas.

Si desea bordes ruidosos, puede agregar ruido a las coordenadas barcéntricas:

hexágono con bordes ruidosos

Al jugar con la amplitud de onda / frecuencia de ruido, puede obtener algunos efectos geniales:

hexágono con bordes aún más ruidosos

Debe tener cuidado con el ruido, asegurándose de que sea consistente a través de los límites de los triángulos. Una forma de hacerlo es pasar una identificación hexadecimal y usarla como valor inicial para cada uno de los tres valores de ruido agregados a las coordenadas barcéntricas.

Hice una demostración interactiva aquí . (Para la demostración, no implementé la identificación hexadecimal o algunas de las otras cosas que podría necesitar si estuviera haciendo que esto funcione en un proyecto real, es solo una demostración rápida y sucia)

amitp
fuente
Ahora que es un material de respuesta de primera calidad. Punta de sombrero
Quentin
¡Gran respuesta! Corrección sutil: los polígonos regulares, incluidos los hexágonos, son auto-duales. Sin embargo, las teselaciones de triángulos y hexágonos son dobles entre sí, como lo ilustra su respuesta.
Erik Foss el
0

Estoy seguro de que "podría" resolverse con algún algoritmo de imagen, pero si fuera yo, probablemente lo resolvería con texturas. Hacía texturas hexagonales, las ponía todas en un atlas de texturas, luego, para cada hexágono, miraba a sus vecinos y decidía qué textura aplicar.

Las texturas necesitarían tener versiones para cada tipo de terreno más versiones para cada tipo de transición.

Esto es similar a la cantidad de sistemas basados ​​en mosaicos que hacen el terreno. Aquí hay un ejemplo de juegos 2D .

Otra posibilidad sería simplemente tener sus texturas para sus diversos tipos de terreno (agua, nieve, tierra, hierba) y agregar cantidades de mezcla a cada vértice del hexágono para decidir cómo mezclarlos.

Este artículo muestra la idea de mezclar texturas de terreno. No sugiero seguir su implementación, pero muestra la idea.

gman
fuente
Bueno, en mi caso no es simple preparar texturas, ya que los hexágonos no son uniformes, la forma y el tamaño difieren en todo el mapa. Desafortunadamente, ese es el precio que tengo que pagar por tener un planeta, no solo un mapa plano. También el artículo se ve muy interesante, gracias. Aunque en mi caso estoy renderizando el terreno desde arriba, probablemente desde una altitud en la que, por ejemplo, no se puede ver un solo árbol y todo el bosque parece una sola masa.
user1617735
0

Primero, convierte tus biomas en una textura. Mapear los triángulos a texcoords. Puede hacer esto usando una proyección de mercator o, mejor, un mapa de cubos . Ahora, en el fragment shader, haz algo como esto:

// 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));
}

donde noisehay alguna función pseudoaleatoria (usando, por ejemplo, sinusoides) en la posición 3D del vértice en el espacio modelo que devuelve un desplazamiento ruidoso a la coordenada de textura. Pruebe la textura usando GL_NEARESTpara mantener bordes afilados.

mklingen
fuente