Каков наилучший способ обновить форму шейдера?

10

Каков наиболее приемлемый способ поддержания матриц шейдера в актуальном состоянии и почему?

Например, на данный момент у меня есть Shaderкласс, который хранит дескрипторы шейдерной программы GLSL и униформу. Каждый раз, когда я перемещаю камеру, мне приходится передавать новую матрицу вида в шейдер, а затем каждый другой объект мира, который я должен передавать, его матрицу модели в шейдер.

Это сильно ограничивает меня, поскольку я ничего не могу сделать, не имея доступа к этому объекту шейдера.

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

Lerp
источник
Почему бы не передать данные шейдерам, когда пришло время рендеринга?
Ник Каплингер
Это то, что я намерен, но как? Если один объект создает шейдер, то как мировые объекты должны знать о существовании этого шейдера и передавать ему необходимые матрицы?
Лерп

Ответы:

11

Используйте однородные буферы (то есть постоянные буферы в D3D Lingo).

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

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

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

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

Обратите внимание, что вам нужна умеренно современная версия GL, чтобы вообще использовать унифицированные буферы (3.1 или расширение; достаточно распространенное сегодня, за исключением некоторых старых, но все еще работающих ноутбуков), и вам нужна довольно свежая версия, чтобы возможность привязывать одинаковые буферы к определенным местам привязки из кода шейдера (4.2; все еще необычно, но становится все лучше), в противном случае вам придется проделать большую работу в коде на стороне ЦП, чтобы все настроить (код ЦП должен знать правильную привязку в любом случае, это скорее запах API, чем серьезная проблема). OpenGL | ES не добавлял унифицированные буферы до версии 3.0, которая, к сожалению, до сих пор не поддерживается на большинстве популярных мобильных платформ.

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

См. GLSL Inteface Blocks и Uniform Buffer Objects .

Шон Миддледич
источник
Я знал, что они будут встроены. Спасибо!
Лерп
4

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

Я делаю это в своей игре так, что у меня есть абстрактный Materialкласс. Материалы выступают в качестве моста между игрой и шейдерами. Каждый Materialимеет Shaderи различные свойства, которые можно установить, в том числе текстуры. Когда пришло время рисовать объект, Materialон связан. У Bindметода есть GraphicsStateпараметр, который содержит все текущие графические состояния - матрицы, источники света и т. Д. BindМетод связывает шейдер и текстуры и устанавливает все формы, используя все, что ему нужно из GraphicsState.

Роберт Роухани
источник