melhor maneira de calcular valores normais de vértices na lista de um triângulo

8

oi eu sou um novato completo em computação gráfica, desculpe se é uma resposta estúpida. Estou tentando criar um mecanismo 3D simples a partir do zero, mais para fins educacionais do que para uso real.

por enquanto eu computo apenas face normais. nesse caminho:

Eu tenho um objeto de superfície com dentro de uma lista de triângulo. eu computo normais dentro da classe Triangle, desta maneira:

triangle.computeFaceNormals() {
    Vec3D u = v1.sub(v3)
    Vec3D v = v1.sub(v2)
    Vec3D normal = Vec3D.cross(u,v)
    normal.normalized()
    this.n1 = this.n2 = this.n3 = normal
}

e ao construir a superfície:

t = new Triangle(v1,v2,v3)
t.computeFaceNormals()
surface.addTriangle(t)

e acho que essa é a melhor maneira de fazer isso .. não é?

agora .. isso funciona, ok. mas a luz não está suavizada. Eu estou tentando calcular também normais de vértice. (estou testando meu motor com superfícies tubolares, para que eu tenha quase todo o vértice compartilhado com mais de um triângulo)

Eu encontrei este algoritmo simples: flipcode vértice normal, mas .. hei este algoritmo tem .. complexidade exponencial? (se minha memória não falhar no meu conhecimento em ciência da computação ..) (por outro lado .. ele possui 3 loops aninhados .. não acho que seja a melhor maneira de fazê-lo ..)

alguma sugestão?

nkint
fonte
Que língua é essa? Parece-me que té o resultado de computeFaceNormals(que não retorna nada), não um triângulo.
é pseudocódigo :) você está certo no código real, eu também "retorne isso", desculpe, eu editei!
Nkint

Respostas:

6

"Melhor" é bastante subjetivo - envolverá pesar o que você precisa do algoritmo, em comparação com entradas, complexidade de tempo de execução e outras propriedades.

Dito isto, tanto a sua abordagem quanto a abordagem do FlipCode vinculado são razoáveis ​​em termos de produção de resultados utilizáveis ​​(você pode simplesmente copiar suas 'normais de rosto' para cada vértice, se não estiver compartilhando instâncias reais de vértices entre triângulos, o que não estou claro). do seu código). Outras técnicas incluem ponderar a contribuição de cada face normal pelo tamanho do ângulo feito com cada face compartilhada pelo vértice.

Você está certo, pois a abordagem FlipCode parece subótima como está escrita, mas pode ser mal especificada: é um pouco incerto se pretende ou não sugerir que o segundo loop atravesse todas as faces do modelo, em comparação com as poucas faces que compartilham o mesmo. vértice em questão. Se você tiver as informações de adjacência para reduzir o espaço de pesquisa do segundo loop, isso se tornará menos preocupante. É claro que você pode não ter essas informações de adjacência - isso é o que quero dizer com considerar quais entradas você tem disponível para o seu algoritmo.

Se você não deseja apenas copiar a face normal para os vértices, ou está compartilhando vértices e não deseja dividi-los, pode:

foreach vertex:
   set vertex normal to (0,0,0)

foreach triangle:
   foreach vertex on that triangle:
      set vertex normal = normalize( vertex normal + face normal )

Isso pressupõe que cada triângulo está realmente referenciando cada vértice em vez de armazenar uma cópia dele - não sei qual idioma você está usando, então não sei se é esse o caso ou não.


fonte
desculpe inglês não é minha primeira língua então talvez eu expliquei de uma maneira ruim o que eu quero .. eu ainda não tenho normais de vértice! Eu editei a minha resposta um pouco, espero que agora eu mais clara
nkint
Tudo bem, eu entendi que você não tinha vértices normais. O pseudocódigo que eu forneci os calculará para você, primeiro definindo o normal de cada vértice para (0,0,0) e, em seguida, resumindo as normais de face para cada face em que o vértice toca (e re-normalizando, é claro).
5
Você não deve normalizar os normais dessa maneira. Você deve normalizar após todas as somas em outro foreach. Se houver, por exemplo, 10 triângulos com o mesmo normal, mas o último for com diferente (o resultado normal deve ser quase igual ao normal de 10 triângulos), você terá isso ao somar o último: set vertex nromal = normalize (( 1,0,0) + (0,0,1)) e isso é (0,5,0,0,5), o que está errado. Espero não ter confundido você.
Zacharmarz
7

Josh Petrie tem razão. Se você deseja calcular valores normais do vértice, deve ponderar a contribuição do triângulo por ângulo. Mas você pode usar uma solução ingênua e apenas somar todas as normais de todas as faces ao redor do vértice.

Então você apenas calcula todos os normais normais e os normaliza. Em seguida, defina todas as normais do vértice para zero. Em seguida, para cada face (triângulo), adicione seu normal a todos os seus vértices. Em seguida, normalize todas as normais do vértice. E está feito.

Não é muito correto, mas pode ser o suficiente.

E o seu cálculo normal do rosto acima - está correto, mas você deve saber de que lado as cabeças normais. Pode subir ou descer. Depende do produto cruzado - AxB não é o mesmo que BxA - fornece o vetor oposto.

zacharmarz
fonte
Bom ponto em relação ao produto cruzado.
2

Vamos supor que temos um cubo feito de 6 retângulos. Já sabemos que o vértice normal de um cubo não é a soma normalizada das faces da conexão devido à aresta aguda. Se você criar um mapa do valor do vértice e os diferentes vértices normais para um cubo, você terá 3 normais para cada vértice.

Mantendo o ponto acima em mente, é assim que eu calculo os vértices normais (eu testei e estou usando).

class Face{
    uint vert_indices[3];
    vec3 surface_normal;
    vec3 vertex_normal[3];
}

Cada face controla os valores normais do vértice a serem usados, pois duas faces podem compartilhar um vértice, não significa que o vértice normal será o mesmo para cada face.

Vamos supor que estamos tentando calcular o vértice normal para vert ao renderizar face2 .

vert é compartilhada por face0 , face1 , face2 , face3 e Face4 . Todas as faces acima são vizinhas em ordem e face0 e face4 se conectam para formar um loop.

O vértice normal para vert é a soma de toda a cadeia vizinha conectada à face2 normalizada. A cadeia para se o ângulo entre duas faces vizinhas for superior a 0,8 radianos (ângulo == arccos (crossProduct (faceA.surface_normal, faceB.surface_normal))).

Se o ângulo entre face0 e face1 é superior a 0,8 radiano e o ângulo entre face3 e Face4 é superior a 0,8 radiano do que o vértice normal para vert quando render face2 é normalizar ( surfnormal1 + surfnormal2 + surfnormal3 ).

Fredrique Samuels
fonte