A detecção de colisão é a aplicação mais natural de campos de distância com sinal no desenvolvimento de jogos. O campo que descreve uma forma para renderização também a descreve para consultas de contato: avalie o SDF em qualquer ponto e você obtém duas respostas de uma vez, a que distância o ponto está da superfície e de que lado ele se encontra. Este artigo aborda os três tipos de consulta que motores de física constroem sobre esse contrato (contenção de ponto, profundidade de penetração com direção de separação e sphere casting para detecção de colisão contínua), com o código GLSL, exemplos trabalhados, casos extremos e a comparação de desempenho com abordagens baseadas em malha.
Para a visão geral dos SDFs em todo o motor, veja SDFs no desenvolvimento de jogos . Se precisar revisar a convenção de sinal ou o conceito de gradiente, a visão geral de Campos de Distância com Sinal constrói ambos a partir dos primeiros princípios.
O Contrato de Consulta
Três fatos sobre um SDF transformam cada consulta de colisão abaixo em uma expressão puramente aritmética:
- Sinal: significa dentro, significa fora, significa na superfície.
- Magnitude: é a distância até a superfície mais próxima, exata para SDFs analíticos, aproximada para amostrados.
- Gradiente: aponta para longe da superfície mais próxima, então na fronteira ele é a normal voltada para fora.
Nenhum deles exige percorrer uma árvore ou testar pares de triângulos. O custo de uma consulta é o custo de avaliar a expressão do campo.
Consultas de Ponto em Volume
A pergunta de colisão mais simples é se um ponto está dentro de um volume. Com um SDF, a resposta é uma única verificação de sinal:
bool isInside(vec3 point, SDF volume) {
return evaluateSDF(point, volume) < 0.0;
}
Essa linha única substitui o que seria um teste de ponto em malha exigindo ray casting ou cálculo de winding number. Para um SDF de esfera, a avaliação de distância custa uma subtração de vetores e um cálculo de comprimento. Para uma árvore CSG complexa construída com dezenas de primitivas, o custo escala com a profundidade da árvore, mas continua sendo uma expressão puramente aritmética, sem estrutura de busca com ramificações.
Um exemplo trabalhado torna a aritmética concreta. Considere um SDF de esfera centrado na origem com raio 2:
Teste o ponto . Sua distância ao centro é . O valor do SDF é , então o ponto está dentro, e ele se encontra 0.882 unidades abaixo da superfície. A mesma consulta contra uma malha triangular precisaria encontrar o triângulo mais próximo e calcular o ponto mais próximo sobre ele, uma operação muito mais cara para a mesma resposta.
Profundidade de Penetração e Direção de Separação
Uma resposta binária dentro/fora raramente é suficiente para um motor de física. Quando dois objetos se sobrepõem, o solver precisa saber quão profundamente eles se interpenetram e em que direção empurrá-los para separá-los. O SDF fornece ambos.
Para um ponto dentro de um volume, a profundidade de penetração é e a direção de separação é o gradiente normalizado apontando em direção à superfície. Juntos, eles formam o vetor de translação mínima : mova o ponto por esse vetor e ele aterrissa exatamente na superfície.
Continuando o exemplo da esfera, o ponto tem profundidade 0.882 e direção de gradiente . Empurrar o ponto 0.882 unidades nessa direção resulta em , cuja distância à origem é , exatamente o raio. A aritmética confere: uma única consulta produziu tanto a quantidade de sobreposição quanto a correção.
Na prática, você amostra o SDF em cada vértice do objeto penetrante e toma a penetração mais profunda:
struct PenetrationResult {
float depth;
vec3 direction;
vec3 contactPoint;
};
PenetrationResult computePenetration(MeshVolume mesh, SDF volume) {
PenetrationResult result;
result.depth = 0.0;
for (int i = 0; i < mesh.vertexCount; i++) {
vec3 p = mesh.vertices[i];
float d = evaluateSDF(p, volume);
if (d < result.depth) {
result.depth = d;
result.direction = normalize(gradientSDF(p, volume));
result.contactPoint = p;
}
}
return result;
}
Como o gradiente do SDF aponta para fora da superfície, a direção de separação naturalmente empurra o ponto penetrante em direção à fronteira mais próxima.
Os casos extremos são onde a abordagem exige cuidado. Para volumes convexos definidos por SDFs exatos, o vértice de penetração mais profunda sempre aponta para a direção de separação correta. Para SDFs não convexos ou amostrados, a amostragem apenas de vértices pode perder penetrações finas: uma caixa pode interpenetrar uma superfície não convexa através de uma aresta ou face sem que nenhum de seus vértices cruze a fronteira. Um solver de produção, portanto, adiciona amostragem aresta-aresta e face-vértice, e para campos amostrados ele trata a distância interpolada como aproximada, contando com uma pequena margem de segurança para que o solver não oscile com valores ruidosos. Amostrar pontos extras multiplica o custo da consulta, e é por isso que a propriedade de tempo constante por avaliação importa tanto: você pode se dar ao luxo de várias amostras por objeto quando cada amostra é uma busca na grade.
Sphere Casting para Detecção de Colisão Contínua
Objetos em movimento rápido podem atravessar geometria fina entre quadros quando se usa verificações de colisão discretas. Uma bala que se move 50 unidades por quadro contra uma parede de 0.1 unidades de espessura pode estar de um lado no início do quadro e do outro no final, sem que nenhum teste discreto veja a sobreposição. A solução padrão é a detecção de colisão contínua (CCD), que varre um volume ao longo do caminho de movimento e encontra o primeiro tempo de impacto.
Com um SDF, você pode implementar CCD usando sphere casting. A ideia é marchar um ponto ao longo do vetor de movimento usando o campo de distância como tamanho do passo, exatamente como o sphere tracing, mas ao longo de um vetor de deslocamento em vez de um raio de visão:
float sphereCast(vec3 origin, vec3 direction, float maxDist, float radius,
SDF volume) {
float t = 0.0;
for (int i = 0; i < MAX_STEPS; i++) {
vec3 p = origin + direction * t;
float d = evaluateSDF(p, volume) - radius;
if (d < EPSILON) return t; // Hit
t += d;
if (t > maxDist) break; // No hit within the motion segment
}
return -1.0; // No hit
}
Quem chama passa o comprimento do deslocamento do quadro como maxDist e uma direção de movimento unitária como direction. O t retornado é a distância do impacto ao longo dessa direção, e t / maxDist é o tempo do primeiro contato como uma fração do movimento do quadro, que o motor de física usa para parar o objeto exatamente na superfície de colisão.
Dois detalhes importam. Subtrair o raio do objeto em movimento do valor do campo encolhe a forma contra a qual se varre: o cast trata a superfície do mundo como se tivesse sido inflada pelo raio do projétil, que é exatamente a superfície com a qual o ponto central do projétil colide. E a garantia de passo se mantém porque o SDF nunca superestima a distância, então nenhum passo pode pular por cima de uma superfície.
Um exemplo trabalhado: um projétil de raio 0.25 começa em e se move na direção em direção a uma esfera de raio 1 centrada na origem, com maxDist = 4. Em , o valor do campo é , então o cast avança até . Nessa posição, , o valor é , e o cast informa um impacto em . A resposta analítica é , então o cast encontrou o primeiro contato exatamente, em um único passo.
Os casos extremos do sphere casting espelham os do sphere tracing. Se o campo é um SDF amostrado ou suavizado que apenas aproxima a garantia de distância, a marcha pode ultrapassar características finas, então você mantém um épsilon conservador, limita a contagem de passos ou recorre a um teste discreto quando o cast não informa impacto. Objetos que começam o quadro já interpenetrados (o valor do campo é negativo na origem) retornam um impacto imediato em , que o motor deve tratar como uma sobreposição a resolver, não como um novo evento de colisão.
Casts de Raio e Cápsula
O sphere casting se generaliza para outras formas varridas. Um ray cast é um sphere cast com raio zero, útil para verificações de linha de visada e hitscan de armas. Um capsule cast substitui a marcha com origem pontual por um segmento que mantém uma distância fixa de uma linha, o que exige amostrar o SDF nos extremos do segmento e tomar o mínimo, ou usar um SDF de cápsula diretamente para o volume varrido. O mesmo laço de passo seguro funciona em todos os casos; apenas a consulta de distância dentro do laço muda. Essa uniformidade é uma vitória prática: um laço de cast, muitos tipos de consulta, sem estruturas de colisão novas.
Perfil de Desempenho
Consultas de colisão com SDF escalam de maneira diferente das abordagens tradicionais. Um teste de colisão malha contra malha exige percorrer hierarquias de volumes envolventes para ambos os objetos e testar pares de triângulos. Um teste SDF contra malha substitui uma hierarquia por uma avaliação de distância em tempo constante por vértice da outra forma.
Isso torna os SDFs especialmente fortes em cenários onde uma forma é simples e a outra complexa, ou onde muitos objetos precisam de consultas de colisão contra o mesmo ambiente estático. Muitos jogos assam campos de distância para a geometria estática do nível e os consultam em tempo de execução contra centenas de objetos dinâmicos. O custo de assar é pago uma única vez no tempo de build; o custo em tempo de execução é um punhado de buscas na grade por consulta. Quando o ambiente é estático, o campo nunca precisa de atualização. Quando ele muda, reconstruir ou atualizar localmente o campo assado é uma preocupação de pipeline separada, abordada em campos de distância com sinal assados .
SDFs não são a ferramenta certa para todo par de colisões. Duas malhas dinâmicas densas colidindo entre si continuam mais baratas como malha contra malha, porque avaliar um campo amostrado para ambas as formas ainda custa buscas na grade e interpolação, enquanto o caminho da malha usa hardware de função fixa. O ponto ideal é uma forma fixa e com muitas consultas (o nível) contra muitas formas dinâmicas baratas (projéteis, personagens, veículos).
Resumo
O contrato do SDF transforma as três consultas de colisão de trabalho pesado em aritmética:
- Contenção é uma verificação de sinal.
- Profundidade de penetração e direção de separação são a magnitude e o gradiente no ponto amostrado mais profundo, combinados em um vetor de translação mínima.
- Detecção de colisão contínua é um sphere cast com a mesma garantia de passo seguro do sphere tracing, usando o valor do campo menos o raio em movimento como tamanho do passo.
A mesma matemática de gradiente e penetração alimenta o próximo subsistema do motor: a resposta de colisão de partículas e os campos de força resolvem exatamente essas sobreposições em escala de partícula individual.