¿Cómo puedo reparar los artefactos de mapeo UV en zigzag en una malla generada que se estrecha?

10

Estoy creando una malla de procedimiento basada en una curva y el tamaño de la malla se hace más pequeño a lo largo de la curva, como puede ver a continuación.

ingrese la descripción de la imagen aquí

Y el problema es que el UV se zigzaguea a medida que cambia el tamaño (funciona perfectamente cuando el tamaño de la malla es el mismo en toda la curva).

ingrese la descripción de la imagen aquí

 Vertices.Add(R);
 Vertices.Add(L);
 UVs.Add(new Vector2(0f, (float)id / count));
 UVs.Add(new Vector2(1f, (float)id / count));
 start = Vertices.Count - 4;
          Triangles.Add(start + 0);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 1);
                 Triangles.Add(start + 2);
                 Triangles.Add(start + 3);

 mesh.vertices = Vertices.ToArray();
         //mesh.normals = normales;
         mesh.uv = UVs.ToArray();
         mesh.triangles = Triangles.ToArray();

¿Cómo puedo arreglar esto?

fkkcloud
fuente
¿Qué son idy count? ¿Qué UVs.ToArray()hacer? ¿Cómo carga los vértices y las coordenadas de textura en la tarjeta?
user1118321
@ user1118321 del contexto, ides el índice del segmento actual de todas las barras transversales a lo largo de la tira, de donde hay counten total. List<T>.ToArray()devuelve una matriz escrita de todas las entradas en la lista (así que aquí, a Vector2[]) Estos almacenamientos intermedios de datos se ponen a disposición del sistema de representación asignándolos a la Meshinstancia meshen las últimas líneas. Pero nada de esto es la parte particularmente interesante del código: cómo se eligen los puntos de vértice. Esos detalles serían más útiles para ver. ;)
DMGregory
Esa fue mi suposición, pero pedí una aclaración porque a veces las suposiciones están equivocadas. He visto algo así en mi propio código en el pasado solo para darme cuenta de que en countrealidad significaba vértices en lugar de polígonos, o viceversa. Solo asegurarme de que todo lo que creemos que está sucediendo realmente está sucediendo.
user1118321

Respuestas:

13

El mapeo UV típico es lo que se llama una transformación afín . Eso significa que el mapeo de cada triángulo entre el espacio 3D y el espacio de textura puede incluir rotación, traslación, escala / aplastamiento y sesgo (es decir, cualquier cosa que podamos hacer con una multiplicación de matriz homogénea)

Lo que pasa con las transformaciones afines es que son uniformes en todo su dominio: la rotación, la traslación, la escala y la inclinación que aplicamos a la textura cerca del vértice A es la misma que aplicamos cerca del vértice B, dentro de cualquier triángulo. Las líneas que son paralelas en un espacio se asignarán a líneas paralelas en el otro, nunca convergentes / divergentes.

Pero la reducción gradual que está tratando de aplicar no es uniforme: está mapeando líneas paralelas en la textura a líneas convergentes en la malla. Eso significa que la escala de la textura medida a través de la banda está cambiando continuamente a medida que avanzamos por la franja. Eso es más de lo que las transformaciones afines del mapeo UV 2D pueden representar con precisión: la interpolación de coordenadas uv 2D entre vértices adyacentes obtendrá una escala consistente a lo largo de todo el borde, incluso el borde diagonal que debería reducirse a medida que avanza por la franja. Esa falta de coincidencia es lo que crea este zigzag chirriante.

Este problema surge cada vez que queremos mapear un rectángulo a un trapecio: lados paralelos a lados convergentes: simplemente no hay transformación afín que haga esto, por lo que tenemos que aproximarlo por partes, lo que lleva a costuras visibles.

Para la mayoría de los propósitos, puede minimizar el efecto agregando más geometría. Aumentar el número de subdivisiones a lo largo de la tira y dividir la tira en dos o más segmentos a lo largo de su ancho, con las diagonales de los triángulos dispuestos en un patrón de espiga, puede hacer que el efecto sea mucho menos notable. Sin embargo, siempre estará presente en cierta medida siempre que estemos usando transformaciones afines.

Pero hay una forma de evitarlo. Podemos usar el mismo truco que usamos para el renderizado 3D para dibujar trapecios en perspectiva dados los muros y pisos rectangulares: ¡usamos coordenadas proyectivas !

Texturas afines: Ejemplo de texturas afines normales con artefactos en zig-zag

Texturas proyectivas: Ejemplo de texto proyectivo que corrige los artefactos

Para hacer esto, necesitamos agregar una tercera coordenada uv (uvw) y modificar nuestros sombreadores.

Dado un factor de escala en cada punto (digamos, igual a la amplitud de su tira en ese punto), puede construir la coordenada uvw proyectiva 3D a partir de su coordenada uv 2D normal de esta manera:

Vector3 uv3 = ((Vector3)uv2) * scale;
uv3.z = scale;

Para aplicar estas coordenadas 3D uvw a su malla, necesitará usar Mesh.SetUVs de sobrecarga de Vector3 (int channel, List uvs)

Y asegúrese de cambiar la estructura de entrada de su sombreador para esperar una coordenada de textura 3D (que se muestra aquí usando el sombreador apagado predeterminado):

struct appdata
{
    float4 vertex : POSITION;
    float3 uv : TEXCOORD0; // Change float2 to float 3.
};
// Also do this for the uv sent from the vertex shader to the fragment shader.

También deberá cortar la macro TRANSFORM_TEX en el sombreador de vértices, ya que espera un uv 2D:

// o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv = v.uv;
// If you define a float4 with the special name _MainTex_ST, 
// you can get the same effect the macro had by doing this:
o.uv.xy = o.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

Finalmente, para volver a una coordenada de textura 2D para el muestreo de textura, simplemente divida por la tercera coordenada en su sombreador de fragmentos :

float2 uv = i.uv.xy / i.uv.z;

Como hicimos esta coordenada uvw 3D a partir de nuestra coordenada 2D deseada multiplicando por el mismo número, las dos operaciones se cancelan y volvemos a nuestra coordenada 2D original deseada, pero ahora con interpolación no lineal entre los vértices. :RE

Es importante hacer esta división por fragmento y no en el sombreador de vértices. Si se hace por vértice, entonces volvemos a interpolar las coordenadas resultantes linealmente a lo largo de cada borde, ¡y hemos perdido la no linealidad que estábamos tratando de introducir con la coordenada proyectiva!

DMGregory
fuente
Hola, he estado luchando con el mismo problema desde hace bastante tiempo, y esto parece exactamente lo que me está sucediendo. La única diferencia es que estoy trabajando en threejs y no en la unidad. Logramos resolver el problema aumentando el número de triángulos, pero todavía nos gustaría probar texturas proyectivas. ¿Tienes alguna idea de cómo comenzar a implementar esto en threejs? ¡Gracias!
Dživo Jelić
1
Más o menos de la misma manera. Necesita una tercera coordenada de textura para almacenar el divisor, luego realiza la división en el sombreador de fragmentos. Si ha tenido problemas para aplicar esto a su caso, hacer una nueva pregunta le permitirá incluir una muestra de código, que muestre cómo está dibujando su malla hasta ahora, sobre la que se pueden construir respuestas.
DMGregory
¿Necesito almacenar el divisor como una tercera coordenada (y debido a esa estructura de cambio y cortar la macro TRANSFORM_TEX, que no sé cómo hacer en threejs) o puedo pasar el divisor como un atributo al sombreador de vértices y luego a partir de allí como variaciones para fragmentar el sombreador donde podría usarse para la división?
Dživo Jelić
TRANSFORM_TEX es una macro de Unity. No tienes eso en three.js, así que no hay nada que recortar. Mientras las coordenadas y divisores UV interpolados lleguen al sombreador de fragmentos, en realidad no importa cómo los hayas logrado allí. ¿Has tenido problemas para proporcionarlo como un atributo / variable hasta ahora?
DMGregory
Todavía no lo intenté, ya que no quería perder el tiempo hasta que verifique que es posible. Solo una última pregunta, el divisor también se interpolará una vez que llegue al sombreador de fragmentos, ¿correcto? ¿Significa que en píxeles entre dos vértices será un valor interpolado y no un valor original? Me resulta difícil entender por qué multiplicar la radiación UV y luego dividirla ayuda, pero aceptaré tu palabra y lo intentaré. : slightly_smiling_face: ¡Muchas gracias por tu ayuda!
Dživo Jelić