O que exatamente faz com que uma superfície se sobreponha a outra?

10

Eu realmente não consigo descobrir o que faz uma superfície se sobrepor a outra. Em um mecanismo 3D que estou criando, minha técnica está falhando em casos extremos.

Meu método é classificar as superfícies a serem pintadas da mais distante para a mais próxima. Para determinar a proximidade, estou comparando os valores médios de z. Às vezes, no entanto, uma superfície sobreposta tem um valor z médio mais alto do que aquele sobreposto. Assim, a superfície mais distante é pintada sobre a mais próxima - resultando em renderizações bizarras como esta:

Quadrado roxo grande com uma seção de vermelho no lado

O que se pretende ver é apenas a superfície frontal roxa do cubo, enquanto a superfície lateral vermelha é pintada sobre a roxa. O valor médio de z da superfície roxa é mais alto e, portanto, 'mais distante'. Então, eu estou tendo alguma dúvida se esta técnica está correta.

O que eu também tentei é distanciar a câmera (ou seja, a origem) da superfície, mas então precisava de um ponto. Eu escolhi o meio de cada superfície, mas isso nem sempre parece funcionar porque nem todas as superfícies são tão grandes quanto as outras.

Portanto, qual é uma maneira confiável de determinar a ordem de proximidade das superfícies em relação à origem?

pimvdb
fonte

Respostas:

11

Você parece estar tentando implementar o algoritmo do Painters . Suponho que você esteja tentando escrever um rasterizador do zero como um exercício de aprendizado, pois a maioria dos hardwares 3D modernos usa o que Bart mencionou (o buffer Z / Depth). Para que o algoritmo dos pintores funcione em todos os casos, você precisa estar preparado para subdividir as superfícies conforme elas são renderizadas para resolver possíveis cenários (como o problema de polígono sobreposto mostrado na página da Wikipedia).

Ao renderizar do mais longe para o mais próximo, você também gasta tempo renderizando pixels que possivelmente serão ocluídos por outros polígonos, que quando você começa a colocar texturas e shaders complexos nos polígonos desperdiçam ciclos preciosos. Essa é a razão pela qual o hardware moderno prefere renderizar da frente para trás, usando o buffer de profundidade para determinar se o pixel a ser renderizado está mais distante do que o da tela (e, portanto, pode ser descartado).

Mesmo com o hardware de aceleração mais moderno, você ainda precisará classificar e renderizar de trás para frente quaisquer polígonos semitransparentes, renderizando isso apenas quando todos os polígonos opacos tiverem sido renderizados.

Roger Perkins
fonte
Você está certo ao dizer que estou criando um renderizador simples. O ponto é que não estou usando nenhuma biblioteca 3D. Estou fazendo todos os cálculos de projeção e desenhando as linhas usando uma biblioteca de desenhos 2D. Este não é o mais rápido lá fora, e eu não estou fazendo nada por pixel. Eu só tenho 4 pontos de cada superfície e preencha a forma retangular com uma cor. Como estou desenhando uma superfície completa de uma só vez, estou correto ao dizer que preciso recuar para a frente?
Pimvdb
Parece muito divertido e uma ótima maneira de aprender. Voltar para a frente é o seu único caminho, se você estiver fazendo isso sem qualquer forma de buffer de profundidade. Acho que você terá que elaborar todas as coordenadas de tela dos seus polígonos e depois verificar se há sobreposição. Onde eles se sobrepõem em profundidade, você potencialmente precisará cortar os polígonos para renderizar, para poder resolver a classificação corretamente. Você também pode querer olhar em volta da cara Culling como uma maneira de remover polígonos que não deve ser necessariamente visível
Roger Perkins
Roger Perkins está certo. Você não poderá obter a classificação z correta dessa maneira (o que significa que sempre terá alguns casos extremos), a menos que você corte os polígonos. Isso pode se tornar bastante lento.
bummzack
Eu consegui! O que fiz foi uma combinação de seleção e classificação da face posterior: em um cubo sólido, algumas faces não são visíveis enquanto outras são (máximo de 3). Então pintei as partes não visíveis primeiro e as partes visíveis sobre elas. Isso funciona como um encanto, se eu remover um ou mais lados, eles estarão sobrepostos, mas ainda serão desenhados na ordem correta. Obrigado!
Pimvdb
1
Isso só funciona para formas convexas simples, como cubos. Estou feliz que esteja funcionando atualmente para você, mas não é uma solução geral.
Richard Fabian
3

O que você tem é um problema de visibilidade . Uma solução está usando um buffer z .

Bart van Heukelom
fonte
Obrigado, mas o z-buffering requer renderização por pixel, se não me engano. Não estou fazendo isso - estou desenhando linhas retas entre os pontos projetados do cubo. O buffer z ainda é possível?
Pimvdb
Mesmo se você estiver desenhando linhas retas, em algum momento antes da imagem chegar à tela (ou arquivo de bitmap), ela estará no formato de pixel.
Bart van Heukelom 12/04
Está correto, mas minha biblioteca de desenhos é muito lenta para renderizar pixels com 20fps. Obrigado embora.
Pimvdb
2

Classificar as faces de acordo com o valor z médio não funciona, porque o valor z médio não fornece informações sobre o valor z real dos vértices ou mesmo dos pixels da superfície.

Exemplo (aviso, arte ASCII adiante):

          /
         /
cam     /
-->    / 
      /
     /--       <--B
    /
    ^--A

Existem duas faces A e B. Na perspectiva da câmera, A está na frente de B. No entanto, o valor z médio de B é menor. Vejamos os outros valores z:

  • o valor z mínimo de A é 4
  • o valor z máximo de A é 11
  • portanto, o valor z médio de A é 7,5
  • o valor z mínimo de B é 6
  • o valor z máximo de B é 8
  • portanto, o valor z médio de B é 7

Você pode tentar classificá-los pelos valores z mínimos, mas há casos em que isso também não funciona. Não há como classificar faces arbitrárias corretamente usando apenas um valor z.

No algoritmo de Newell http://en.wikipedia.org/wiki/Newell 's_algorithm, o que você faz é classificar os intervalos mínimo / máximo dos valores-z. Se os intervalos de duas faces não se sobrepuserem, você pode saber com certeza qual deles está na frente. Se o fizerem, às vezes é absolutamente necessário dividir os rostos. Às vezes, basta rastrear todos os vértices para oclusão ou alguma outra técnica.

Jonas Bötel
fonte
Esse diagrama é uma visão geral do que eu acho que está acontecendo em sua captura de tela.
jhocking
1

É bom que você esteja aprendendo sobre renderização fazendo. Parabéns. Nesse caso, não existe uma solução de "algoritmo de pintores", em vez de tentar corrigi-lo por classificação, no PS1, costumávamos tentar manter os polígonos do mesmo tamanho quando estavam próximos um do outro (o que você está fazendo como até onde eu sei) e abate da face posterior (o que você não está fazendo)

A seleção da face posterior está verificando a superfície normal no espaço da tela em relação à sua direção (basta obter o sinal do elemento de profundidade no espaço da tela transformado normal (no nosso caso, era o z do normal) ou o produto cruzado de dois vetores do triângulo, ou seja, cruz (v1-v0, v2-v0))

Se você implementa a seleção de backface, também reduz a quantidade de rasterização que está fazendo .. dupla vitória.

Richard Fabian
fonte
Obrigado pela sua resposta. Eu li sobre a seleção de backface, mas o ponto é que eu gostaria de poder remover um lado. Com um cubo sólido, isso pode funcionar muito bem, mas se eu remover um lado, ocorrerá ocultação - então ainda preciso classificá-los, acho. De qualquer forma, tive a idéia de disparar um raio da origem através do ponto médio de uma superfície. Se esse raio cruzar outra superfície posteriormente, a primeira superfície se sobrepõe à segunda. Você poderia me dizer se essa ideia está correta? Obrigado!
Pimvdb
Embora sua ideia funcione para o seu exemplo, existem muitos outros casos em que não vai funcionar. Por exemplo, a mesma cena que você tem lá, mas com a câmera inclinada para que o centro do quadrilátero de renderização incorreto não se sobreponha. Acho que você precisará separar suas primitivas em pedaços menores.
Richard Fabian