Faixas especulares com alta potência especular

8

Estamos tendo alguns problemas com o nosso traçado de raios no DirectX, especialmente com alguns problemas sérios de faixas com o especular. Com alta potência especular (acima de 8), começa a formação de faixas. Gostaria de saber se este é um problema de HDR / LDR ou pode estar relacionado a outra coisa, como normais ou outros vetores?

ATUALIZAR

Veja abaixo as atualizações.

Aqui está o código de sombreamento relevante para Blinn-Phong em uma esfera:

float3 hitPoint = thisRay.origin + thisRay.direction * bestHit.hitT;
float3 normal = normalize(hitPoint - spheres[bestHit.hitID].center);
float3 toLight = pointLights[0].position.xyz - hitPoint;
float d = length(toLight);
toLight = normalize(toLight);

float diffuse = max(dot(normal, toLight), 0.0f);

float3 v = normalize(thisRay.origin - hitPoint);
float3 h = normalize(v + toLight);

float spec = 0;
if (diffuse > 0)
    spec = pow(max(dot(normal, h), 0.0f), specPower) * diffuse;

output[threadID.xy] = spheres[bestHit.hitID].colour * diffuse + spheres[bestHit.hitID].specColour * spec;

specPower é igual a 8 nesta imagem specPower é igual a 8 nesta imagem

specPower é igual a 9 nesta imagem specPower é igual a 9 nesta imagem

Isso é tão simples quanto um problema de HDR / LDR ou está de alguma forma relacionado à precisão normal? Acredito que já encontrei esse problema antes em um renderizador diferido, onde os normais eram de baixa precisão e embalados / descompactados incorretamente, mas, neste caso, os normais são gerados em tempo real e tudo é renderizado diretamente no backbuffer.

Atualização 1

Gostaria de acrescentar que os triângulos sofrem o mesmo artefato e que eles têm seu normal gerado atualmente da seguinte maneira:

float3 normal = normalize(cross(triangles[bestHit.hitID].vertices[1] - triangles[bestHit.hitID].vertices[0],
                                    triangles[bestHit.hitID].vertices[2] - triangles[bestHit.hitID].vertices[0]));

Eu diria que isso torna ainda mais improvável que o normal da superfície seja o problema. A imagem a seguir mostra o que acontece quando o specPower chega a 2048.

insira a descrição da imagem aqui

Bentebent
fonte
11
apenas um palpite: tente float3 h = normalize( reflect(toLight,normal) );, espec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan
Lamento dizer que isso produziu o mesmo artefato.
Bentebent
4
Parece um problema de estouro. Seu valor deve ir para zero, mas, em vez disso, volta para 1. Tente gerar o valor specdiretamente, e também max(dot(normal, h), 0.0f). Procure esse retorno para um valor 1 em qualquer cálculo.
Esta imagem mostra em que ponto as especificações ultrapassam 0,9f, de modo que parece que ele pode ser o culpado. imgur.com/YbTS8m5
Bentebent
Enquanto isso, max (ponto (normal, h), 0,0f) fica assim ( imgur.com/YV22egw ) ao gerar valores acima de 0,99f.
Bentebent

Respostas:

1

Parece um problema com a implementação da função pow nesta GPU específica.

Ao suspeitar de um bug em uma função matemática, substitua a função matemática por seu equivalente, por exemplo, substitua:

spec = pow(max(dot(normal, h), 0.0f), specPower) * diffuse;

Com o bug aparecendo em specPower = 9.0, codifique-o para

float temp = max(dot(normal, h), 0.0f);
spec = pow(temp, 8.0) * temp * diffuse;

Ou

float temp = max(dot(normal, h), 0.0f);
spec = temp * temp * temp * temp * temp * temp * temp * temp * temp * diffuse;

Se o problema desaparecer, isso significa que provavelmente há um bug de fluxo insuficiente na função pow em relação ao expoente nesta GPU ou driver.

O que você pode fazer como uma solução alternativa, assumindo que o cálculo do fragment shader seja feito internamente com flutuações de 16 bits. Se eu calcular isso direito, você deve fazer

pow(max(temp, (Nth root of (1.0/16384.0))), N);

Pode ser 32768 ou 8192: posso estar um pouco desligado ou a GPU pode estar usando mais ou menos precisão. para pow (x, 9.0), a fixação seria pow (max (temp, 0.3401975), 9.0) Os valores de X abaixo disso estariam abaixo do intervalo de expoentes (+15 a -14) dos pontos flutuantes IEEE de 16 bits (novamente, assumindo que é isso que a GPU usa.)

do código rosetta ( http://rosettacode.org/wiki/Nth_root#C.2B.2B )

double NthRoot(double value, double degree)
{
    return pow(value, (double)(1 / degree));
};

my_shader->SetUniform("specular_minimum", NthRoot((1.0/16384.0), specPower));

Você precisará calcular isso na CPU e alimentar o valor de maneira uniforme: a GPU provavelmente terá o mesmo problema de precisão se você calcular isso corretamente no shader.

Se você puder confirmar isso, tente entrar em contato com a equipe de drivers da GPU com um código-fonte de sombreador de amostra que replique o problema e forneça a versão exata do driver, o SO e a revisão do modelo da GPU, um executável pode ajudar, mas não o envia conforme seu email fique preso no verificador de vírus / spam. Basta criar uma e manter uma cópia, caso elas solicitem um executável de amostra pré-criado. Como cortesia, dê a eles a chance de corrigi-lo antes de nomeá-lo publicamente. Poderia ser simplesmente um bug de otimização no compilador de shader que só acontece com o shader específico. Pode demorar um pouco (meses) para entrar em contato com eles, esses caras geralmente têm falta de pessoal.

Stephane Hockenhull
fonte