Каков наиболее приемлемый способ поддержания матриц шейдера в актуальном состоянии и почему?
Например, на данный момент у меня есть Shader
класс, который хранит дескрипторы шейдерной программы GLSL и униформу. Каждый раз, когда я перемещаю камеру, мне приходится передавать новую матрицу вида в шейдер, а затем каждый другой объект мира, который я должен передавать, его матрицу модели в шейдер.
Это сильно ограничивает меня, поскольку я ничего не могу сделать, не имея доступа к этому объекту шейдера.
Я думал о создании одноэлементного ShaderManager
класса, который отвечает за хранение всех активных шейдеров. Затем я могу получить к нему доступ из любой точки мира, и объекту мира не нужно будет знать о том, какие шейдеры активны, только потому, что ему нужно сообщить ShaderManager
нужные матрицы, но я не уверен, что это лучший способ, и, возможно, есть некоторые проблемы, которые возникнет от принятия этого подхода.
Ответы:
Используйте однородные буферы (то есть постоянные буферы в D3D Lingo).
Пока все ваши шейдеры согласовывают расположение и точку привязки каждого такого буфера, обновление становится быстрым. Моделям вообще не нужно ничего знать о шейдерах. Им нужно только обновить матрицу вида модели в своем постоянном буфере, и конвейер рендеринга будет использовать ее автоматически.
Как минимум, я бы сказал, что у вас должно быть два таких буфера. Первый должен хранить вашу матрицу проекции, матрицу камеры, сцепленную матрицу проекции камеры, информацию о области просмотра, подробные сведения о кадре и обратные матрицы. Вам нужно обновить этот буфер только один раз для каждой сцены.
Затем дайте каждой модели другой буфер для хранения ее матрицы вида модели, нормальной матрицы, инверсий и свойств материала. Это обновляется один раз для каждой модели и может быть выполнено на другом этапе обновления (если / когда это необходимо). Информация о материале может / должна быть перемещена в третий буфер для конкретного материала, если у вас есть возможность обмениваться материалами между несколькими объектами.
В настройке с передним затенением имеет смысл поместить все источники света в другой буфер, а при отложенном затенении также имеет смысл использовать буфер для каждого источника света для прохода света (вместо буфера модели / материала, используемого в геометрия прохода).
Обратите внимание, что вам нужна умеренно современная версия GL, чтобы вообще использовать унифицированные буферы (3.1 или расширение; достаточно распространенное сегодня, за исключением некоторых старых, но все еще работающих ноутбуков), и вам нужна довольно свежая версия, чтобы возможность привязывать одинаковые буферы к определенным местам привязки из кода шейдера (4.2; все еще необычно, но становится все лучше), в противном случае вам придется проделать большую работу в коде на стороне ЦП, чтобы все настроить (код ЦП должен знать правильную привязку в любом случае, это скорее запах API, чем серьезная проблема). OpenGL | ES не добавлял унифицированные буферы до версии 3.0, которая, к сожалению, до сих пор не поддерживается на большинстве популярных мобильных платформ.
Если буферы недоступны, вам понадобится глобальное место для хранения местоположений индекса для активного шейдера. Вы можете использовать
glGetUniformLocation
после загрузки своего шейдера, чтобы найти индексы для известных имен (например,ModelViewMatrix
или тому подобное), а затем сохранить эти индексы. Ваш рендер может отображать значения перечисления, например,MODEL_VIEW
передаваемые в функцию-SetUniform
обертку, чтобы посмотреть в связанный шейдер, найти индекс иglUniform
правильно вызвать . Это не очень большое изменение в использовании клиентского кода по сравнению с буферами, за исключением необходимости устанавливать каждую униформу индивидуально, если вы все правильно завершили.См. GLSL Inteface Blocks и Uniform Buffer Objects .
источник
Самый простой способ сделать это - просто стандартизировать унифицированные имена между вашими шейдерами и просто отправить все данные прямо перед рисованием. Накладные расходы не безумны, но вы можете оптимизировать их позже, чтобы не всегда отправлять менее обновленную форму.
Я делаю это в своей игре так, что у меня есть абстрактный
Material
класс. Материалы выступают в качестве моста между игрой и шейдерами. КаждыйMaterial
имеетShader
и различные свойства, которые можно установить, в том числе текстуры. Когда пришло время рисовать объект,Material
он связан. УBind
метода естьGraphicsState
параметр, который содержит все текущие графические состояния - матрицы, источники света и т. Д.Bind
Метод связывает шейдер и текстуры и устанавливает все формы, используя все, что ему нужно изGraphicsState
.источник