Зеркальная полоса с высокой зеркальной мощностью

8

У нас есть некоторые проблемы с трассировкой лучей в DirectX, особенно с некоторыми серьезными проблемами с зеркальным отражением. При высокой зеркальной мощности (выше 8) начинается полосатость. Мне интересно, если это проблема HDR / LDR или это может быть связано с чем-то другим, например с нормальными или другими векторами?

ОБНОВИТЬ

Смотрите ниже для обновлений.

Вот соответствующий код шейдера для Blinn-Phong на сфере:

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 равно 8 на этом изображении specPower равно 8 на этом изображении

specPower на этом изображении равен 9 specPower на этом изображении равен 9

Это так же просто, как проблема HDR / LDR, или это как-то связано с нормальной точностью? Я полагаю, что я сталкивался с этой проблемой раньше в отложенном рендере, где нормали были низкой точности и неправильно упакованы / распакованы, но в этом случае нормали генерируются на лету, и все это передается непосредственно в буфер.

Обновление 1

Я хотел бы добавить к вышесказанному, что треугольники страдают от одного и того же артефакта, и их нормаль в настоящее время генерируется следующим образом:

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

Я бы сказал, что это делает еще более маловероятным, что нормаль поверхности является проблемой. На следующем изображении показано, что происходит, когда specPower достигает 2048.

введите описание изображения здесь

Bentebent
источник
1
просто предположение: попробуйте float3 h = normalize( reflect(toLight,normal) );, иspec = pow(dot(v, h) * 0.5 + 0.5, specPower) * diffuse;
Raxvan
Извините, что это привело к тому же самому артефакту.
Бентебент
4
Похоже, проблема переполнения. Ваше значение должно стремиться к нулю, но вместо этого возвращается к 1. Попробуйте вывести значение specнепосредственно, а также max(dot(normal, h), 0.0f). Ищите этот возврат к значению 1 в любом расчете.
Это изображение показывает, в какой момент спецификации выше 0,9f, так что кажется, что это может быть виновником. imgur.com/YbTS8m5
Бентебент
Между тем, max (точка (нормаль, h), 0,0f) выглядит так ( imgur.com/YV22egw ) при выводе значений выше 0,99f.
Бентебент

Ответы:

1

Похоже, проблема с реализацией функции pow на этом конкретном графическом процессоре.

При подозрении на ошибку в математической функции замените математическую функцию ее эквивалентом, например, замените:

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

Если ошибка отображается в specPower = 9.0, закодируйте ее

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

Или

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

Если проблема исчезнет, ​​это означает, что в функции pow, возможно, имеется ошибка недостаточного значения, связанная с показателем степени на этом графическом процессоре или драйвере.

Что можно сделать в качестве обходного пути, если предположить, что вычисление фрагмента шейдера выполняется внутри с помощью 16-битных операций с плавающей точкой Если я рассчитываю это право, вы должны сделать

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

Это может быть 32768 или 8192: я могу быть отключен на один бит или GPU может использовать более или менее точную точность. для pow (x, 9.0) зажим будет pow (max (temp, 0.3401975), 9.0). Значения X ниже этого значения приведут к снижению диапазона экспонент (от +15 до -14) 16-битных IEEE с плавающей запятой (опять же, я при условии, что это то, что использует графический процессор.)

из кода розетки ( 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));

Вам нужно будет рассчитать это на CPU и передать значение в униформе: у GPU, вероятно, будет та же проблема точности, если вы вычислите это прямо в шейдере.

Если вы можете это подтвердить, попробуйте связаться с командой драйверов графического процессора с примером исходного кода шейдера, который повторяет проблему, чтобы сообщить им точную версию драйвера, версию ОС и модель графического процессора. Исполняемый файл может помочь, но не отправляйте его по электронной почте. попасть в ловушку вируса / спама. Просто создайте его и сохраните копию на случай, если они попросят предварительно скомпилированный пример исполняемого файла. В качестве любезности дайте им возможность исправить это, прежде чем называть его публично. Это может быть просто ошибка оптимизации в их шейдерном компиляторе, которая может случиться только с вашим конкретным шейдером. Это может занять некоторое время (месяцы), чтобы связаться с ними, эти парни часто недоукомплектованы.

Стефан Хоккенхалл
источник