opengl передает текстуру в программу: один раз или при каждом рендеринге?

#opengl #textures

#opengl #Текстуры

Вопрос:

У меня есть программа с двумя текстурами: одна из видео, а другая из изображения.

Должен ли я передавать текстуру изображения в программу при каждом рендеринге или я могу сделать это только один раз? т.е. я могу сделать

 glActiveTexture(GLenum(GL_TEXTURE1))
glBindTexture(GLenum(GL_TEXTURE_2D), texture.id)
glUniform1i(textureLocation, 1)
  

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

Ответ №1:

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

Прежде всего, ни одно из приведений в стиле C, которые вы выполняете в своем коде, не требуется. Просто используйте GL_TEXTURE_2D и так далее вместо GLenum(GL_TEXTURE_2D) .

glActiveTexture(GL_TEXTURE0 i) , где i находится в диапазоне [0, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1] , выбирает текущую активную текстурную единицу. Команды, которые изменяют состояние текстурного модуля, будут влиять на модуль i до тех пор, пока вы не вызываете glActiveTexture с другим допустимым идентификатором модуля.

Как только вы вызываете glBindTexture(target, name) текущий активный текстурный модуль i , состояние текстурного модуля изменяется, чтобы ссылаться name на указанное target при его выборке с помощью соответствующего сэмплера в шейдере (т. Е. name Может быть привязано к TEXTURE_2D , и соответствующий образец должен быть a sampler2D ). Вы можете привязать только один текстурный объект к определенной цели для текущего активного текстурного блока — так что, если вам нужно выполнить сэмплирование двух 2D-текстур в вашем шейдере, вам нужно будет использовать два текстурных блока.

Из вышесказанного должно быть очевидно, что glUniform1i(samplerLocation, i) делает.

Итак, если у вас есть две 2D-текстуры, которые вам нужно сэмплировать в шейдере, вам понадобятся два текстурных блока и два сэмплера, каждый из которых относится к одному конкретному блоку:

 GLuint regularTextureName = 0;
GLunit videoTextureName = 0;

GLint regularTextureSamplerLocation = ...;
GLint videoTextureSamplerLocation = ...;

GLenum regularTextureUnit = 0;
GLenum videoTextureUnit = 1;

// setup texture objects and shaders ...

// make successfully linked shader program current and query
// locations, or better yet, assign locations explicitly in
// the shader (see below) ...

glActiveTexture(GL_TEXTURE0   regularTextureUnit);
glBindTexture(GL_TEXTURE_2D, regularTextureName);
glUniform(regularTextureSamplerLocation, regularTextureUnit);

glActiveTexture(GL_TEXTURE0   videoTextureUnit);
glBindTexture(GL_TEXTURE_2D, videoTextureName);
glUniform(videoTextureSampleLocation, videoTextureUnit);
  

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

 layout(binding = 0) uniform sampler2D regularTextureSampler;
layout(binding = 1) uniform sampler2D videoTextureSampler;
  

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

Что касается вопроса о том, как часто вам нужно это делать: вам нужно делать это, когда вам нужно это сделать — не меняйте состояние, которое не нуждается в изменении. Если вы никогда не меняете привязки соответствующего текстурного блока, вам вообще не нужно повторно привязывать текстуру. Настройте их один раз правильно и оставьте их в покое.

То же самое касается привязок сэмплера: если вы не выполняете сэмплирование других текстурных объектов с помощью шейдера, вам вообще не нужно изменять состояние программы шейдера. Настройте его один раз и оставьте в покое.

Короче говоря: не меняйте состояние, если в этом нет необходимости.

РЕДАКТИРОВАТЬ: я не совсем уверен, так это или нет, но если вы используете один и тот же шейдер с одним сэмплером для обеих текстур в отдельных вызовах шейдеров, вам придется что-то изменить, но угадайте, что, это так же просто, как позволить сэмплеру ссылаться на другую текстурную единицу:

 // same texture unit setup as before
// shader program is current 

while (rendering)
{
  glUniform(samplerLocation, regularTextureUnit);
  // draw call sampling the regular texture 

  glUniform(samplerLocation, videoTextureUnit);
  // draw call sampling teh video texture
}
  

Комментарии:

1. (Мне нужны приведения в стиле C, потому что я пишу Swift, и компилятор жалуется, если я этого не делаю) Я знаю, что это глупо..

2. @Guig: На самом деле я не прав, предполагая, что вы пишете там код на C или C . Пока вы не используете Objective-C … 😉

3. Я думаю, все учатся и учат GL по-разному. Я все еще думаю, что очень важно понимать, что активная текстура и привязка являются глобальным состоянием gl, но равномерная привязка (местоположение) для каждой программы.

4. @starmole: Важно то, что существует состояние контекста и состояние объекта (помимо различия между состоянием сервера и состоянием клиента), а текущая активная текстурная единица — это состояние контекста. Привязки текстур явно являются состоянием текстурного блока , что делает их своего рода гибридом — они являются контекстным состоянием, но вы не меняете глобальное TEXTURE_BINDING_* , вы меняете то, что принадлежит активному текстурному блоку. По сути, это тот же эффект, что и изменение состояния объекта с использованием метода привязки к изменению.

Ответ №2:

Вы должны привязывать текстуру перед каждым рисованием. Вам нужно только один раз установить местоположение. Для этого вы также можете выполнить компоновку (привязку = 1) в своем коде шейдера. Единообразное расположение остается в программе. Привязка текстуры является глобальным состоянием GL. Также будьте осторожны с ActiveTexture: это глобальное состояние GL.

Хорошей практикой было бы:

  • При создании программы один раз установите расположение текстуры (равномерное)
  • При рисовании: SetActive (i), Bind (i), Draw, SetActive (i) Bind (0), SetActive (0)

Затем оптимизируйте позже для избыточных вызовов.

Комментарии:

1. Ваше объяснение противоречит самому себе. Вам не нужно привязывать текстуру перед каждым вызовом рисования, потому что это глобальное состояние. Вам НУЖНО хотя бы один раз предоставить единообразное местоположение для каждой программы, для которой требуется выборка из этой текстуры.

2. @MichaelIvanov: Как бы вы объяснили это лучше? Я исходил из разумного предположения, что существует более одного рисунка, каждый с разными текстурами. Я не вижу противоречия? Я пытался указать, что местоположение зависит от программы и привязка глобальная. И самый простой (не самый эффективный) способ справиться с глобальным состоянием — просто установить его.

3. Да, верно, теперь это кажется мне более разумным;) Вы говорите: «если нам нужно привязать разные текстуры при каждом вызове рисования, мы перепривязываем, но местоположения могут быть установлены только один раз для каждой программы», правильно?