Sombras em Tempo Real e Oclusão Ambiente com SDF

EN NL ES PT-BR


Os efeitos de renderização baseados em SDF vão além das superfícies primárias com sphere tracing abordadas no artigo do hub. Campos de distância também impulsionam dois efeitos de iluminação difíceis de fazer bem com mapas de sombra tradicionais: sombras suaves com penumbra uniforme e oclusão ambiente sem uma passada de screen-space. Este artigo explica a matemática por trás de ambos, percorre o GLSL e compara os resultados com o pipeline padrão de mapas de sombra.

Para os fundamentos do sphere tracing e do gradiente do SDF, comece com a visão geral de Campos de Distância com Sinal . Para o contexto do motor como um todo, veja SDFs no desenvolvimento de jogos .

Sombras Suaves com Campos de Distância

Sombras suaves de luzes de área são caras com mapas de sombra porque você precisa amostrar a fonte de luz em múltiplas posições e tirar a média dos resultados. Mapas de sombra também sofrem aliasing severo em ângulos rasantes e precisam de cascatas para cenas grandes.

Sombras suaves com campos de distância aproximam a penumbra de luzes de área percorrendo um raio secundário de cada ponto da superfície em direção à fonte de luz e registrando quão próximo o raio passa da geometria oclusora. A distância mínima até o oclusor ao longo do raio determina a suavidade da sombra:

float sdfSoftShadow(vec3 origin, vec3 lightDir, float maxDist, float softness,
                    SDF scene) {
    float res = 1.0;
    float t = 0.02;  // Start slightly off the surface to avoid self-occlusion

    for (int i = 0; i < MAX_SHADOW_STEPS; i++) {
        float d = evaluateSDF(origin + lightDir * t, scene);
        // The closer the ray passes to an occluder, the darker the shadow
        res = min(res, softness * d / t);
        t += clamp(d, 0.01, 0.5);
        if (res < 0.001 || t > maxDist) break;
    }
    return clamp(res, 0.0, 1.0);
}

A percepção chave está na linha softness * d / t. Quando o raio passa longe de qualquer superfície, d permanece grande e res fica próximo de 1.0 (totalmente iluminado). Quando o raio roça próximo a um oclusor, d se torna pequeno, d / t diminui e res cai em direção a 0.0 próximo ao contato. O parâmetro softness controla a largura da penumbra: valores maiores espalham a borda da sombra por uma região mais ampla.

Um exemplo trabalhado torna a razão concreta. A uma distância de passo t=1.0t = 1.0 ao longo do raio de luz, o campo lê d=0.1d = 0.1, significando que o raio passa a 0.1 unidades de uma superfície. Com softness = 8.0, a contribuição é 8.00.1/1.0=0.88.0 \cdot 0.1 / 1.0 = 0.8, então esse passo reduz o resultado para 80% do brilho. Se o raio passa duas vezes mais perto, d=0.05d = 0.05, a contribuição é 8.00.05/1.0=0.48.0 \cdot 0.05 / 1.0 = 0.4, e o ponto cai para 40% do brilho. A penumbra é a região onde essa razão interpola entre os valores totalmente iluminados e totalmente sombreados, e o parâmetro softness estica ou comprime essa região.

Isso produz sombras suaves e fisicamente plausíveis com um único raio por ponto da superfície, sem limites de resolução de mapas de sombra nem emendas de cascata. O custo são os passos extras de ray-marching dentro do laço de sombra, mas esses passos avaliam o mesmo SDF que o raio primário já usou, então nenhuma nova estrutura de dados é necessária. Quando a cena já é um SDF, a passada de sombra é apenas um segundo laço sobre a mesma função.

Oclusão Ambiente com SDF

A oclusão ambiente mede o quanto um ponto da superfície está ocluído pela geometria próxima. Em um pipeline de malhas, isso exige amostragem em screen-space (SSAO) ou mapas de oclusão pré-assados. Com um SDF, a oclusão ambiente pode ser calculada diretamente durante a passada de sombreamento, amostrando o campo de distância em um hemisfério ao redor do ponto da superfície:

float sdfAmbientOcclusion(vec3 position, vec3 normal, float radius,
                           SDF scene) {
    float occlusion = 0.0;
    float weightSum = 1e-6;

    for (int i = 0; i < AO_SAMPLES; i++) {
        vec3 dir = sampleHemisphere(normal, i, AO_SAMPLES);
        float t = 0.01;
        float sampleOcclusion = 0.0;

        // March a short ray into the hemisphere
        for (int j = 0; j < AO_STEPS; j++) {
            float d = evaluateSDF(position + dir * t, scene);
            // If we are inside nearby geometry, accumulate occlusion
            sampleOcclusion += max(0.0, -d) / (1.0 + t);
            t += max(abs(d), 0.01);
            if (t > radius) break;
        }

        float weight = 1.0 / (1.0 + t);
        occlusion += sampleOcclusion * weight;
        weightSum += weight;
    }
    return 1.0 - occlusion / weightSum;
}

Cada direção de amostra marcha um raio curto para fora da superfície e acumula oclusão sempre que a distância fica negativa (dentro da geometria próxima). O denominador 1.0 + t faz com que oclusores próximos contribuam mais do que distantes. Um raio de amostra que atinge geometria bem ao lado do ponto da superfície acumula um termo de oclusão grande; um raio que sai para o espaço aberto acumula nada e contribui com um peso grande para a média final, mantendo o resultado brilhante.

Um exemplo trabalhado: um raio de amostra a uma distância de passo t=0.5t = 0.5d=0.2d = -0.2, significando que o raio está dentro da geometria próxima. O termo contribuído é max(0,0.2)/(1+0.5)=0.133\max(0, 0.2) / (1 + 0.5) = 0.133 e o peso para essa direção é 1/(1+0.5)=0.6671 / (1 + 0.5) = 0.667, então essa direção puxa a média em direção à oclusão. Em terreno aberto, cada direção de amostra lê distâncias positivas, cada termo de oclusão é zero e o resultado permanece 1.0.

Isso produz sombras de contato em frestas e sob saliências sem precisar de uma passada de screen-space separada nem de pré-computação. Ele compartilha o SDF existente do renderizador, então a geometria dinâmica é tratada automaticamente: mova uma caixa e a resposta de AO se move com ela, sem re-assar e sem artefatos de screen-space.

Comparação com Mapas de Sombra

PropriedadeMapas de sombraSombras com SDF
Custo de configuraçãoUma passada de render por luzNenhum (usa o SDF existente)
Custo por pixelUma amostra de textura (dura) / várias (suave)Múltiplas avaliações de SDF
AliasingLimitado pela resolução, precisa de cascatasSuave por construção
Geometria dinâmicaGrátis (re-renderizar o mapa de sombra)Grátis (o SDF se atualiza com a cena)
Mundos grandesPrecisa de mapas de sombra em cascataUm único SDF cobre tudo
Sombras semitransparentesDifícilNatural (baseado em distância)

Sombras com SDF não são uma substituição universal para mapas de sombra. Mapas de sombra são mais rápidos para sombras duras de luzes direcionais distantes em cenas estáticas. Sombras com SDF se destacam quando você precisa de penumbra suave, quando a cena já está representada como um SDF ou quando a geometria dinâmica muda a oclusão a cada quadro.

As limitações merecem ser ditas claramente. Os laços de sombra e AO adicionam custo de ray-marching por cima do raio primário, então uma cena totalmente rasterizada com malhas e sombras em screen-space pode ser mais barata no total. E a qualidade de ambos os efeitos depende da garantia de distância do campo: um campo amostrado ou suavizado que superestima a distância pode deixar raios de sombra atravessarem oclusores finos, então efeitos como esses são mais confiáveis em campos analíticos exatos ou em grades cuidadosamente assadas com clamps de passo conservadores.

Resumo

Dois efeitos de iluminação surgem do mesmo contrato do SDF:

  • Sombras suaves marcham um raio em direção à luz e registram a aproximação mais próxima à geometria, softness * d / t, que estima a largura da penumbra a partir de um único raio.
  • Oclusão ambiente marcha raios curtos em um hemisfério e acumula -d / (1 + t) onde quer que o campo fique negativo, produzindo sombras de contato sem uma passada de screen-space.

Ambos os efeitos reutilizam o SDF da cena, então a geometria dinâmica é grátis e nenhum limite de resolução de mapas de sombra se aplica. Quando o resto da cena já é baseado em campos, eles são o caminho de iluminação natural; o mergulho profundo em terreno destrutível mostra o mesmo campo impulsionando a geometria que essas passadas iluminam.