суббота, 5 декабря 2015 г.

Candide

Скоро у нас будет новый кандид:


Вместо стандартного рисования линий средствами OpenGL ES я решил расчитывать сглаженную линию в шейдере. Для этого вокруг каждой линии рисуется "конвертик"из triangle-strip и в шейдер передаются начало и конец отрезка и его толщина. В шейдере рассчитывается расстояние от фрагмента до центра линии, плюс добавлен суперсемплинг 16x для максимально сглаженного результата. Вся геометрия рисуется одним вызовом.

Сначала "конвертики" рисовались с включенным аддитивным блендингом, чтобы линии нормально накладывались друг на друга. Но это приводит к всеобщей распространнённой проблеме ярких "узелков", где сходится больше двух линий:


В итоге было решено задействовать расширение EXT_shader_framebuffer_fetch которое доступно начиная с iPhone 6. Благодаря заточенности железа под тайловый рендеринг, в шейдере вы можете получить цвет фрагмента из render target-а, что недоступно на десктопных графических картах в силу их не-тайловой архитектуры. Имея доступ к цвету в памяти, вы можете сложить его с цветом полученным в шейдере так, как вам удобно - получив по сути программируемый блендинг. Сделав так, чтобы суммарная яркость не превышала уже существующий порог, можно избавиться от ярких "узелков".

На FullHD экране телефона новый кандид выглядит очень здорово!

пятница, 20 ноября 2015 г.

Looksery Inc

Теперь я работаю в этом стартапе:


И у меня появился реальный шанс сесть на трактор!
:)

четверг, 8 октября 2015 г.

понедельник, 28 сентября 2015 г.

пятница, 25 сентября 2015 г.

Procedural Textures

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



diffuse + normalmap + phong lighting

Pixar's ToyBall

Вся мякотка в том, чтобы рассчитывать сглаженные переходы между границами разных цветов, иначе будет видна попиксельная лесенка. smoothstep, fwidth, frac и floor в помощь. Колдовать приходится много, но сглаженный результат того стоит.

Недостатком таких текстур является отсутствие фильтрации mip-уровней, из-за чего при удалении начинает сказываться point семплирование. Как решение можно написать вручную собственную фильтрацию с множеством процедурных выборок, как сделано в этом шейдере от Inigo Quilez. Врочем, писать свою фильтрацию придётся и в случае с обычными текстурами, т. к. железо делает её в fixed function на основе экранных деривативов, а при рей-трейсинге это нужно делать вручную.

понедельник, 14 сентября 2015 г.

Depth of Field Ray-Traced

Набросал за час демку рей-трейсинга Depth of Field эффекта. В сущности отличий мало от AA-метода, только лучи не параллельны друг другу, а сходятся в точке, где луч-centroid пересекается с фокальной плоскостью. Радиус Circle of Confusion можно легко варьировать.



Метод даёт правдивые результаты, но не отличается быстродействием. Нужно множество лучей (на скриншотах - 32 + jittering), чтобы снизить шум, т. е. сцена семплируется много раз.

Если у вас не простые сферки с фонгом, а что-то потяжелее, то в реал-тайме работать уже не сможет. В сущности, комбинация нескольких нечётких эффектов может замедлять рендеринг в геометрической прогрессии. Например, пиксель содержит пенумбру тени, которую считаем по 16 семплам. Но это для pinhole камеры. Если тот же самый пиксель нужно отобразить для камеры с большой апертурой, например с 32 семплами для DoF, то получится что к солнцу нужно пустить 32x16=512 лучей, и это при скромных начальных условиях. Для production quality рендера количество лучей будет измеряться тысячами.

понедельник, 7 сентября 2015 г.

AA Edge Detection

На выходных возился с оптимизацией оверсемплинга при рейтрейсинге. Идея старая: запускать множество лучей не в каждом пикселе, а только там, где наблюдается лестница алиасинга. Для этого, например, в deferred-renderer-e ищутся контуры объектов, а затем либо по stencil test либо по discard в шейдере запускается множество лучей только для тех пикселей, которые входят в контур. Вопрос как искать эти самые контуры? Путей несколько:

Для пробы я написал шейдер, который берёт depth в camera space (t на луче), определяет дельты между соседними пикселами и по threshold решает быть тут контуру или нет. Это не работает как нужно из-за того, что t меняется нелинейно в screen space, из-за этого вдалеке дельты рано или поздно становятся больше критического значения и маска заливает весь объект. Для корректной работы нужен linear z в screen space, Humus как-то писал на эту тему: A couple of notes about Z. Для этого нужно писать в depth z/w, который интерполируется растеризатором в screen space, тогда деривативы будут давать константный шаг, но в рейтрейсере так сделать не получится.

Другой вариант - брать косинус угла (dot) между соседними нормалями и если он больше некоего threshold то помечать здесь контур. Это неплохо работает, но с оговорками: например, нормали могут совпадать у геометрически разных объектов в некоторых участках изображения, тогда контур определён не будет. Или например в случае bump-mapping'а или похожих алгоритмов может давать "фальшивые" контуры. По-видимому, надёжный алгоритм должен быть многогранным и оперировать всей информацией, которая вносит вклад в создание discontinuities, как на рисунке вверху. Я решил что для стартового варианта лучше всего записывать ObjectID в отдельный канал, и если normal test ничего не дал, проверять контур по ID. В принципе на простых объектах сам ID-тест работает лучше чем тест нормалей, но предсказуемо будет давать ошибки на сложных объектах, части которых могут перекрываться на экране. Поэтому комбинация нескольких тестов тут практически обязательна.