Подтвердить что ты не робот

Как управлять текстурным контентом на лету?

У меня есть приложение для iPad, над которым я работаю, и одна возможная функция, которую мы рассматриваем, - это позволить пользователю коснуться изображения и деформировать его.

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

Есть ли эффективная техника для чего-то подобного? Я пытаюсь понять, что нужно сделать, и насколько тяжелой будет операция.

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

4b9b3361

Ответ 1

Существует по крайней мере два принципиально разных подхода:

1. Обновить пиксели (я предполагаю, что это то, что вы имеете в виду в вопросе)

Самый эффективный метод изменения пикселей в текстуре называется Render-to-Texture и может быть выполнен в OpenGL/OpenGL ES через FBOs. На рабочем столе OpenGL вы можете использовать пиксельные объекты буфера (PBOs) для управления пиксельными данными непосредственно на GPU (но OpenGL ES пока не поддерживает это).

В Unextended OpenGL вы можете изменить пиксели в системной памяти, а затем обновить текстуру с помощью glTexImage2D/glTexSubImage2D, но это неэффективное решение последнего курорта, и его следует избегать, если это возможно. glTexSubImage2D обычно намного быстрее, поскольку он только обновляет пиксель внутри существующей текстуры, а glTexImage2D создает совершенно новую текстуру (в качестве преимущества вы можете изменить размер и формат пикселя текстуры). С другой стороны, glTexSubImage2D позволяет обновлять только части текстуры.

Вы говорите, что хотите, чтобы он работал с OpenGL ES, поэтому я бы предложил сделать следующие шаги:

  • замените glTexImage2D() на glTexSubImage2D() - если вы получите достаточную производительность, просто пусть это будет;
  • реализовать рендеринг-текстуру с помощью FBOs и шейдеров - это потребует гораздо больше работы по переписыванию кода, но даст еще лучше производительность.

Для FBOs код может выглядеть так:

// setup FBO
glGenFramebuffers( 1, &FFrameBuffer );
glBindFramebuffer( GL_FRAMEBUFFER, FFrameBuffer );
glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, YourTextureID, 0 );
glBindFramebuffer( GL_FRAMEBUFFER, 0 );

// render to FBO
glBindFramebuffer( GL_FRAMEBUFFER, FFrameBuffer );
glViewport( 0, 0, YourTextureWidth, YourTextureHeight );
your rendering code goes here - it will draw directly into the texture
glBindFramebuffer( GL_FRAMEBUFFER, 0 );

// cleanup
glBindFramebuffer( GL_FRAMEBUFFER, 0 );
glDeleteFramebuffers( 1, &FFrameBuffer );

Имейте в виду, что не все пиксельные форматы могут быть отображены. RGB/RGBA обычно прекрасны.

2. Обновить геометрию

Вы также можете изменить геометрию объекта, на который нанесена ваша текстура. Геометрия должна быть достаточно тесселирована, чтобы обеспечить гладкое взаимодействие и предотвратить появление артефактов. Деформация геометрии может быть выполнена различными способами: параметрическими поверхностями, NURBS, патчами.

Ответ 2

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

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

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

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);

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

Я надеюсь, что это поможет, хотя это довольно мало по деталям.

Ответ 3

Модификация цели рендеринга текстуры с использованием FBO сложна, но довольно сложная.

Итак, мы имеем:

  • TW через черный буфер (связанный с текстурой) - мы вызываем этот Dest
  • Новый "массив пикселей", IW по текстуре IH - мы называем это Src
  • Желание поместить текстуру (IW x IH) в положение (TX, TY) в тексту Dest.

Трюк "поставить" Src в Dest -

  • Связать сгенерированный FBO как цель рендеринга
  • Используйте фиктивный 4-вершинный квадрат с тривиальными вершинными коордами (TX, TY), (TX + IW, Y), (TX + IW, TY + IH), (TX, TY + IH) и координатами текстуры (0, 0), (1,0), (1,1), (0,1)
  • Привязать тривиальный пиксельный шейдер, который считывает текстуру Src, привязанную к первому модулю Texture, и выводит его на "экран" (цель рендеринга a.k.a., Dest)
  • Отметьте квадрат
  • Отвязать FBO

Для правильного отображения Src вам необходимо использовать преобразование орфографической проекции и идентичности камеры.

Координаты (TX, TY) и (IW, IH) в моем 4-шаговом решении должны быть разделены на TW и TH соответственно, чтобы правильно отобразить размер [0.1, 0..1] framebuffer. Чтобы избежать этих делений в шейдере, вы можете просто использовать соответствующую ортографическую проекцию для окна просмотра [0..TW, 0..TH].

Надеюсь, что это решает проблемы с FBOs.