Однажды я готовился к Ludum Dare и сделал простую игру, где использовал пиксельные шейдеры (других в движок Phaser не завезли).
Что такое шейдеры?
Шейдеры — это программы на си-подобном языке GLSL, которые выполняются на видеокарте. Есть два вида шейдеров, в этой статье речь идет про пиксельные (они же “фрагментные”, fragment shaders), которые очень грубо можно представить в таком виде:
color = pixelShader(x, y, ...other attributes)
Т.е. шейдер выполняется для каждого пикселя выводимого изображения, определяя или уточняя его цвет.
Вводную можно почитать на другой статье на хабре — https://habr.com/post/333002/
Потестировав, кинул ссылку другу, и получил от него вот такой скриншот с вопросом "а это нормально?"
Нет, это было ненормально. Посмотрев внимательно код шейдера, я обнаружил ошибку в вычислениях:
if (t < M) {
realColor = mix(color1,color2, pow(1. - t / R1, 0.5));
}
Т.к. константа R1 была меньше чем M, то в некоторых случаях в первом аргументе pow получалось число меньше нуля. Квадратный корень из отрицательного числа — штука загадочная, по крайней мере для стандарта GLSL. Мою видеокарту ничего не смутило, и она как-то выпуталась из этого положения (похоже, вернув из pow 0), а вот у друга она оказалась более разборчивой.
И тут я задумался: а могу ли я избежать таких проблем в будущем? От ошибок никто не застрахован, особенно таких, которые не воспроизводятся локально. Юнит-тесты на GLSL не напишешь. В то же время преобразования внутри шейдера довольно простые — умножения, деления, синусы, косинусы… Неужели нельзя отследить значения каждой переменной и убедиться, что ни при каких условиях не происходит выхода за допустимые границы значений?
Так я решил попробовать сделать статический анализ для GLSL. Что из этого получилось — можно прочитать под катом.
Сразу предупрежу: какого-то законченного продукта получить не удалось, только учебный прототип.
Читать полностью »