Прогаем с кенгурой
229 subscribers
190 photos
190 videos
17 files
156 links
Сидим с кенгурой под горой, запекаем текстуры...

{ программирование, процедурная генерация, графика, аудио, образование и немного про специфичные игры }

https://gostrobrod.dev
Download Telegram
Дальше пойдут искажения на свертках: расфокусы, повороты линз, боке, продольные абберации и прочее. И это мы еще не довели наше изображение до сенсора.

Вообще, заметьте, что понимая принцип работы эффекта, нам не нужно воспроизводить физически верную симуляцию и персчитывать рейтрейсинг, а достаточно упрощенной модели, которая позволит достичь довольно близкого результата.
5
https://www.makingsoftware.com/chapters/color-spaces-models-and-gamuts

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

Как я уже говорил, следующим этапом за геометрией идет PSF (Point Spread Function), который, хоть и отражает несколько разных искажений

- дефокус,
- продольная цветовая абберация,
- скос линзы,
- размытие к краю,
- кома,
- астигматизм,
- жировые полосы/царапины,

сводится к свертке с ядром, отражающим все перечисленные эффекты - разбить на части не получится правильным образом.

Тем не менее, я буду рассказывать про каждый по отдельности, а в конце соберу в один эффект.
4
Небольшое отступление:

Есть два эффекта, которые применить к обычному видео проблематично (возможно, нейронка поможет):
1 - Глубина резкости (если есть карта глубин для каждого кадра, то, в принципе, возможно)
2 - Искажения проекции на разных фокусных расстояния

Так как их нормальным путем реализовать не получится, то и рассказывать я о них не буду, но если интересна физика/геометрия процесса - напишите - распишу на пальцах
5
Media is too big
VIEW IN TELEGRAM
Дефокус

Размытие объекта на картинке из-за непопадания его проекции в фокус.

В идеале - именно объекта, а не всей картинке - так будет физически вернее. Но для этого нам нужна карта глубин, коей как правило, нет, поэтому будем делать просто размытие всей картинки.
3🔥1
Media is too big
VIEW IN TELEGRAM
Принцип:

Когда объект уходит из фокуса, то лучи от него не сходятся в точку на плоскости сенсора, где они должны быть, а в круг, что приводит к размытию картинки.

Причем в зависимости от расстояния до плоскости резкости размытие должно быть больше (но мы упростим и будем делать везде одинаковое).

То есть радиус диска ядра свертки зависит от диафрагмы, расстояния до плоскости резкости и от шага пикселя/зерна на сенсоре
2🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Алгоритм:

1. Можно пойти простым путем и взять MPSImageGaussianBlur - это даже будет выглядеть правдоподобно на малых значениях размытия, но неестественно на больших. Зато будет очень быстро
2. Можно пойти сложным путем и гонять свертку диском. Честно это делать супер дорого, поэтому вместо этого ограничиваются псевдослучайными равномерными сэмплами по нему (иначе вам придется гонять террабайты в секунду - видюха будет не рада такому). Зато у вас будет +/- красивое боке, кастомные формы диафрагмы. Ну и остальные эффекты требуют кастомной свертки

На видео сравнение, как выглядит гаусс и диск (разница, думаю, очевидна)
🔥41
Media is too big
VIEW IN TELEGRAM
Сечение пучка - боке принимает форму диафргамы.

Принцип
По сути ядро свертки - это проекция формы диафрагмы, соответственно, в зависимотсти от числа и формы лепестков вы будете получать разные фигуры на боке. Плюс чем сильнее закрыта диафргма, тем четче эта форма будет. Правда при этом у вас вырастет глубина резкости, но мы ее не имитируем.
3
Алгоритм
Тут по сути мы модифицируем форму ядра свретки, отбрасывая точки за пределами формы чем-нибудь типа

// Regular n-gon aperture test (inscribed in unit circle). blades<3 → circle (always inside).
inline bool insideAperture(float2 d, int blades) {
if (blades < 3) return true; // circle
float N = float(blades);
// polar angle
float a = atan2(d.y, d.x);
// sector size and half-sector
float sector = 2.0f * M_PI_F / N;
float halfSec = 0.5f * sector;
// wrap angle to [0, sector)
float m = fmod(a + M_PI_F, sector);
if (m < 0.0f) m += sector;
// offset to nearest edge normal (in [-half, half])
float delta = m - halfSec;
// allowed radius along this direction for polygon with circumradius=1
float rAllowed = cos(M_PI_F / N) / max(cos(delta), 1e-4f);
float r = length(d);
return r <= rAllowed;
}


Что можно еще "поломать":
- сделать скругление маски через SDF например
- сделать искажение формы, например неоднородным масштабом и поворотом
- исказить один из секторов лепестков
- сделать любую форму в принципе
🔥3
Media is too big
VIEW IN TELEGRAM
Вот эти вот звездочки - это дифракция

Принцип:

Возникает она тем сильнее, чем сильнее у нас закрыта диафрагма. Число лучей зависит от числа лепестков, а вид - от их формы.

Как видно из названия, основано искажение на дифракции: в нашем случае - огибании грани лепестка, что даёт вдоль её нормали усиления, формируя направленные лучи.
Для круга симметрия полная - направленных пиков нет, только свечение или кольца (их в примере нет, но, думаю, вы их видели на фото и глазами: когда на фонари таращились).
5
Алгоритм:

Тут мы не трогаем свертку, а делаем блюр вдоль каждого луча (перпендикулярно лепестку). При этом каждую прочитанную точку мы проверяем на яркость и, если она недостаточная, игнорируем ее вклад. Таким образом "звездочки" будут только от якрих источников света.

Можно в принципе сделать нелинейную функцию, чтобы ближе к источнику света луч звездочки был более ярким, чем по мере удаления.

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

Проблема этого эффекта в том, что, если его считать честно, он выходит довольно тяжелым, поэтому можно делать в низком качестве на первью и честно пересчитывать для экспорта.

Кстати, пришла идея как можно ускорить, сделав в несколько проходов, но потребуется сильно больше памяти:
1 - Вытаскиваем в одтельную текстуру меньшего размера (не более чем в два раза) яркие пиксели - сразу уменьшаем нагрузку на пересчет яркости
2 - Делаем несколько копий, повернутых в нужных направлениях
3 - По каждой пробегаем одномерным ядром (вертикальным или горизонтальным)
4 - Поворачиваем обратно
5 - Компилируем поверх исходной картинки или в еще одну и ее подблюриваем гаусом, а потом накладываем на исходную

Не факт, что это лучшее решение, но что пришло в голову быстро
🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Астигматизм

Немного отличается от того, что у вас, но суть +/- та же - изображение смазывается.

Есть два вида:
1 - тангенциальный (вдоль радиуса)
2 - радиальный (он же саггитальный) - вдоль окружности

Принцип:
Как мы уже знаем, идеально все работает только вдоль оптической оси, вне ее лучи фокусируются как правило вне соответствующей оси (если провести от исходной точки через центр условной линзы) из-за чего объекты так или иначе искажаются (вспоминаем геометрические искажения) и становятся смазанными.

Можно было бы копнуть глубже и рассказать про S и T фокусы, но я тут про общие принципы и реализацию рассказываю, а не школьный курс физики)))
4
This media is not supported in your browser
VIEW IN TELEGRAM
Алгоритм

Cчитаем параметры пространства, и когда считаем точки для свертки - применяем его


struct AstigCtx {
float2 e_r;
float2 e_t;
float s_r;
float s_t;
};

struct Parameters {
float2 invSize;
float2 opticalCenter;
float astigK;
};

inline AstigCtx astigSetup(float2 uv, Parameters params) {
AstigCtx C;
float ar = params.invSize.y / params.invSize.x;
float2 df = uv - params.opticalCenter;
float2 dfIso= float2(df.x * ar, df.y);
float rhoN = clamp(length(dfIso) * 2.0f, 0.0f, 1.0f);

float2 rIso = (length(dfIso) > 1e-6f) ? normalize(dfIso) : float2(1.0f, 0.0f);
C.e_r = normalize(float2(rIso.x / ar, rIso.y));
C.e_t = float2(-C.e_r.y, C.e_r.x);

float mag = fabs(params.astigK) * (rhoN * rhoN);
float a = 1.0f + mag;
C.s_r = (params.astigK >= 0.0f) ? (1.0f / a) : a;
C.s_t = (params.astigK >= 0.0f) ? a : (1.0f / a);
return C;
}

inline float2 astigApply(float2 d, AstigCtx C) {
float dr = dot(d, C.e_r);
float dt = dot(d, C.e_t);
return dr * C.s_r * C.e_r + dt * C.s_t * C.e_t;
}
3👏1
Из искажений входной оптики осталось всего ничего:
- кома
- продольная цветовая абберация - засветы - виньетка на сенсоре Потом пойдет интереснее. Пока можете выбрать, что интереснее:
Anonymous Poll
60%
пленочная камера
30%
ТВ трубка
10%
ПЗС матрица
🔥2
Разбираюсь с build123d, чтобы процедурно генерить ракушки
🔥2
У меня на второй работе происходит жесткий церебральный коитус с экспортом и женитьбой разных кодеков на разных андроидофонах, поэтому пока посты про видео на паузе.

Вот зато ракушку сделал в перерыве на build123d
🤗4🤓1
😁3😢3🤔2
This media is not supported in your browser
VIEW IN TELEGRAM
Кома

Смещение пучка из центра пятна боке - получаются свеого рода кометы.
👍1
Принцип

Из-за неидеальности оптики вне центральной оптической оси лучи фокусируются неравномерно (см на картинке).

Соотвественно, чем дальше от оси, тем сильнее эффект.
👍1