Bandes spéculaires à puissance spéculaire élevée

8

Nous rencontrons des problèmes avec notre lancer de rayons dans DirectX, en particulier avec de graves problèmes de regroupement avec spéculaire. Avec une puissance spéculaire élevée (supérieure à 8), les bandes commencent. Je me demande s'il s'agit d'un problème HDR / LDR ou s'il pourrait être lié à autre chose, comme des normales ou d'autres vecteurs?

MISE À JOUR

Regardez ci-dessous pour les mises à jour.

Voici le code de shader pertinent pour Blinn-Phong sur une sphère:

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 est égal à 8 dans cette image specPower est égal à 8 dans cette image

specPower est égal à 9 dans cette image specPower est égal à 9 dans cette image

Est-ce aussi simple qu'un problème HDR / LDR ou est-ce lié à la précision normale? Je crois avoir déjà rencontré ce problème dans un rendu différé où les normales étaient de faible précision et mal emballées / déballées mais dans ce cas, les normales sont générées à la volée et le tout est rendu directement dans le backbuffer.

Mise à jour 1

Je voudrais ajouter à ce qui précède que les triangles souffrent du même artefact et qu'ils ont leur normale actuellement générée de la manière suivante:

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

Je dirais que cela rend encore plus improbable que la normale de la surface soit le problème. L'image suivante montre ce qui se passe lorsque specPower atteint 2048.

entrez la description de l'image ici

Bentebent
la source
1
juste une supposition: essayez float3 h = normalize( reflect(toLight,normal) );, etspec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan
Désolé de dire que cela a produit le même artefact.
Bentebent
4
Ressemble à un problème de débordement. Votre valeur doit aller à zéro, mais revient à la place à 1. Essayez de sortir la valeur de specdirectement, et aussi max(dot(normal, h), 0.0f). Recherchez ce retour à une valeur 1 dans l'un ou l'autre calcul.
Cette image montre à quel point la spécification va au-dessus de 0,9f, ce qui semble être le coupable. imgur.com/YbTS8m5
Bentebent
Pendant ce temps, max (point (normal, h), 0,0f) ressemble à ceci ( imgur.com/YV22egw ) lors de la sortie de valeurs supérieures à 0,99f.
Bentebent

Réponses:

1

Cela ressemble à un problème avec l'implémentation de la fonction pow sur ce GPU particulier.

Lorsque vous suspectez un bogue dans une fonction mathématique, remplacez la fonction mathématique par son équivalent, par exemple remplacez:

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

Avec le bogue apparaissant à specPower = 9.0, codez-le en dur

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;

Si le problème disparaît, cela signifie qu'il y a probablement un bogue de sous-dépassement dans la fonction pow concernant l'exposant sur ce GPU ou ce pilote.

Ce que vous pouvez faire en contournant, en supposant que le calcul du fragment shader est fait en interne avec des flottants 16 bits. Si je calcule ce droit, vous devriez le faire

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

Cela pourrait être 32768 ou 8192: je pourrais être décalé d'un bit ou le GPU pourrait utiliser plus ou moins de précision. pour pow (x, 9.0), le serrage serait pow (max (temp, 0.3401975), 9.0) Les valeurs de X en dessous de cette valeur dépasseraient la plage d'exposants (+15 à -14) des virgules flottantes IEEE 16 bits (encore une fois, je suis en supposant que c'est ce que le GPU utilise.)

à partir du code 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));

Vous devrez calculer cela sur le CPU et alimenter la valeur de manière uniforme: le GPU aura probablement le même problème de précision si vous calculez cela directement dans le shader.

Si vous pouvez le confirmer, essayez de contacter l'équipe des pilotes GPU avec un exemple de code source de shader qui reproduit le problème en leur donnant la version exacte du pilote, le système d'exploitation et la révision du modèle GPU, un exécutable peut vous aider mais ne l'envoyez pas comme votre e-mail le fera. être piégé dans le vérificateur de virus / spam. Créez-en un et conservez-en une copie au cas où ils demanderaient un exemple d'exécutable pré-construit. Par courtoisie, donnez-leur la chance de le réparer avant de le nommer publiquement. Cela pourrait simplement être un bogue d'optimisation dans leur compilateur de shader qui ne se produit qu'avec votre shader particulier. Cela peut prendre un certain temps (des mois) pour entrer en contact avec eux, ces gars-là sont souvent en sous-effectif.

Stéphane Hockenhull
la source