Spiegelband mit hoher Spiegelkraft

8

Wir haben einige Probleme mit unserer Raytracing-Funktion in DirectX, insbesondere mit einigen schwerwiegenden Banding-Problemen mit Specular. Bei hoher Spiegelkraft (über 8) beginnt die Streifenbildung. Ich frage mich, ob dies ein HDR / LDR-Problem ist oder mit etwas anderem in Verbindung gebracht werden könnte, beispielsweise mit Normalen oder anderen Vektoren.

AKTUALISIEREN

Suchen Sie unten nach Updates.

Hier ist der relevante Shader-Code für Blinn-Phong auf einer Kugel:

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 entspricht in diesem Bild 8 specPower entspricht in diesem Bild 8

specPower entspricht in diesem Bild 9 specPower entspricht in diesem Bild 9

Ist das so einfach wie ein HDR / LDR-Problem oder hängt es irgendwie mit normaler Präzision zusammen? Ich glaube, ich bin auf dieses Problem bereits bei einem verzögerten Renderer gestoßen, bei dem Normalen von geringer Genauigkeit waren und nicht ordnungsgemäß verpackt / entpackt wurden. In diesem Fall werden Normalen jedoch im laufenden Betrieb generiert und das Ganze direkt in den Backbuffer gerendert.

Update 1

Ich möchte dem oben Gesagten hinzufügen, dass Dreiecke das gleiche Artefakt aufweisen und ihre Normalen derzeit auf folgende Weise erzeugt werden:

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

Ich würde sagen, dies macht es noch unwahrscheinlicher, dass die Normalität der Oberfläche das Problem ist. Das folgende Bild zeigt, was passiert, wenn specPower 2048 erreicht.

Geben Sie hier die Bildbeschreibung ein

Bentebent
quelle
1
Nur eine Vermutung: versuchen Sie es float3 h = normalize( reflect(toLight,normal) );undspec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan
Es tut mir leid zu sagen, dass dies das gleiche Artefakt hervorgebracht hat.
Bentebent
4
Sieht nach einem Überlaufproblem aus. Ihr Wert sollte auf Null gehen, wird aber stattdessen auf 1 zurückgesetzt. Versuchen Sie, den Wert von specdirekt und auch auszugeben max(dot(normal, h), 0.0f). Suchen Sie in beiden Berechnungen nach dieser Rückkehr zu einem Wert von 1.
Dieses Bild zeigt, an welchem ​​Punkt die Spezifikation über 0,9 f liegt, so dass es den Anschein haben könnte, als wäre es der Schuldige. imgur.com/YbTS8m5
Bentebent
Währenddessen sieht max (Punkt (normal, h), 0.0f) so aus ( imgur.com/YV22egw ), wenn Werte über 0,99f ausgegeben werden.
Bentebent

Antworten:

1

Sieht nach einem Problem mit der Implementierung der Pow-Funktion auf dieser bestimmten GPU aus.

Wenn Sie einen Fehler in einer mathematischen Funktion vermuten, ersetzen Sie die mathematische Funktion durch die entsprechende Funktion. Ersetzen Sie beispielsweise:

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

Wenn der Fehler bei specPower = 9.0 angezeigt wird, codieren Sie ihn fest

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

Oder

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

Wenn das Problem behoben ist, liegt wahrscheinlich ein Unterlauffehler in der Pow-Funktion in Bezug auf den Exponenten auf dieser GPU oder diesem Treiber vor.

Was Sie als Workaround tun können, vorausgesetzt, die Fragment-Shader-Berechnung erfolgt intern mit 16-Bit-Floats. Wenn ich das richtig berechne, solltest du es tun

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

Es könnte 32768 oder 8192 sein: Ich könnte um ein Bit abweichen oder die GPU verwendet möglicherweise mehr oder weniger Präzision. für pow (x, 9.0) wäre die Klemmung pow (max (temp, 0.3401975), 9.0) Werte von X darunter würden den Exponentenbereich (+15 bis -14) von IEEE 16-Bit-Gleitkommawerten unterschreiten (wieder bin ich vorausgesetzt, dies wird von der GPU verwendet.)

aus Rosetta-Code ( 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));

Sie müssen dies auf der CPU berechnen und den Wert in einer Uniform eingeben: Die GPU hat wahrscheinlich das gleiche Präzisionsproblem, wenn Sie dies direkt im Shader berechnen.

Wenn Sie dies bestätigen können, wenden Sie sich an das GPU-Treiber-Team mit einem Beispiel-Shader-Quellcode, der das Problem repliziert. Geben Sie die genaue Version des Treibers, des Betriebssystems und des GPU-Modells an. Eine ausführbare Datei kann hilfreich sein, sendet sie jedoch nicht wie Ihre E-Mail im Viren- / Spam-Checker gefangen sein. Erstellen Sie einfach eine und bewahren Sie eine Kopie auf, falls Sie nach einer vorgefertigten ausführbaren Beispieldatei gefragt werden. Geben Sie ihnen aus Höflichkeit die Möglichkeit, das Problem zu beheben, bevor Sie es öffentlich benennen. Es könnte einfach ein Optimierungsfehler in ihrem Shader-Compiler sein, der nur bei Ihrem speziellen Shader auftritt. Es kann eine Weile (Monate) dauern, bis sie mit ihnen in Kontakt treten. Diese Leute sind oft unterbesetzt.

Stephane Hockenhull
quelle