Qual é a utilidade do raio quadrado e do raio inverso ao quadrado para cálculos de iluminação?


16

Em um dos slides do PowerPoint "Renderização do DirectX 11 no Battlefield 3", observei o código a seguir:

struct Light {
    float3 pos; float sqrRadius;
    float3 color; float invSqrRadius;
}

Não entendo por que eles armazenariam o raio ao quadrado e até o inverso ao quadrado (que acredito ser apenas um raio ao quadrado) em vez de simplesmente armazenar o raio? Como eles estão usando esses dados em seus cálculos? Além disso, e as luzes de cone e linha? Essa estrutura deve ser apenas para as luzes de ponto, não consigo vê-la funcionando para outros tipos - não há dados suficientes. Ainda assim, eu gostaria de saber como eles usam esse quadrado e o invSquare.

ATUALIZAÇÃO: Ok, finalmente entendi.

Aqui está a equação clássica de atenuação da luz, facilmente encontrada na rede:

float3 lightVector = lightPosition - surfacePosition;

float attenuation = saturate(1 - length(lightVector)/lightRadius);

É relativamente caro, pois length(lightVector)está realmente fazendo isso:

length(lightVector) = sqrt(dot(lightVector, lightVector);

além disso, a operação de divisão (/lightRadius)também é bastante cara.

Em vez de calcular a atenuação da luz dessa maneira, você pode calculá-la da seguinte maneira, o que seria muito mais rápido:

attenuation = saturate(1 - dot(lightVector, lightVector)*invRadiusSqr);

onde invRadiusSqr pode ser pré-calculado no nível da CPU e passado como uma constante de sombreador.

Além disso, você obtém uma atenuação quadrática da luz como resultado (em vez de linear no primeiro caso), o que é ainda melhor, pois a luz IRL demonstrou ter queda quadrática.

Obrigado a todos por sua ajuda!


13
Preenchimento. Os shaders estão alinhados por 16 bytes. E se você der uma olhada em como o código é formatado, verá que ele será compactado em dois float4. Por que não armazenar algo útil quando você o obtém gratuitamente do cache?
Tordin

Respostas:


23

Isso é simplesmente um tipo de otimização invSqrRadius = 1/SqrRadius, pois, em vez de calcular o raio inverso ao quadrado de cada luz toda vez que eles simplesmente armazenam em cache, a razão é que a divisão é geralmente uma operação "lenta" pelo menos quando comparada à multiplicação.

Essa otimização é relevante principalmente:

  • quando a operação é realizada uma quantidade enorme de vezes para cada luz, o armazenamento em cache do valor libera ciclos extras de CPU / GPU
  • E supondo que o tempo de acesso à memória para ler o valor seja realmente mais rápido do que recalculá-lo.

Em relação à forma como é usado, não tenho certeza sobre sua implementação específica, mas em relação a 1/sqrRadiusisso é simplesmente usado para atenuação leve, queda e seleção. Também é relevante para direcionais e holofotes, a única diferença no caso de holofotes é que você precisa calcular o fator holofotes após aplicar a atenuação . Em relação às luzes direcionais, como o sol, ele geralmente não possui atenuação ou queda, por isso acho que será ignorado.

[EDIT] Só para elaborar mais, é não dados irrelevantes. A irradiância da luz pode ser calculada usando a seguinte equação:

E = Phi / 4 * pi * rSqr;

Onde

E é a densidade da área do fluxo.

Phi é fluxo de radiação.

4 * pi * rSqr é a área da superfície de uma esfera.

Esta equação explica por que a quantidade de energia recebida diminui com a distância ao quadrado.

Outro ponto é que você precisa calcular a distância entre o vértice e a luz para calcular a contribuição da luz em um vértice específico (é improvável que esse valor seja armazenado em cache), o vértice pode estar dentro ou fora da faixa de luz que nos leva ao próximo ponto onde Radius Squareé útil para abate.

Se você deseja um exemplo prático para calcular a queda de luz e o descarte, isso é especialmente útil em renderizadores diferidos com base em bloco, aqui está um exemplo .


Maldições, você me venceu por 7 segundos! ;) (E com uma resposta mais abrangente, também)!
Trevor Powell

Obrigado pelo comentário detalhado, esp pelo link! Pelo que entendi no último link, o Battlefield 3 armazena não o raio, mas a distância real entre a fonte de luz e o receptor de luz, certo? Esse é o valor "d" que eles usam no artigo.
cubrman

@ cubrman é difícil especular sem ver o código. Meu palpite é que é o raio inversoSqr. E as equações que eles usam podem variar muito do artigo.
precisa saber é o seguinte

Mas diga-me, como você pode usar um raio de luz invertida ao quadrado em um cálculo de iluminação? Cada fonte que encontrei na rede me diz que preciso encontrar a DISTÂNCIA entre a superfície receptora e a fonte de luz, dividir a última pelo raio de luz original E, depois, quadrar o resultado. Onde você usaria o raio quadrado ou o invSqrRadius? Isso parece dados completamente irrelevantes para mim.
cubrman

1
@cubrman atualizou a resposta.
precisa saber é o seguinte

6

invSqrRadius não é 1 - sqrRadius; é 1 / sqrRadius.

Isso significa que você pode multiplicar por invSqrRadius, em vez de dividir por sqrRadius (como a divisão geralmente é muito mais cara que a multiplicação)


6

As outras respostas aqui tratam do raio quadrado inverso, mas, em vez disso, vou olhar para o raio quadrado (conceito que tocamos, mas acredito que merece uma discussão mais aprofundada).

Para que os quadrados são úteis são comparações de distância. Sabemos que calcular a distância entre dois pontos envolve uma raiz quadrada, e as raízes quadradas são caras de calcular, mas se tudo o que queremos fazer é comparar distâncias (para descobrir qual é menor ou maior e fazer algo interessante com base no resultado) podemos jogar fora a raiz quadrada.

Se sqrt (x)> sqrt (y), também é o caso de x> y.

Para uma luz, o raio ao quadrado é o mesmo que a distância entre o centro da luz e sua extensão máxima - ao quadrado, é claro.

Para cálculos de iluminação, isso pode ser usado para um caso de saída antecipada. Se a distância entre o ponto que você está iluminando e o centro da luz (quadrado) for maior que o raio quadrado, o ponto não receberá luz e você não precisará executar o resto dos seus cálculos. Portanto, isso é apenas uma otimização (bastante comum) - podemos usar o raio quadrado para fazer a comparação da distância sem raízes quadradas caras, e a um custo de apenas um produto de subtração e ponto.

É claro que não sei se é exatamente para isso que o BF3 está usando, mas espero que não esteja muito longe da realidade.


Então, se eu entendi direito, o código será: if (dot ((lightPos - surfacePos)) ((lightPos - surfacePos))> lightRadiusSqr) não acende, certo?
cubrman
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.