Краткий курс компьютерной графики, аддендум: лечим по фотографии

Год назад я опубликовал цикл статей, имевший целью популяризацию графического программирования. Много воды утекло с тех пор, появилась англоязычная версия цикла, прошедшая некоторую полировку по сравнению с оригинальным. За этот год мне написало несколько сотен человек, причём многие просили помочь отладить их код.

Я предлагаю вам поиграть в игру: я даю только картинку, на которой видна проблема, попробуйте понять, в каком месте кода нужно искать баг, что именно сломано. Я в эту игру играю ежедневно, досконально смотреть сотни версий рендера у меня нет никакой возможности, поэтому я, как заправский экстрасенс, лечу по фотографии. Зачастую успешно.

Абсолютно все картинки сгенерированы не мной, я лишь собрал самые типичные баги. Настоящая людская боль перед вашими глазами, ко мне, понятно, обращаются (особенно по почте) только после того, как не могут сами найти баг в разумное время.

Вот первый баг для затравки, слева битый рендер, справа то, что ожидалось:

d3eccde4f31474b9ade4b264477e8579.jpg

Скрытый текст

Наипервейший хинт: очень круглая форма. Сразу намекает на то, что вместо массива вершин человек загрузил массив нормальных векторов, точнее, перепутал строки «v x y z» и «vn x y z» из .obj файла

b8040fde592d3a0fa6b54aaf1e84fe58.png

Скрытый текст

Треугольники плохо стыкуются друг с другом. В wavefront .obj файлах индексы массивов начинаются с единицы, а не с нуля, человек забыл отнять единицу после прочтения файл модели.

0b22e205331a5a10ea33558c5a26b09b.jpg

Скрытый текст

Обратите внимание, что все треугольники на их геометрическом месте, а проблемные места текстуры образуют полные кольца вокруг одной вершины, что намекает на то, что периодически одна вершина битая. Так и есть, был плавающий баг в прочтении текстурных индексов.

fb0fc1d445a17a1ee26522366b1c9eaa.png

Скрытый текст

На картинке тонировка Гуро, заполнение треугольника явно сделано при помощи сканирующей линии, когда вершины сортируются по их игрек-координате, а затем горизонтальными линиями заметается треугольник. Порядок вершин изменился, а вот цвет вершин поменять местами забыли. Очень частый баг.

c093b512715cfa08d45452dc20b52289.jpg

Скрытый текст

На картинке битый z-буфер. Практически то же самое, что и предыдущий баг, только забыли поменять z-координату вершин при сортировке.

e6ec3de24cbf32e8ce08bfb0dfbb20cf.jpg

Скрытый текст

И опять тот же самый баг, но чуть-чуть в другом месте: при заметании треугольника он для удобства делится на две части, каждая горизонтальная линия рисуется слева направо. Меняя местами вершины линии, забыли про текстурные координаты.

6c84c93b36521f790be576abc2a781e2.png

Скрытый текст

Ошибки округления, игрек сканирующей линии явно вычисляется интерполяцией, вместо того, чтобы по нему непосредственно сделать заметающий цикл.

40886075a4e126410befd93036d421ac.png

Скрытый текст

Опять ошибки округления, экранные вершины треугольника не были округлены перед растеризацией.

Тут полезно знать, что человек пытался сделать блестящую поверхность (specular map):

2185f09af8e7142b214ee17747936466.jpg

Скрытый текст

Неважно что в степени ноль даёт единицу…

610b5c57b3059c7679af64e5ddab995e.jpg

Скрытый текст

Растеризация треугольника явно сделана через барицентрические координаты, и их неплохо бы сдвинуть по кругу при вычислении текстурных координат.

5065f55d3585088c84b1c35e5b2c120e.jpg

Скрытый текст

Текстуру явно нужно перевернуть по вертикали

Ха, эта картинка очень похожа на предыдущую, но баг совсем другой (почему?)

a69c08a146e1815944c96f4242d126c7.jpg

Скрытый текст

Вместо текстурных координат человек просто использовал xy текущего пикселя для поиска цвета в текстуре…

Здесь и далее картинки до и после отладки:

fed52ee5aabeba11400cc2916f20efa8.png

Скрытый текст

Направление света было использовано для отсечения задних граней. А надо было бы направление взгляда…

Попытка применить карту нормалей:

68d2b95b7df78ac40d164339c1f0d06a.jpg

Скрытый текст

Ну что, цвет, живущий в RGB [0,255]^3 был сконвертирован в XYZ, живущий в [0,2]^3, а не в [-1,1]^3.

c62be15fd1bf48f4e58a6ff4d9e9d59e.png

Скрытый текст

При плоской тонировке (flat shading) вектор света и/или вектор нормали не был нормализован, в итоге попытка отрисовать цвета со значением более 255, переполнение unsigned char

352d530a0efbce5fe453983d78b2584e.png

Скрытый текст

Явно опять переполнение, но на сей раз отрицательные значения. Человек забыл, что скалярное произведение бывает отрицательным. Картинка справа — это fabs (интенсивности), предыдущая картинка могла бы быть получена, если бы вместо fabs мы использовали обычный clamp

Have fun!

© Habrahabr.ru