Bandas especulares con alto poder especular.

8

Tenemos algunos problemas con nuestro trazado de rayos en DirectX, especialmente con algunos problemas serios de bandas con especular. Con alta potencia especular (por encima de 8) comienza el anillado. Me pregunto si este es un problema HDR / LDR o podría estar relacionado con otra cosa, como las normales u otros vectores.

ACTUALIZAR

Mire abajo para actualizaciones.

Aquí está el código de sombreador relevante para Blinn-Phong en una 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 es igual a 8 en esta imagen specPower es igual a 8 en esta imagen

specPower es igual a 9 en esta imagen specPower es igual a 9 en esta imagen

¿Es tan simple como un problema HDR / LDR o está relacionado de alguna manera con la precisión normal? Creo que he encontrado este problema antes en un renderizador diferido donde las normales eran de baja precisión y estaban empaquetadas / desempaquetadas incorrectamente, pero en este caso las normales se generan sobre la marcha y todo se procesa directamente en el backbuffer.

Actualización 1

Me gustaría agregar a lo anterior que los triángulos sufren el mismo artefacto y actualmente tienen su normal generado de la siguiente manera:

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

Diría que esto hace que sea aún más improbable que la normalidad de la superficie sea el problema. La siguiente imagen muestra lo que sucede cuando specPower alcanza 2048.

ingrese la descripción de la imagen aquí

Bentebent
fuente
1
solo una suposición: intente float3 h = normalize( reflect(toLight,normal) );, yspec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan
Lamento decir que esto produjo el mismo artefacto.
Bentebent
44
Parece un problema de desbordamiento. Su valor debería ir a cero, pero en cambio está volviendo a 1. Intente generar el valor de specdirectamente, y también max(dot(normal, h), 0.0f). Busque este retorno a un valor 1 en cualquier cálculo.
Esta imagen muestra en qué punto la especificación supera los 0.9f, por lo que parece que podría ser el culpable. imgur.com/YbTS8m5
Bentebent
Mientras tanto, max (punto (normal, h), 0.0f) se ve así ( imgur.com/YV22egw ) al generar valores superiores a 0.99f.
Bentebent

Respuestas:

1

Parece un problema con la implementación de la función pow en esta GPU en particular.

Cuando sospeche un error en una función matemática, reemplace la función matemática por su equivalente, por ejemplo, reemplace:

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

Con el error que se muestra en specPower = 9.0, codifíquelo

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;

Si el problema desaparece, esto significa que probablemente haya un error de flujo inferior en la función pow con respecto al exponente en esta GPU o controlador.

Lo que puede hacer como solución alternativa , suponiendo que el cálculo del sombreador de fragmentos se realice internamente con flotadores de 16 bits. Si calculo esto bien, deberías hacer

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

Podría ser 32768 u 8192: podría estar apagado un bit o la GPU podría estar usando más o menos precisión. para pow (x, 9.0), la sujeción sería pow (max (temp, 0.3401975), 9.0) Los valores de X por debajo de esto desbordarían el rango de exponente (+15 a -14) de los puntos flotantes IEEE de 16 bits (de nuevo, estoy suponiendo que esto es lo que usa la GPU).

del código de 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));

Tendrá que calcular esto en la CPU y alimentar el valor de manera uniforme: la GPU probablemente tendrá el mismo problema de precisión si calcula esto directamente en el sombreador.

Si puede confirmar esto, intente ponerse en contacto con el equipo de controladores de GPU con un código fuente de sombreador de muestra que reproduzca el problema, proporcione la versión exacta del controlador, el sistema operativo y la revisión del modelo de GPU, un ejecutable podría ayudar, pero no lo envíe como su correo electrónico lo hará. quedar atrapado en el verificador de virus / spam. Simplemente construya uno y conserve una copia en caso de que soliciten un ejecutable de muestra preconstruido. Como cortesía, deles la oportunidad de arreglarlo antes de nombrarlo públicamente. Podría ser simplemente un error de optimización en su compilador de sombreadores que solo ocurre con su sombreador en particular. Puede tomar un tiempo (meses) ponerse en contacto con ellos, estos muchachos a menudo carecen de personal.

Stephane Hockenhull
fuente