Bande speculari ad alto potere speculare


8

Stiamo riscontrando alcuni problemi con il nostro ray tracing in DirectX, in particolare con alcuni gravi problemi di banding con speculare. Con alta potenza speculare (sopra 8) inizia la fasciatura. Mi chiedo se questo è un problema di HDR / LDR o potrebbe essere correlato a qualcos'altro, come normali o altri vettori?

AGGIORNARE

Guarda sotto per gli aggiornamenti.

Ecco il codice shader rilevante per Blinn-Phong su una sfera:

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 è uguale a 8 in questa immagine specPower è uguale a 8 in questa immagine

specPower è uguale a 9 in questa immagine specPower è uguale a 9 in questa immagine

È semplice come un problema di HDR / LDR o è in qualche modo correlato alla normale precisione? Credo di aver già riscontrato questo problema in un renderer differito in cui le normali erano di bassa precisione e impacchettate / disimballate in modo improprio ma in questo caso le normali vengono generate al volo e il tutto viene reso direttamente al backbuffer.

Aggiornamento 1

Vorrei aggiungere a quanto sopra che i triangoli subiscono lo stesso artefatto e hanno il loro normale attualmente generato nel modo seguente:

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

Direi che questo rende ancora più improbabile che la normale superficie sia il problema. L'immagine seguente mostra cosa succede quando specPower raggiunge il 2048.

inserisci qui la descrizione dell'immagine


1
solo un'ipotesi: provare float3 h = normalize( reflect(toLight,normal) );, espec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan il

Mi dispiace dire che questo ha prodotto lo stesso artefatto.
Bentebent,

4
Sembra un problema di overflow. Il tuo valore dovrebbe andare a zero, ma sta invece tornando indietro a 1. Prova a fornire il valore di specdirettamente e anche max(dot(normal, h), 0.0f). Cerca questo ritorno a un valore 1 in entrambi i calcoli.

Questa immagine mostra a che punto le specifiche vanno oltre 0.9f, quindi sembra che potrebbe essere il colpevole. imgur.com/YbTS8m5
Bentebent,

Nel frattempo, max (punto (normale, h), 0.0f) assomiglia a questo ( imgur.com/YV22egw ) quando si generano valori superiori a 0.99f.
Bentebent,

Risposte:


1

Sembra un problema con l'implementazione della funzione pow su questa particolare GPU.

Quando si sospetta un bug in una funzione matematica, sostituire la funzione matematica con il suo equivalente, ad esempio sostituire:

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

Con il bug visualizzato su specPower = 9.0, codificalo a

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

O

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

Se il problema scompare significa che probabilmente c'è un bug underflow nella funzione pow per quanto riguarda l'esponente su questa GPU o driver.

Cosa si può fare come soluzione, supponendo che il calcolo dello shader di frammenti sia fatto internamente con float a 16 bit. Se calcolo questo diritto, dovresti farlo

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

Potrebbe essere 32768 o 8192: potrei essere spento di un bit o la GPU potrebbe usare più o meno precisione. per pow (x, 9.0) il bloccaggio sarebbe pow (max (temp, 0.3401975), 9.0) I valori di X al di sotto di questo porterebbero al di sotto dell'intervallo esponente (da +15 a -14) dei punti fluttuanti IEEE a 16 bit (di nuovo, sono supponendo che questo sia ciò che utilizza la GPU.)

dal codice 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));

Dovrai calcolare questo sulla CPU e alimentare il valore in modo uniforme: la GPU avrà probabilmente lo stesso problema di precisione se calcoli questo diritto nello shader.

Se puoi confermarlo, prova a contattare il team del driver GPU con un codice sorgente di shader di esempio che replica il problema fornendo loro la versione esatta del driver, il sistema operativo e la revisione del modello GPU, un eseguibile potrebbe aiutare ma non inviarlo come la tua email rimanere intrappolati nel controllo virus / spam. Basta crearne uno e conservarne una copia nel caso in cui richiedano un eseguibile di esempio precostruito. Per cortesia, dai loro la possibilità di risolverlo prima di nominarlo pubblicamente. Potrebbe semplicemente essere un bug di ottimizzazione nel loro compilatore di shader che si verifica solo con il tuo shader particolare. Potrebbe volerci un po '(mesi) per entrare in contatto con loro, questi ragazzi sono spesso a corto di personale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.