OpenGL - Detecção de arestas

12

Eu gostaria de carregar malhas arbitrárias e desenhar linhas pretas grossas ao longo das bordas para obter uma aparência de sombreamento. Consegui desenhar uma silhueta preta ao redor dos objetos usando o buffer de estêncil. Você pode ver o resultado aqui:

insira a descrição da imagem aqui

Mas o que falta são as linhas pretas no próprio objeto. Pensei em verificar descontinuidades normais: Verificar se um pixel vizinho tem um vetor normal diferente do atual. Se sim, uma borda foi encontrada. Infelizmente, não tenho idéia de como implementar essa abordagem, nem no OpenGL nem no GLSL vertex / fragment shader.

Ficaria muito feliz por alguma ajuda com relação a essa abordagem ou qualquer outra sobre detecção de borda.

Edit: Eu não uso nenhuma textura para minhas malhas.

Para ser mais preciso, eu gostaria de criar uma solução CAD / CAM com a maior aparência possível (retirada do Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4 ):

insira a descrição da imagem aqui

enne87
fonte
Eu acredito que você precisa definir a "borda" mais detalhadamente. Como isso difere de uma estrutura de arame simples? Na resposta cifz, há uma boa descrição do pós-processo do espaço na tela, mas a partir da sua pergunta é difícil determinar se é aplicável.
Andreas
Bem, com vantagem quero dizer os "vincos" e "sulcos" que formam os sólidos. Um wireframe exibirá todas as faces do triângulo, o que não é o que eu quero.
Enne87
Ok, exatamente o que eu pedi :-) Eu geraria uma estrutura de arame a partir desses vincos e sulcos. A parte complicada ainda é determinar o que é um vinco / cume. Você tem alguma idéia de como fazer isso?
Andreas
1
Os programas cad não fazem isso com um shader principalmente. Em vez disso, eles conhecem as arestas rígidas do modelo e desenham uma linha no topo da malha para formar essa informação.
Joojaa 20/05
joojaa você tem mais alguma informação sobre essa técnica? O que é feito no caso de superfícies curvas livres de forma dupla? Também não tenho certeza do que acontece quando você tem um cone ou um cilindro que está sendo cortado / aparado por algo de forma livre. stackoverflow.com/questions/43795262/…
Dusan Bosnjak 'pailhead'

Respostas:

18

Geralmente, a detecção de borda se resume a detectar áreas da imagem com alto valor de gradiente.

No nosso caso, podemos ver grosseiramente o gradiente como a derivada da função de imagem; portanto, a magnitude do gradiente fornece informações sobre o quanto sua imagem muda localmente (em relação aos pixels / texels vizinhos).
Agora, uma vantagem é, como você diz, uma indicação de descontinuidade; portanto, agora que definimos o gradiente, fica claro que essas informações são tudo o que precisamos. Depois de encontrarmos o gradiente de uma imagem, basta aplicar um limite a ela para obter um valor binário edge / non-edge.

Como você acha que esse gradiente é realmente o que você está perguntando e ainda estou para responder :)

Muitas maneiras! Aqui estão algumas :)

Funções de sombreador incorporadas

Tanto hlsl quanto glsl oferecem funções derivadas. No GLSL, você tem dFdx e dFdy que fornecem, respectivamente, informações de gradiente nas direções x e y. Normalmente essas funções são avaliadas em um bloco de fragmentos 2x2.
A menos que você esteja interessado em uma única direção, uma boa maneira de obter um resultado compacto que indique quão forte é o gradiente na região é uma largura que não fornece mais nada além da soma do valor absoluto de dFdy e dFdy.
É provável que você esteja interessado em uma borda na imagem geral em vez de em um canal específico; portanto, convém transformar sua função de imagem em luma. Com isso em mente, quando se trata de detecção de borda, seu shader pode incluir algo parecido com:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

Com um limite alto, você encontrará arestas mais grossas e poderá perder algumas, por outro lado, com um limite baixo, poderá detectar arestas falsas. Você precisa experimentar para encontrar o limite que melhor se adapte às suas necessidades.

Vale a pena mencionar a razão pela qual essas funções funcionam, mas não tenho tempo para isso agora, provavelmente atualizarei esta resposta mais tarde :)

Pós-processo do espaço na tela

Você pode ficar mais chique do que isso, agora o campo de detecção de Borda no processamento de imagens é imenso. Eu poderia citar dezenas de boas maneiras de detectar a detecção de bordas de acordo com suas necessidades, mas vamos simplificar por enquanto, se você estiver interessado, posso citar mais opções!

Portanto, a idéia seria semelhante à acima, com a diferença de que você poderia olhar para uma vizinhança mais ampla e usar um conjunto de pesos em amostras envolventes, se desejar. Normalmente, você executa uma convolução sobre sua imagem com um kernel que fornece como resultado uma boa informação de gradiente.
Uma escolha muito comum é o kernel Sobel

                                   insira a descrição da imagem aqui

Que, respectivamente, fornecem gradientes nas direções x e y:

                                  De um pdf antigo, escrevi há muito tempo.

GrumadEuentMumagnEutvocêde=(GrumadEuentx)2+(GrumadEuenty)2

Então você pode limiar da mesma maneira que mencionei acima.

Esse kernel, como você pode ver, dá mais peso ao pixel central, e efetivamente calcula o gradiente + um pouco de suavização que tradicionalmente ajuda (geralmente a imagem é gaussiana borrada para eliminar pequenas arestas).

O exemplo acima funciona muito bem, mas se você não gosta da suavização, pode usar os kernels Prewitt:

                                                   insira a descrição da imagem aqui

(Observe que estou com pressa, em breve escreverei o texto formatado adequado em vez de imagens!)

Realmente, existem muito mais kernels e técnicas para encontrar a detecção de bordas no processo de imagem, em vez de gráficos em tempo real, então excluí métodos mais complicados (trocadilhos não pretendidos), pois provavelmente você ficaria bem com as funções dFdx / y .

cifz
fonte
Muito boa explicação cifz, mas e se nenhum gradiente for visível em uma determinada situação? Por exemplo, não há fonte de luz na parte traseira do cubo e, portanto, nenhum gradiente é visível. Então esse processo de detecção de borda baseado em imagem que você descreve não funcionaria, estou certo?
Enne87
Se você usar um renderizador diferido, poderá fazer o mesmo, mas no buffer normal ou novamente, se tiver um pré-passe, poderá aplicar o algoritmo à profundidade. Ainda assim, você tem razão, uma abordagem de espaço na tela pode não ser ideal em todos os casos :) Qual solução é viável depende muito de qual é o seu orçamento para esse efeito, quão complexa é a sua cena.
Cifz
Potencialmente, se isso é realmente central para o seu jogo, uma maneira muito fácil, mas potencialmente tributária, seria calcular um gradiente nos valores normais vizinhos para cada vértice (offline no tempo de carregamento) e passar esse fator como atributo adicional de vértice.
Cifz 19/05/19
Thangs novamente cifz por sua ajuda. Bem, o que eu quero alcançar é desenvolver uma solução CAD / CAM parecida com esta: youtube.com/watch?v=-qTJZtYUDB4 E eu realmente gostaria de saber como eles conseguiram renderizar todas as bordas pretas, vincos e cumes. cifz, você acha que, como especialização em programação gráfica, eles conseguiram esse visual usando uma de suas abordagens de espaço na tela? Ou talvez calculando um gradiente nos normais vizinhos? Eu realmente quero saber como isso pode ser feito. Obrigado novamente!
31516 enne87
1
Não precisa pagar ninguém, apenas comece a ler alguns tutoriais on-line, compre alguns livros e estude bastante :) No final, será muito gratificante!
Cifz 20/05
3

Caso alguém também precise detectar arestas: Aqui está um bom artigo sobre como exibir uma estrutura de arame e este artigo explica como mostrar apenas as arestas.

enne87
fonte