Como posso corrigir artefatos de mapeamento UV em zigue-zague em uma malha gerada que se afunila?

10

Estou criando uma malha processual baseada em uma curva e o tamanho da malha fica menor ao longo da curva, como você vê abaixo.

insira a descrição da imagem aqui

E o problema é que a UV fica em zigue-zague quando o tamanho muda (funciona perfeitamente quando o tamanho da malha é o mesmo em toda a curva).

insira a descrição da imagem aqui

 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();

Como posso consertar isso?

fkkcloud
fonte
O que são ide count? O que UVs.ToArray()faz? Como você está carregando os vértices e as coordenadas de textura no cartão?
user1118321
@ user1118321 do contexto, idé o índice do segmento atual de todas as barras transversais ao longo da faixa, de onde existem countno total. List<T>.ToArray()retorna uma matriz digitada de todas as entradas na lista (então aqui, a Vector2[]). Esses buffers de dados são disponibilizados para o sistema de renderização, atribuindo-os à Meshinstância meshnas últimas linhas. Mas nada disso é a parte particularmente interessante do código: como os pontos de vértice são escolhidos. Esses detalhes seriam mais úteis para ver. ;)
DMGregory
Essa foi minha suposição, mas pedi esclarecimentos, porque algumas vezes as suposições estão erradas. Eu olhei para algo assim no meu próprio código no passado apenas para perceber que countrealmente significava vértices em vez de polígonos, ou vice-versa. Apenas garantir que tudo o que pensamos esteja acontecendo realmente está acontecendo.
user1118321

Respostas:

13

O mapeamento UV típico é chamado de transformação afim . Isso significa que o mapeamento de cada triângulo entre o espaço 3D e o espaço da textura pode incluir rotação, tradução, redimensionamento / squash e inclinação (ou seja, qualquer coisa que possamos fazer com uma multiplicação de matriz homogênea)

A questão das transformações afins é que elas são uniformes em todo o domínio - a rotação, a translação, a escala e a inclinação que aplicamos à textura perto do vértice A é a mesma que aplicamos perto do vértice B, dentro de qualquer triângulo. Linhas paralelas em um espaço serão mapeadas para linhas paralelas no outro, nunca convergindo / divergindo.

Mas o afilamento gradual que você está tentando aplicar não é uniforme - ele está mapeando linhas paralelas na textura para linhas convergentes na malha. Isso significa que a escala da textura medida na banda está mudando continuamente à medida que avançamos na faixa. Isso é mais do que as transformações afins do mapeamento UV 2D podem representar com precisão: a interpolação de coordenadas uv 2D entre vértices adjacentes obterá uma escala consistente ao longo de toda a borda, até a borda diagonal que deve estar diminuindo de escala à medida que se move pela faixa. Essa incompatibilidade é o que cria esse ziguezague de guerra.

Esse problema surge a qualquer momento que queremos mapear um retângulo para um trapézio - lados paralelos para lados convergentes: simplesmente não há transformação afim que faz isso, por isso temos que aproximá-lo por partes, levando a costuras visíveis.

Para a maioria dos propósitos, você pode minimizar o efeito adicionando mais geometria. Aumentar o número de subdivisões ao longo do comprimento da faixa e dividi-la em dois ou mais segmentos ao longo de sua largura, com as diagonais dos triângulos dispostos em um padrão de espinha de peixe, pode tornar o efeito muito menos perceptível. Ele sempre estará presente até certo ponto, enquanto estivermos usando transformações afins.

Mas há uma maneira de contornar isso. Podemos usar o mesmo truque que usamos na renderização 3D para desenhar trapézios em perspectiva, dadas paredes e pisos retangulares: usamos coordenadas projetivas !

Texturas afins: Exemplo de texturas afins normais com artefatos em zig-zag

Texturização projetiva: Exemplo de textring projetivo corrigindo os artefatos

Para fazer isso, precisamos adicionar uma terceira coordenada uv (uvw) e modificar nossos shaders.

Dado um fator de escala em cada ponto (digamos, igual à largura da sua faixa nesse ponto), você pode construir a coordenada uvw projetiva em 3D a partir da coordenada uv 2D 2D normal da seguinte maneira:

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

Para aplicar essas coordenadas uvw 3D à sua malha, você precisará usar a sobrecarga Vector3 Mesh.SetUVs (int channel, List uvs)

E certifique-se de alterar a estrutura de entrada do seu sombreador para esperar uma coordenada de textura 3D (mostrada aqui usando o sombreador apagado padrão):

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.

Você também precisará recortar a macro TRANSFORM_TEX no sombreador de vértices, pois espera um 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;

Por fim, para retornar a uma coordenada de textura 2D para amostragem de textura, basta dividir pela terceira coordenada no seu shader de fragmento :

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

Como fizemos essa coordenada uvw 3D a partir da coordenada 2D desejada multiplicando pelo mesmo número, as duas operações cancelam e voltamos à coordenada 2D desejada original, mas agora com interpolação não linear entre os vértices. : D

É importante fazer essa divisão por fragmento e não no shader de vértice. Se for feito por vértice, voltaremos a interpolar linearmente as coordenadas resultantes ao longo de cada aresta e perdemos a não linearidade que estávamos tentando introduzir com a coordenada projetiva!

DMGregory
fonte
Oi, eu tenho lutado com o mesmo problema há algum tempo, e isso parece exatamente o que está acontecendo comigo. A única diferença é que estou trabalhando em threejs e não em unidade. Conseguimos resolver o problema aumentando o número de triângulos, mas ainda assim gostaríamos de tentar texturas projetivas. Você tem alguma idéia de como começar a implementar isso no threejs? Obrigado!
Dživo Jelić 12/12
11
Praticamente da mesma maneira. Você precisa de uma terceira coordenada de textura para armazenar o divisor e, em seguida, realiza a divisão no sombreador de fragmentos. Se você teve problemas para aplicar isso ao seu caso, fazer uma nova pergunta permitirá que você inclua um exemplo de código, mostrando como você está desenhando sua malha até agora, para que as respostas possam se basear.
DMGregory
Preciso armazenar o divisor como uma terceira coordenada (e por causa dessa alteração da estrutura e cortar a macro TRANSFORM_TEX, que eu não sei fazer em threejs) ou posso apenas passar o divisor como um atributo para o vertex shader e então a partir daí, como variações para fragmentar shader, onde poderia ser usado para a divisão?
Dživo Jelić
TRANSFORM_TEX é uma macro do Unity. Você não tem isso no three.js, então não há nada a cortar. Desde que as coordenadas e divisores uv interpolados atinjam o sombreador de fragmentos, não importa realmente como você os conseguiu lá. Você já teve algum problema em fornecê-lo como um atributo / variável até agora?
DMGregory
Ainda não tentei, pois não queria perder tempo até verificar se é possível. Apenas uma última pergunta, o divisor também será interpolado quando atingir o shader do fragmento, correto? Significando que no pixel entre dois vértices haverá algum valor interpolado e não o valor original? Tenho dificuldade em entender por que multiplicar o UV e depois dividi-lo ajuda, mas vou acreditar na sua palavra e tentar. : ligeiramente_smiling_face: Muito obrigado pela sua ajuda!
Dživo Jelić