Использование LUT для ускорения работы шейдера для мобильных устройств

10

Я пытаюсь заставить этот шейдер работать на действительно старом iDevice, а также на Android.

Даже когда я уменьшаю код до 2 синусоидальных функций на фрагмент, шейдер работает со скоростью около 20 кадров в секунду.

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

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

Во всяком случае, у меня есть две дилеммы:

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

    func range(float angle)
    {
       float temp = angle
       while (temp > 360) {temp -= 360;}
       while (temp < 0)   {temp += 360;}
       return temp;
    }
    

    Однако GLSL не допускает циклы while или рекурсивные функции.

j.doe
источник
Я не знаю, будет ли это практичным для реализации этого, но поможет ли это, чтобы предварительно вычисленные значения синуса были распределены неравномерно, с более тесно сгруппированными значениями, где наклон кривой синуса является самым крутым, и меньшими значениями, где он выравнивается и не так сильно меняется? Позволит ли это более высокую точность там, где это необходимо, без необходимости хранить большое количество значений?
Трихоплакс
5
Что касается # 2, встроенная modфункция - то, что вы хотите. Вы бы написали mod(angle, 360.0).
Натан Рид
1
Прекрасная идея @trichoplax, но я не знаю, как вы могли бы тогда искать значения в таблице. Допустим, мы поместили их в массив, некоторые из которых были более концентрированными. Как мы можем найти правильный индекс?
J.Doe
6
Как насчет размещения ваших значений в 3-канальную 1D текстуру? Таким образом, вы можете получить грех, потому что и загар по цене одного поиска текстуры. Если вы отображаете угол 0 - 2pi на 0 - 1 UV и используете режим повтора текстуры, вам даже не нужен вызов мода, он будет автоматически «оборачиваться», и вы также можете получать линейно-отфильтрованные аппроксимации между сохраненными значениями, а не привязка к ближайшему.
Русс
3
Большую часть времени вы можете исключить тригонометрические функции, когда используете их для геометрии, не используя угол, а начинайте и заканчивайте парой sin / cos и используйте идентификаторы триггера для половинных углов и т.п.
чокнутый урод

Ответы:

8

[0,360)[0,2π)

  • [0,360][0,1]
  • Вы получаете дополнительное преимущество, заключающееся в том, что вам не нужно выполнять петлевую настройку интервала (хотя вам все равно не понадобятся циклы, и вы можете просто использовать операцию модуля). Просто используйте в GL_REPEATкачестве режима обтекания текстуру, и он автоматически запустится в начале снова при доступе с аргументами> 1 (и аналогично для отрицательных аргументов).
  • И вы также получаете преимущество линейной интерполяции между двумя значениями в массиве в основном бесплатно (или, скажем, почти бесплатно), используя в GL_LINEARкачестве фильтра текстуры, таким образом получая значения, которые вы даже не сохраняли. Конечно, линейная интерполяция не является на 100% точной для тригонометрических функций, но она, безусловно, лучше, чем без интерполяции.
  • Вы можете сохранить более одного значения в текстуре , используя текстуру RGBA (или сколько угодно компонентов). Таким образом, вы можете получить, например, sin и cos с помощью одного текстурного поиска.
  • [1,1][0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;(или даже больше компонентов для более мелкого зерна). Это позволяет вам снова получать прибыль от многокомпонентных текстур.

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

Что касается заполнения текстуры данными и адресации одного из ваших комментариев, вы должны учитывать, что фильтрация текстуры возвращает точное значение в центре текселя , то есть координату текстуры, уменьшенную на половину размера текселя. Так что да, вы должны генерировать значения на .5текселях , то есть что-то вроде этого в коде приложения:

float texels[256];
for(unsigned int i = 0; i < 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);

Однако вы, возможно, все же захотите сравнить производительность этого подхода с подходом, использующим небольшой однородный массив (т. Е. uniform float sinTable[361]Или, может быть, на практике меньше, следите за ограничением вашей реализации на единообразном размере массива), который вы просто загружаете с соответствующими значениями, используя glUniform1fvи получить доступ, отрегулировав свой угол до с помощью функции и округлив его до ближайшего значения:[0,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];
Крис говорит восстановить Монику
источник
1
Вот интересное расширение для хранения справочных таблиц в текстурах. Он (ab) использует N-линейную текстурную интерполяцию, чтобы получить интерполяцию более высокого порядка (лучше, чем линейная) точек данных, поверхностей, объемов и гипервотов. blog.demofox.org/2016/02/22/…
Алан Вулф,