Descarte por visibilidade Paulo Ivson 21-10-2008
Temos site! www.tecgraf.puc-rio.br/~psantos Aulas Códigos fonte Exemplos Bibliotecas Material de pesquisa (e.g. papers)
Sumário Motivação Vdlib Descarte contra o volume de visão Descarte por oclusão Resultados de desempenho
Motivação Por que visibilidade?
Motivação P-38 90.871 objetos 4.717.749 vértices
Motivação Diminuir quantidade de dados transferidos Eliminar objetos não-visíveis para o observador Não contribuem para a imagem final Diminuir quantidade de dados transferidos Reduzir processamento no hardware
Vdlib Visibility Determination Library Volumes envolventes http://www.tecgraf.puc-rio.br/~psantos/software/vdlib.7z Volumes envolventes AABB OBB Construção de hierarquia Kd-tree, média das geometrias Descarte por visibilidade Volume de visão Oclusão
Descarte contra o volume de visão
Descarte contra o volume de visão Objetivo Identificar objetos fora do frustum de visão Seriam descartados no HW (teste de clipping) Efetuar teste na CPU Antes de enviar geometria para GPU Qual teste podemos fazer?
Descarte contra o volume de visão Frustum Tronco de pirâmide Pode ser definido pela interseção de 6 planos Near, far, left, right, top, bottom Convenção: planos orientados para dentro Como testar uma geometria? Testar seu volume envolvente! Determinar se este se encontra dentro ou fora do frustum
Descarte contra o volume de visão Se volume envolvente está totalmente fora Descartar geometria Senão, continuar testando outros planos
Como obter planos do frustum? A partir da matriz projection Plano definido no espaço do olho A partir da matriz view * projection Plano definido no espaço global Idéia Obter planos a partir do espaço de clipping Cubo → maís fácil de extrair planos Vide aula de pipeline de geometria Material no site Seção “Frustum Culling”
Teste AABB x Plano p-vertex n-vertex p = (xmin,ymin,zmin) if (normal.x >= 0) p.x = xmax; if (normal.y >=0)) p.y = ymax; if (normal.z >= 0) p.z = zmax; n-vertex n = (xmax,ymax,zmax) if (normal.x >= 0) n.x = xmin; if (normal.y >=0)) n.y = ymin; if (normal.z >= 0) n.z = zmin;
Teste AABB x Plano Se p-vertex está do lado negativo Caixa está totalmente fora Se n-vertex está do lado positivo Caixa está totalmente dentro
Teste OBB x Plano Descrever plano no espaço local da caixa n: normal do plano b: base ortonormal da OBB nb = (bx . n, by . n, bz . n) Usar nb para determinar n-vertex e p-vertex
Teste OBB x Plano (2) Separating Axis No caso da OBB x Plano “Two convex curves will not curve around each other. Thus, if separated there will be a gap into which a plane can be inserted. A separating axis is a line perpendicular to this plane. Objects projected on the axis give non-overlapping intervals if objets do not intersect.” No caso da OBB x Plano O plano de separação é paralelo ao plano sendo testado O eixo de separação é paralelo à normal do plano sendo testado
Teste OBB x Plano (2) int IntersectionBetween( const Plane& plane, const Box& box ) { const float projectedCenter = DistanceBetween( box.center, plane ); const float projectedRadius = abs( plane.normal.dot( box.axis[0] ) * box.extents[0] )+ abs( plane.normal.dot( box.axis[1] ) * box.extents[1] ) + abs( plane.normal.dot( box.axis[2] ) * box.extents[2] ); // Totally inside if( projectedCenter >= projectedRadius ) return +1; // Totally outside if( projectedCenter <= ( -projectedRadius ) ) return -1; // Intersected return 0; }
Otimizações Evitar testar todos os planos contra todos os volumes envolventes De um quadro para o outro, câmera não costuma se mover bruscamente Será que podemos agrupar testes? Idéia: hierarquias!
Coerência temporal Para cada geometria, guardar índice do último plano a descartá-la Testar primeiro este plano Boa chance de descartar novamente geometria
Coerência espacial Dado um conjunto de geometrias Se seu volume envolvente está totalmente fora de um plano, descartar todas as geometrias Se seu volume envolvente está totalmente dentro de um dos planos, não testar volumes menores contra este plano Se totalmente dentro do frustum, não é preciso testar contra nenhum plano Senão, é necessário testar volumes menores
Coerência espacial Construir hierarquia de uma cena Testar volume envolvente do nó pai Guardar máscara de quais planos ainda precisam ser testados Ao visitar nó filho, verificar máscara do nó pai Se indica que está totalmente dentro do frustum → não testar nenhum plano Se indica que está totalmente dentro de um plano → não testar este plano Atualizar máscara para continuar percurso na hierarquia
Outras otimizações possíveis Octantes Identificar qual octante um volume envolvente se encontra Testar somente contra 3 planos Translações e rotações Identificar objetos que permenecerão fora do frustum Projetar translação da câmera ao longo da normal do plano Guardar direção de rotação da câmera
Descarte por oclusão
Descarte por oclusão Cenas complexas → muitos objetos → oclusão
Descarte por oclusão Cells, Portals e PVS Pré-computar visibilidade de regiões no espaço
Descarte por oclusão Shadow Frusta Hierarchical Z-Buffer Hierarchical Occlusion Maps
Descarte por oclusão Descarte coerente hierárquico Mais uma vez Utilizar placa gráfica para determinar oclusão Occlusion queries Explorar coerência temporal Explorar coerência espacial Mais uma vez Volumes envolventes! Hierarquias!
Occlusion Queries Extensão do OpenGL int glCreateQuery( num ) void glBeginQuery( id, OCCLUSION_QUERY ) Draw(); void glEndQuery() void glGetQueryObject( id, GL_NUM_SAMPLES_PASSED, int* ) Retorna número de pixels que seriam gerados Draw() passa pelo pipeline convencional
Occlusion Queries Paradoxo Precisamos desenhar para saber oclusão Mas queremos saber oclusão para saber se vamos desenhar
Occlusion Queries Paradoxo Solução Precisamos desenhar para saber oclusão Mas queremos saber oclusão para saber se vamos desenhar Solução Desenhar volume envolvente!
Occlusion Queries Paradoxo Solução Precisamos desenhar para saber oclusão Mas queremos saber oclusão para saber se vamos desenhar Solução Desenhar volume envolvente! Mas e a imagem final?
Occlusion Queries Paradoxo Solução Precisamos desenhar para saber oclusão Mas queremos saber oclusão para saber se vamos desenhar Solução Desenhar volume envolvente! Mas e a imagem final? “Fingir” que vai desenhar! Desligar escrita no framebuffer: glColorMask( false )
Considerações Se vamos desenhar volumes envolventes Deve ter baixo custo para desenhar Usar caixas: 8 vértices, 12-14 triângulos Não usar esferas ou volumes mais complexos Ajuste do volume é importante! Usar AABB ou OBB? Ajuste ruim → falsos positivos Renderização de frente-para-trás Potenciais oclusores primeiro Fila de prioridade Occlusion query tem overhead? Problema crítico Sincronização CPU e GPU
Sincronização CPU ↔ GPU CPU Stalls e GPU Starvation Ocupar CPU e GPU!
Descarte coerente hierárquico Algoritmo hierárquico Percurso de frente-para-trás Análogo ao descarte contra volume de visão Caso volume seja descartado Descartar subárvore do nó correspondente Continuar em um irmão ou ancestral Desafios Gerenciar envio de occlusion queries Obter somente os resultados já disponíveis Minimizar stalls e starvation
Descarte coerente hierárquico Coerência temporal Assume movimentos suaves do observador Nós previamente visíveis continuam visíveis Não esperar resultados de visibilidade Já renderizar assumindo coerência temporal Adiar classificação de um nó interno Marcar como invisível por default Atualizar de acordo com visibilidade dos filhos Pull-up visibility
Descarte coerente hierárquico Para cada nó visitado Interno previamente visível Visitar sub-árvores Marcar como invisível por default Interno ou Folha previamente invisível Enviar query para volume envolvente Armazenar teste em uma fila Continuar caminhamento em irmão ou ancestral Folha previamente visível Renderizar geometrias enviando uma occlusion query
Descarte coerente hierárquico
Descarte coerente hierárquico Como obter resultados evitando CPU stalls e GPU starvation?
Simples! Enquanto caminha na hierarquia Intercalar Obter resultados conforme tornam-se disponíveis Se resultado for “visível” Pull-up visibility Renderizar geometrias (folha) Visitar sub-árvores (nó interno) Intercalar Visita de um nó da hierarquia Obtenção de resultados já disponíveis
Algoritmo enquanto não terminou caminhamento ou existem queries pendentes enquanto existem queries com resultado disponível, ou se caminhamento já acabou obter resultado da occlusion query do início da fila (mais antiga) se visivel pull-up visibility, renderizar geometrias e adicionar filhos à fila de prioridade se já terminou caminhamento, continue. Senão, obter próximo nó a ser visitado se nó está totalmente fora do volume de visão, continue se nó intercepta o plano near senão se nó era previamente visível se nó é interno adicionar filhos à fila de prioridade senão renderizar geometrias efetuando uma occlusion query efetuar occlusion query para volume envolvente
Possíveis melhorias Caminhamento terminou Renderizar especulativamente E ainda existem occlusion queries pendentes CPU Stalls! Renderizar especulativamente Adiar atualização de visibilidade
Possíveis melhorias Muitas occlusion queries por quadro Mesmo com hierarquia Depende da cena e divisão espacial Estimar visibilidade Nós continuam de fato visíveis no quadro atual? Sim! Propostas Queries somente de tempos em tempos Histórico de visibilidade de cada nó Probabilidade Movimento da câmera Outras
Resultados
Configuração de Testes Windows XP Professional Microsoft Visual Studio 2003 .NET Athlon XP 3800+ (2.4 Ghz) 2 Gb RAM DDR2 800 MHz NVIDIA GeForce 7800 GT 256 Mb (PCI-E)
Cenas de Teste Teapots(n,s) 1.364 vértices 2.781 índices
Cenas de Teste P-38 90.871 objetos 4.717.749 vértices
Testes Volumes envolventes AABB ~ OBB Clássica Melhor custo x benefício OBB Aproximada Mínima
Testes AABB Clássica Aprox. Mín. Melhor AABB Clássica Aprox. Mín.
Testes Fecho (OBB-Tree) Fecho (Centróides) Fecho (Área)
Testes Occlusion Queries tTesteDisp: Apenas um teste de disponibilidade Desprezível tDisp: Tempo para resultado disponível Proporcional à complexidade geométrica tDispObt: Resultado disponível + obtenção Obter resultado é desprezível
Testes Occlusion Queries Tempo para disponibilidade de resultado Linearmente proporcional à complexidade geométrica
Testes Occlusion Queries tRender: Renderização sem teste de visibilidade tUmaQuery: Apenas um teste de visibilidade Sem overhead tVariasQueries: Um teste para cada geometria Quanto mais testes, maior o custo de cada um
Testes Occlusion Queries Limiar em torno de 500 queries por quadro
Performance de Visualização Caminho automático percorrido pelo observador Variando número de objetos visíves Base de comparação Renderização simples, sem otimizações Avaliar Impacto de diferentes hierarquias Algoritmo de descarte contra volume de visão Algoritmo de descarte por oclusão AABB, OBB Clássica e Aproximada Mínima
Teapots(500,30) Pouca complexidade geométrica Muita oclusão “Cenas que já possuem bom desempenho”
Teapots(500,30) Descarte contra volume de visão: 5% Descarte por oclusão: 40% Aprox. Mín não vale a pena, 100x mais lento para construir
Teapots(5000,3) Elevada complexidade geométrica Pouca oclusão “Medir overhead dos testes de visibilidade”
Teapots(5000,3) Descarte contra volume de visão: 8% Descarte por oclusão: 12%
Teapots(5000,20) Elevada complexidade geométrica Muita oclusão “Melhor cenário para descarte por oclusão”
Teapots(5000,20) Descarte contra volume de visão: 6% Descarte por oclusão: 81% (5x fps)
P-38 Elevada complexidade geométrica Muitas geometrias pouco complexas Distribuição irregular no espaço “Caso de uso real”
P-38 Descarte contra volume de visão: 5% Descarte por oclusão: 57%
Conclusões Hierarquia essencial para escalabilidade Descarte contra volume de visão Otimização fundamental, mas não suficiente Descarte por oclusão Traz melhorias mesmo com pouca oclusão Dificilmente se torna overhead Mesmo assim, perda de desempenho é mínima Extremamente eficiente Ganhos de até 6x na média de quadros por segundo Cerca de 2x no caso real de uso Bom-ajuste dos volumes não é essencial AABB é suficiente
Próximas aulas Waldemar estará de volta Mais adiante OpenSceneGraph Traçado de raios