Рассеяние света в атмосфере в менее чем четырёх килобайтах

    Введение


    В этой краткой заметке будет рассказано о том, как устроена модель атмосферного рассеяния света в нашей последней 4к интре Appear by Jetlag, party-версия которой заняла почётное 12-е место в 4k intro compo на демопати Revision 2018 в апреле этого года.


    Cкачать бинарник бесплатно без смс можно здесь.


    Если, однако, у вас не Виндовс, или нет мощной современной видеокарты, то есть утешительный утупчик:



    Музыку к этой работе написал keen, используя 4klang. За мной же остался весь код и визуальный ряд.


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


    Скучная история, которую можно пропустить


    Работа над этой работой началась с осознания, что основная на-полный-рабочий-день работа не оставляет времени на работу над полноценной 4к работой — на дворе уже почти середина марта, до Ревижена остаётся пара-тройка недель.
    Остаётся лишь придумать что-нибудь достаточно простое для быстрого, на пару вечеров, компо-филлера. Делать очередной тупо реймарч — не уважать зрителя, поэтому я вспомнил, что несколько лет назад доводилось делать шейдер с рассеянием, и он был довольно простым, компактным и в то же время допустимо красивым, хотя и довольно медленным.


    В ходе недолгого обсуждения я настоял на своём, и мы решили остановиться на следующем: сделать ландшафт, залитый рассеянным светом, с закатом, облаками и сумеречными лучами (TIL как переводится выражение "god rays"). Для того, чтобы не задирать количество шагов по атмосфере до совсем неинтерактивных величин, придётся сильно дребезжать лучами (дворовый такой Монте-Карло метод), что породит видимый шум. Но не беда, если камеру двигать и сцену менять медленно и пустить эмбиент-трек, то можно безболезненно смешивать соседние кадры и темпорально сглаживать этот шум.


    Keen написал музыку довольно быстро — она была практически готова за две недели до Ревижена. Меня, однако, серьёзно подкосил грипп — со скорой и инфекционкой — поэтому я практически не начинал работать над шейдером вплоть до того момента, пока в более-менее как-то живом состоянии не попал на самолёт до Франкфурта. Прототип этой модели рассеяния был написан уже в воздухе.


    Party-версию интры мы уже на скорую руку собирали из песка и слюней на самой пати в течение нескольких остававшихся часов до дедлайна (и, наверное, парочки — после ;D), пока я параллельно отходил от гриппа, недосыпа, многочасовых перелётов, а так же постоянно отвлекался на участие в Shader showdown livecoding compo.


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


    В общем, 12-е место — это довольно щедро.


    Финальная версия, продемонстрированная выше, была сделана уже позднее и в более расслабленном режиме по 1-2 вечера в неделю в течение месяца. В общей сложности на работу ушло около 40-50 часов труда.


    Модель рассеяния


    (Примечание: я не занимаюсь программированием графики профессионально. Это — моё маленькое уютное хобби на хорошо, если сотню-другую очень расфокусированных под пивовино часов в год. Поэтому не нулевая вероятность того, что некоторые вещи ниже описаны и/или названы неправильно. Дяденьки, бейте!)


    Модель рассеяния позаимствована из статьи "High Performance Outdoor Light Scattering Using Epipolar Sampling" товарища Egor Yusov, опубликованной в книге GPU Pro 5, с полностью выкинутым эпиполярным семплингом.


    Физическая модель


    Фотоны солнышка бомбардируют атмосферу земли и взаимодействуют с частицами воздуха. Фотон может быть рассеян частицей, что влечёт за собой изменение направления фотона, или он может быть поглощён, что означает, что фотон потерян, а его энергия была преобразована в какую-то иную форму.
    Оба процесса вероятностные и зависят в частности от плотности частиц и энергии фотона (которая соответствует его цвету).


    На пальцах, "красные" фотоны имеют более низкую вероятность взаимодействия с воздухом, поэтому они преодолевают толщу атмосферы относительно нетронутыми.
    "Голубые", однако, имеют более высокую вероятность рассеяния, отчего они могут менять направление неоднократно и преодолевать значительные расстояния в атмосфере, прежде чем достигнут (или нет) наблюдателя.


    image


    Интересующие нас параметры взаимодействия света с воздухом следующие:


    • $\beta^s(x)$ — доля рассеянного света на единицу длины в точке $x$
    • $\beta^a(x)$ — доля поглощённого света на единицу длины в точке $x$
    • $\beta^e(x) = \beta^s(x) + \beta^a(x)$ — суммарная дола потерянного света на единицу длины в точке $x$
    • $p(\alpha)$ — угловое распределение рассеянного света, где $\alpha$ это угол между падающим и рассеянным лучом

    Предполагается, что воздух состоит из двух типов частиц, рассеяние на которых происходит независимо: молекулы (Рэлеевская модель) и аэрозоли (относительно крупные сферические частицы, Mie scattering в англоязычной литературе). Модели отличаются только разными значениями для параметров выше.


    Для обеих моделей считается, что плотность соответствующих частиц экспоненциально убывает с высотой: $\rho = \rho_0e^{-\frac{h}{H}}$, где $\rho_0$ — плотность на уровне моря. Коэффициенты $\beta$ пропорциональны $\rho$, и их значения ниже даны для уровня моря.


    Рэлеевская модель


    • $p_R(\alpha) = \frac{3}{16\pi}(1+\cos^2(\alpha))$ [Nishita et al. 93, Preetham et al. 99]
    • $\beta_R^a = 0$
    • $\mathbf{\beta_R^e} = \mathbf{\beta_R^s} = (5.8, 13.5, 33.1)_{rgb}10^{-6}m^{-1}$ [Riley et al. 04, Bruneton and Neyret 08]
    • $H_R = 7994m$ [Nishita et al. 93]

    Аэрозоли


    • $p_M(\alpha) = \frac{1}{4\pi}\frac{3(1-g^2)}{2(2+g^2)}\frac{1+\cos^2(\alpha)}{(1 + g^2 - 2g\cos(\alpha))^\frac{3}{2}}$ [Nisita et al. 93, Riley et al. 04], где $g = 0.76$ [Bruneton and Neyret 08]
    • $\beta_M^s = 2\cdot10^{-5}m^{-1}$ [Bruneton and Neyret 08]
    • $\beta_M^e = 1.1\beta_M^s$
    • $H_M = 1200m$ [Nishita et al. 93]

    Приближение одиночного рассеяния


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


    Свет, достигающий камеры, формируется следующими процессами в толще воздуха:


    • Врассеяние (TIL, что фиг узнаешь, как переводится in-scattering). Добавляется свет, испущенный солнцем, который вероятностным образом рассеивается на угол, соответствующий направлению на камеру.
    • Поглощение. Свет, уже летящий вдоль луча, поглощается воздухом.
    • Рассеяние. Свет, уже летящий вдоль луча, теряется на рассеяние в других направлениях.

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


    Этот подход изображен на следующем прекрасном изображении (я старался!):


    image


    Таким образом, количество света, которое должно быть зарегистрировано пикселем камеры в точке $O$ может быть рассчитано как сумма $\mathbf{L} = \mathbf{L_{in}} + \mathbf{L_{BO}}$, где $\mathbf{L_{in}}$ — врассеянный свет от солнца, а $\mathbf{L_{BO}}$ — количество света от точки $B$ объекта геометрии сцены, достигающего $O$.


    Свет геометрии


    $\mathbf{L_{BO}} = \mathbf{L_O} e ^ {-\mathbf{T}(B \rightarrow O)}$, где $\mathbf{L_O}$ — это свет, испущенный из точки $B$ в направлении камеры.


    $\mathbf{T}(B \rightarrow O)$ называется оптической толщиной среды между точками $B$ и $O$, и вычисляется следующим образом:


    $\mathbf{T}(B \rightarrow O) = \int_B^O(\beta_M^e(s) + \mathbf{\beta_R^e}(s))ds$


    Принимая во внимание, что члены $\beta$ состоят из константы уровня моря и переменной плотности, это выражение можно преобразовать до:


    $\mathbf{T}(B \rightarrow O) = \beta_M^e \cdot \int_B^O \rho_M(s) ds + \mathbf{\beta_R^e} \int_B^O \rho_R(s) ds = \bar{\mathbf{\beta}} \cdot \bar{T_\rho}(B \rightarrow O)$


    Обратите внимание, что я специально не раскрываю $\rho$, потому что мы будем их менять в дальнейшем при добавлении облаков. Также обращаю внимание на то, что $\beta$ — вектора RGB (по крайней мере $\mathbf{\beta_R}$ имеют разные значения для RGB-компонент, а $\beta_M$ — вектор просто для консистентности). Члены с $\rho$ под интегралом — скаляры.


    Солнечный свет


    Солнечный свет $\mathbf{L_{in}}$ рассчитывается интегрированием по всем точкам $P$ вдоль отрезка $OB$ и накоплением всего входящего солнечного света, рассеивающегося в сторону камеры и затухающего в толще $\mathbf{T}(P \rightarrow O)$.


    Количество солнечного света, достигающего точки $P$, вычисляется по аналогичной формуле $\mathbf{L_P} = \mathbf{L_{sun}}e^{-T(A \rightarrow P)}$, где $\mathbf{L_{sun}}$ — яркость солнца, а $A$ — точка, в которой луч из точки $P$ в направлении солнца $\vec{s}$ покидает атмосферу. Доля этого света, которая будет рассеяна в направлении камеры, составляет $\mathbf{L_P} \cdot (\mathbf{\beta_R^s}(s)p_R(\alpha) + \beta_M^s(s) p_M(\alpha))$.


    Итого получим:


    $\mathbf{L_{in}} = \int_B^O \mathbf{L_P}(s) \cdot (\beta_M^s(s) p_M(\alpha) + \mathbf{\beta_R^s}(s)p_R(\alpha)) \cdot e ^ {-\mathbf{T}(P(s) \rightarrow O)} ds$


    Можно заметить, что:


    • $\alpha$ является константой для каждого пикселя-луча камеры (считаем, что солнце бесконечно далеко и лучи от него параллельны)
    • Коэффициенты $\beta$ состоят и констант уровня моря и функций плотности $\rho(s)$
    • Функции $p(\alpha)$ имеют общие множители для обоих процессов рассеяния

    Это позволяет преобразовать выражение в:


    $\mathbf{L_{in}} = \mathbf{L_{sun}} (1+\cos^2(\alpha)) (\frac{\frac{1}{4\pi}\frac{3(1-g^2)}{2(2+g^2)}}{(1 + g^2 - 2g\cos(\alpha))^\frac{3}{2}}\beta_M^s \cdot \mathbf{I_M} + \frac{3}{16\pi} \mathbf{\beta_R^s} \cdot \mathbf{I_R})$


    где


    $\mathbf{I_M} = \int_B^O \rho_M(s) e^{-\mathbf{T}(A \rightarrow P(s)) - \mathbf{T}(P(s) \rightarrow O)} ds$


    $\mathbf{I_R} = \int_B^O \rho_R(s) e^{-\mathbf{T}(A \rightarrow P(s)) - \mathbf{T}(P(s) \rightarrow O)} ds$


    $I_M$ и $I_R$ отличаются только функциями плотности, их экспоненты при этом одинаковы.


    Эти интегралы никому так и не удалось вычислить аналитически, поэтому придётся считать численно, с помощью рэймарчинга (как, говорится в исходных публикациях, делать нельзя!).


    Численное интегрирование


    Из соображений размера и лени считать будем как можно более тупо: $\int_A^B f(x) dx \approx \frac{\left |B - A \right |}{N} \sum_{i=0}^N f(A + i \cdot \frac{\vec{B - A}}{N})$


    Маршировка по лучу будет производиться в противоположном потоку света направлении: от точки камеры $O$ до пересечения луча с геометрией $B$. Отрезок $O \rightarrow B$ делится на $N$ шагов.


    Прежде чем запустить марш, инициализируем переменные:


    • vec2 (две отдельные компоненты, для Релеевского и аэрозольного рассеяний) общая накопленная оптическая толщина $\mathbf{T_\rho}(P(s) \rightarrow O)$
    • vec3 (RGB) $\mathbf{I_M}$, $\mathbf{I_R}$

    Далее для точки $P_i$ каждого шага между $O$ и $B$:


    1. Испутим луч $\vec{s}$ в направлении солнца и получим точку $A_i$ выхода этого луча из атмосферы.
    2. Вычислим толщину $\mathbf{T}(A \rightarrow P_i)$, сначала рассчитав $\int_A^{P_i}\rho_M(s)ds$ и $\int_A^{P_i}\rho_R(s)ds$ с помощью такого же рэймарчинга (с количеством шагов M), а затем перемножим полученные слагаемые с соответствующими константами $\beta_M^e$ и $\mathbf{\beta_R^e}$.
    3. Вычислим толщину $\mathbf{T_\rho}(P_i \rightarrow O) = \mathbf{T_\rho}(P_{i-1} \rightarrow O) + \rho_i(s) \cdot ds$
    4. Аккумулируем $\mathbf{I_R}$ и $\mathbf{I_M}$, используя эти значения

    Финальный цвет после рэймарчинга вычисляется суммой слагаемых:


    1. Слагаемое $\mathbf{L_{BO}}$ получить тривиально: переменная, содержавшая $\mathbf{T_\rho}(P_i \rightarrow O)$ содержит значение $\mathbf{T_\rho}(B \rightarrow O)$, поскольку $P_i$ достигла $B$.
    2. Перемножением $\mathbf{I_R}$ и $\mathbf{I_M}$ на соответствующие константы и сложением результата вычисляется $\mathbf{L_{in}}$

    Шейдеры


    Простое рассеяние без никто


    Немного причёсанные и откомментированные исходники рассеяния, взятые (почти) напрямую из самой интры:


    const float R0 = 6360e3; // Радиус поверхности земли
    const float Ra = 6380e3; // Радиус верхней границы атмосферы
    const vec3 bR = vec3(58e-7, 135e-7, 331e-7); // Рэлеевский коэффициент рассеяния
    const vec3 bMs = vec3(2e-5); // Аэрозольный коэффициент рассеяния
    const vec3 bMe = bMs * 1.1;
    const float I = 10.; // Яркость солнца
    const vec3 C = vec3(0., -R0, 0.); // Координаты центра земли, точка (0, 0, 0) находится на поверхности
    
    // Функция плотностей
    // Возвращает vec2(rho_rayleigh, rho_mie)
    vec2 densitiesRM(vec3 p) {
        float h = max(0., length(p - C) - R0); // Высота от поверхности
        return vec2(exp(-h/8e3), exp(-h/12e2));
    }
    
    // Пересечение луча и сферы, используется для вычисления расстояния покидания лучом атмосферы
    float escape(vec3 p, vec3 d, float R) {
        vec3 v = p - C;
        float b = dot(v, d);
        float det = b * b - dot(v, v) + R*R;
        if (det < 0.) return -1.;
        det = sqrt(det);
        float t1 = -b - det, t2 = -b + det;
        return (t1 >= 0.) ? t1 : t2;
    }
    
    // Вычисление интеграла плотности оптической глубины для отрезка длины `L` из точки `p` в направлении `d`
    // Интегрирует за `steps` шагов
    // Возвращает vec2(depth_int_rayleigh, depth_int_mie)
    vec2 scatterDepthInt(vec3 o, vec3 d, float L, float steps) {
        vec2 depthRMs = vec2(0.);
        L /= steps; d *= L;
        for (float i = 0.; i < steps; ++i)
            depthRMs += densitiesRM(o + d * i);
    
        return depthRMs * L;
    }
    
    // Глобальные переменные (в основном -- из соображений размера)
    vec2 totalDepthRM;
    vec3 I_R, I_M;
    
    // Направление на солнце
    vec3 sundir;
    
    // Рассчитать количество солнечного света, рассеиваемого в направлении `-d` отрезка длиной `L` из точки `o` в направлении `d`.
    // `steps` -- количество шагов интегрирования
    void scatterIn(vec3 o, vec3 d, float L, float steps) {
        L /= steps; d *= L;
    
        // Из точки O в B
        for (float i = 0.; i < steps; ++i) {
    
            // P_i
            vec3 p = o + d * i;
    
            vec2 dRM = densitiesRM(p) * L;
    
            // Накопление T(P_i -> O)
            totalDepthRM += dRM;
    
            // Вычисление суммы оптической глубины T(P_i ->O) + T(A -> P_i)
            // scatterDepthInt() вычисляет скалярную часть T(A -> P_i)
            vec2 depthRMsum = totalDepthRM + scatterDepthInt(p, sundir, escape(p, sundir, Ra), 4.);
    
            vec3 A = exp(-bR * depthRMsum.x - bMe * depthRMsum.y);
    
            I_R += A * dRM.x;
            I_M += A * dRM.y;
        }
    }
    
    // Готовая функция рассеяния
    // O = o -- начальная точка
    // B = o + d * L -- конечная точка
    // Lo -- цвет геометрии в точке B
    vec3 scatter(vec3 o, vec3 d, float L, vec3 Lo) {
        totalDepthRM = vec2(0.);
        I_R = I_M = vec3(0.);
    
        // Вычисление T(P -> O) and I_M and I_R
        scatterIn(o, d, L, 16.);
    
        // mu = cos(alpha)
        float mu = dot(d, sundir);
    
        // Затухание цвета геометрии сцены
        return Lo * exp(-bR * totalDepthRM.x - bMe * totalDepthRM.y)
    
        // Солнечный свет
            + I * (1. + mu * mu) * (
                I_R * bR * .0597 +
                I_M * bMs * .0196 / pow(1.58 - 1.52 * mu, 1.5));
    }

    Зазыкать на шейдертое


    Облака


    Неплохо, но такую картинку можно было бы также получить гораздо проще каким-нибудь хитрым нагромождением градиентов.


    Обманным путём же получить облака и god rays значительно сложнее. Давайте добавим.


    Идея заключается в приближении облаков аэрозолями и модификации только функции плотностей densitiesRM(). Это может быть не настолько физически корректно, как хотелось бы (понятия не имею, как на самом деле приближается рассеяние света в облаках в компьютерной графике).


    // Верхняя и нижняя границы атмосферы
    const float low = 1e3, hi = 25e2;
    
    // vec4 noise24(vec2 v) -- просто читает значения из шумовой текстуры
    // float t -- время
    
    float noise31(vec3 v) {
        return (noise24(v.xz).x + noise24(v.yx).y) * .5;
    }
    
    vec2 densitiesRM(vec3 p) {
        float h = max(0., length(p - C) - R0);
        vec2 retRM = vec2(exp(-h/8e3), exp(-h/12e2) * 8.);
    
        // Облака ограничены высотой (оптимизация)
        if (low < h && h < hi) {
            vec3 v = 15e-4 * (p + t * vec3(-90., 0., 80.));
    
            // Вся эта мешанина <s>аккуратно</s> написана от балды с одной целью: чтобы интра выглядела приемлемо
            retRM.y +=
                250. *
                step(v.z, 38.) *
                smoothstep(low, low + 1e2, h) *
                smoothstep(hi, hi - 1e3, h) *
                smoothstep(.5, .55, // ключевая часть: многооктавный шум
                    .75 * noise31(v)
                    + .125 * noise31(v*4. + t)
                    + .0625 * noise31(v*9.)
                    + .0625 * noise31(v*17.)-.1
                );
        }
    
        return retRM;
    }

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


    Решения костыли, которыми подтыкается интра:


    • Самые неприятные артефакты на горизонте прячутся за горами
    • Облака добавляются только вблизи камеры
    • Добавляется Монте-Карловщина, каждый маршируемый луч сдвигается на случайное смещение: for (float i = pixel_random.w; i < steps; ++i). Это добавляет тот самый шум, который приходится темпорально сглаживать смешиванием подряд идущих кадров.
    • Увеличивается количество шагов для зон, требующих большего количества деталей (например, слой с облаками). Именно для этого сделано такое нелепое разделение функций на scatterImpl() и scatterDepthInt():


      // В цикле функции scatterIn()
          vec2 depthRMsum = totalDepthRM;
          float l = max(0., escape(p, sundir, R0 + hi));
          if (l > 0.) // под облаками 16 шагов
              depthRMsum += scatterDepthInt(p, sundir, l, 16.);
          // над облаками достаточно и 4-х
          depthRMsum += scatterDepthInt(p + sundir * l, sundir, escape(p, sundir, Ra), 4.);

      // в функции scatter()
          // ближайшие 10км получают больше шагов
          float l = 10e3;
          if (L < l)
              scatterIn(o, d, L, 16.);
          else {
              scatterIn(o, d, l, 32.);
      
              // 8 шагов -- достаточно для дальних расстояний
              scatterIn(o+d*l, d, L-l, 8.);
          }


    Совмещение с геометрией сцены


    В результате традиционного рэймарчинга функций расстояния и затенения уже получено расстояние L до точки B и цвет пикселя Lo. Эти значения просто подставляются в функцию scatter(). Если луч не упёрся в геометрию и покинул сцену, тогда цвет Lo нулевой, а L рассчитывается с помощью escape() — считается, что луч покинул атмосферу.


    Вроде всё.


    … На самом деле конечно не всё. Довольно большая боль теперь притереть все части друг к другу так, чтобы оно в целом выглядело правдоподобно. Просто куча возни с подкручиванием параметров, геометрии сцены, шумовых функций, траектории и ракурса камеры. Боюсь, у меня здесь нет хороших советов, кроме как рекомендации много часов итерировать и биться головой о стену.


    Минификация


    После обработки shader minifier'ом конечный шейдерный код рассеяния имеет размер около 1500 байт. Crinkler ужимает его до ~700 байт, что составляет примерно 30% всего шейдерного кода.


    Выведение


    Я не умею в компьютерную графику.

    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 33
    • 0
      Больше похоже на гладь воды, по крайней мере в начале видео.
    • +6
      Вот что в универе на первом курсе надо показывать. И рассказывать что вы сейчас со всем энтузиазмом упрётесь но без теории таких эффектов не сделаете. А то ходят анекдоты про то как интеграл два раза в жизни пригодился, один раз шляпу достать из лужи а второй канализацию прочистить.
      • –1
        второй раз вроде он пригодился, чтобы пистолет достать из деревенского сортира?
      • 0

        Два вопроса появились после просмотра видео демки. Замечал такие косяки и в других демках:
        На 3:30 камера меняет движение резко. Разве сложно сделать движение с плавным ускорением?
        Для уменьшения шума кадры смешиваются банально накладывась друг на друга, с образованием размытия в движении. Но, если известна разница позиций камеры и координаты каждого пикселя, то то можно посчитать смещённые позиции пикселей.
        Вроде простые же вещи.

        • +1

          Демка, к сожалению, показывает только черный экран.

          • 0
            Это прискорбно.
            Какая видеокарта? Звук идёт? Не падает?

            Уже один раз жаловались, что не работало на AMD карточке, но у меня нет возможности отладить на такой системе. Примечательно, что под линуксом на AMD всё работает (но там, конечно, совсем другой стек драйверов).
            • 0
              У меня на AMD тоже не работает — переключается разрешение экрана, проигрывается кусок звука/шума и дальше все пропадает, как будто произошел «тихий» креш приложения.
              • 0
                Тихих крешей этого прекрасного приложения не наблюдал ни разу, только довольно громкие — если упало, то упало со всей сопутствующей атрибутикой.
                Про пропадающую музыку могу предположить, что процессору не хватает мощности синтезировать её на лету — 4klang довольно вычислительноёмкий и синтезирует музыку в отдельном треде (запускается емнип до того, как вообще инциализируется окно, чтобы хоть немного забуферизовать музыку). Проигрывается музыка просто «вот этот буфер в колонки пожалуйста спасибо» в надежде, что курсор проигрывания не будет догонять курсор записи.
                Расчёт на то, что CPU и GPU достаточно мощные, чтобы прожевать.
                Фундаментально ничего с этим поделать нельзя, кроме как разве что добавить sleep, или же синтезировать весь трек перед тем, как запустить видео. Но это даст тишину и черный экран в начале дольше, чем сама интра, что дискомфорт.

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

                  Как и говорил собеседник выше — Windows 10 && AMD 7850 2Gb, при запуске меняется разрешение экрана на 720p && 59Гц, экран чёрный, играет музыка. После закрытия окна, то музыка ещё звучала, убил процесс, залез в настройки и восстановил настройки дисплея. Говорите «что под линуксом на AMD всё работает», сейчас проверим.

                  • 0
                    Спасибо за информацию. Это не объясняет чёрный экран, но боюсь, что 7850 слишком слабая карточка — едва ли фпс будет выше 10 даже на самых лёгких сценах.

                    Под линуксом надо из исходников собирать самостоятельно, при этом отключая музыку (4klang падает, и не удалось за разумное время выяснить, почему — поэтому и нет релиза под линукс).
                    • 0

                      Попробуйте отладочные бинарники отсюда, пожалуйста: http://yolp.omgwtf.ru/appear_debug.zip
                      Открыл баг на гитхабе по этому поводу: https://github.com/w23/jetlag_appear/issues/1

                • 0
                  Не работает на 1060 с мультимониторной конфигурацией. Моргает 2-мя дисплеями и вываливается обратно в w7x64
                  • 0
                    Интересное наблюдение, спасибо! Замечу, что я не замечал проблем с двумя мониторами. Скорее всего здесь другая проблема — падает 4кланг, это более похоже на описываемые симптомы.

                    Пора уже, наверное, issue заводить на гитхабе для этого всего. Вряд ли починю для этой конкретной работы, но по крайней мере есть шанс, что в следующих проблем будет меньше.
                    • 0
                      Тут может быть зацепка у меня основной монитор правый.
                      В нижнем ряду 1600*1200 + 1920*1200.
                      А вообще сейчас делют демки без 4 гигабайтов API ну как под ДОС?
                      • 0
                        Не думаю, что расположение мониторов как-либо влияет. По-хорошему, основной монитор должен перейти в 1280x720 и развернуть окно на весь экран. Апи-вызов для этого используется очень простой и старый, который про мониторы вообще не знает.

                        Под ДОС до сих пор делают, да. В основном крохотулечные, 256 байт и меньше.
                        • 0
                          Аналогично 2 монитора, оба фуллХД карточка 1060 6gb проц i7, windows 7
                          Не заработало.
                          • 0

                            Попробуйте отладочные бинарники отсюда, пожалуйста: http://yolp.omgwtf.ru/appear_debug.zip
                            Открыл баг на гитхабе по этому поводу: https://github.com/w23/jetlag_appear/issues/1

                            • 0
                              Да все 3 запустились в оконном режиме на основном мониторе
                              • 0
                                Обновил архив, попробуйте четвёртый файл, без nofs.
                                • 0
                                  Запустился на основном мониторе в фуллскрине.
                                  • 0
                                    Хммм. Добавил ещё один файл, appear_asm_safer.exe. Попробуйте, пожалуйста.
                                    • 0
                                      не, это не пошло
                                      • 0
                                        Вот это неожиданно, спасибо. Буду разбираться дальше.
              • 0
                Замечания корректные. Однако, к сожалению, все эти простые вещи упираются в небесконечный размер и небесконечное время разработки. Для того, чтобы уложиться в 4кб, туда было закопано часов, наверное, 10 на то, чтобы вырезать нетривиальный объём существующей функциональности, и упростить многое в ущерб качеству.
                • 0

                  Ясно, однако это оставляет ощущение несовершенства, увы.

              • 0
                Я сталкивался с реализацией цветов неба через через эти модели. Хочется заметить, что у такого подхода есть несколько недостатков:
                1) удается более-менее точно вычислить цвета в Земной атмосфере, но стоит поменять планету — там уже действуют другие формулы, и недостаточно просто подкрутить плотность и высоту атмосферы, как может показаться на первый взгляд.
                2) цвета все равно будут казаться не до конца натуральными как не увеличивай количество итераций. если использовать шлем виртуальной реальности, то в некоторые моменты (закат и рассвет) цвета уже на грани преемственности.

                Ну и у меня получилось что-то такое:
                Скрытый текст
                image
                • 0

                  Для других планет другие коэффициенты, которые кстати можно вывести через формулы для Релеевского рассеяния. У меня для Земли рассчитанные коэффициенты почти сошлись с константами Nishita&co. Зависит от плотности, температуры, концентрации молекул и коэффициента преломления насколько я помню. А не до конца натуральные цвета на этом рендере из-за того, что после рассчетов нужно цвета проинтегрировать по кривым чувствительности глаза(cie1931 color matching function), получается гораздо ближе к реальности. Но точный, розовый вместо оранжевого рассвет выходит только если добавить рассеяние второго порядка.

                  • 0
                    Я пробовал добиться натуральных цветов неба Марса, вынеся все коэффициенты в реалтайм и пытаясь их выкручивать в разумных пределах до получения нужной картинки — это оказалось просто невозможно. Реалистичным может быть дневное небо, но закаты и рассветы будут корявые, либо наоборот. Начал читать — там оказалось все дело в большом количестве пыли в атмосфере и в другом составе газа. Про цвета была мысль, что нужен cie, но все равно интуитивно кажется, что это не поможет, и тогда меня, к тому же, не устраивала идея тратить столько времени на реализацию неба, с учетом того что сам подход еще берет значительную часть шейдерного времени, которое было на вес золота, в то время как стандартная дешевая небесная заглушка движка по яркости красок просто убивала мое «честное натуральное» небо.
                    • 0

                      Другой состав атмосферы = другие коэффициенты, пыль = нужна другая фазовая функция для поглощения и рассеивания по Mie плюс видимо надо привлекать геометрическую оптику, так как я читал, что цвет пыли сильно влияет, тоесть реалтайм под большим вопросом. С cie — сильно помогает, и у Брунетона есть отличное приближение с предрассчетом, в шейдере остается домножить цвет на коэффициент. А в общем вычисления ресурсоемкие и в играх сейчас только предрассчет для заданных параметров, загнанный в текстуры конечно.

                • +1

                  Я внутренний интеграл (T) за два года раздумий по вечерам таки придумал как решить аналитически, слегка видоизменив подынтегральное выражение так, чтоб погрешность в масштабах планеты/толщины атмосферы была очень маленькой. Правда возникла функция erf(), очень плохо себя ведущая с float, но и её удалось ужать до пары десятков инструкций, в зависимости от желаемой точности.

                  • +1
                    Даже не знаю, что такое erf :D.
                    Интуиция подсказывает (возможно, ошибочно), что в оффлйне можно было бы построить N-мерные графики/таблицы конечных результатов, а потом искать функцию (например, в рядах Тейлора, но не обязательно), приближающую их с достаточной точностью. В итоге там наверняка будут десятки, если не сотни, параметров и ещё больше операций, но по крайней мере они будут регулярные.
                    Это, конечно, без облаков.
                  • 0
                    «Я не умею в компьютерную графику.» — месье скромничает, однако. Весьма достойно.
                    Единственное «НО» — это где-же в предрассветные часы столько людей уже на работу встают? Горящие окна реально режут глаз… А если это север, то где снег? :)

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое