Перебрать все пиксели и получить / установить отдельный цвет пикселей в OpenGL? - PullRequest
3 голосов
/ 22 января 2011

Я написал кое-что о Обработке, о котором сейчас хотел бы сделать заставку для Mac OS X.Однако погрузиться в OpenGL было не так просто, как я думал.

По сути, я хочу перебрать все пиксели на экране и на основе этого цвета установить другой пиксельный цвет.

Код обработки выглядит следующим образом:

void setup(){
  size(500,500, P2D);
  frameRate(30);
  background(255);
}

void draw(){
 for(int x = 0; x<width; x++){
  for(int y = 0; y<height; y++){
     float xRand2 = x+random(2);
     float yRand2 = y+random(2);

     int xRand = int(xRand2);
     int yRand = int(yRand2);

     if(get(x,y) == -16777216){
     set(x+xRand, y+yRand, #FFFFFF);
     }
     else if(get(x,y) == -1){
     set(x+xRand, y+yRand, #000000);
     }
   }
 }
}

Это не очень красиво и не очень эффективно.Тем не менее, я хотел бы узнать, как сделать что-то похожее с OpenGL.Я даже не знаю с чего начать.

Ответы [ 3 ]

2 голосов
/ 22 января 2011

Если вы не хотите идти по пути шейдера, попробуйте выполнить все ваши модификации пикселей в ЦП на массиве 2D-памяти, а затем используйте glDrawPixels каждый кадр, чтобы выдвинуть ваши пиксели на экран. Это не будет очень аппаратно ускорено, но может подойти для ваших целей. Еще одна попытка - использовать glTexImage2D, чтобы привязать ваши новые данные пикселей каждый кадр к текстуре, а затем визуализировать текстурированный квадрат на весь экран. Я не уверен, что будет быстрее. Мой совет: попробуйте эти вещи, прежде чем переходить к сложности шейдеров.

2 голосов
/ 22 января 2011

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

Чтобы свободно программировать действия каждого отдельного пикселя в OpenGL, необходимо использовать технику, называемую шейдерами.И это не очень легко, если вы не делали ничего подобного раньше.Идея шейдеров заключается в том, что GPU выполняет их вместо CPU, что приводит к очень хорошей производительности и снимает нагрузку с CPU.Но в вашем случае, вероятно, лучше сделать это с процессором, а не с шейдерами и OpenGL, так как этот подход гораздо проще начать.

Я рекомендую использовать библиотеку, подобную SDL (или, возможно, glfw), что позволяет вам работать с пикселями без аппаратного ускорения.Вы все еще можете сделать это и с OpenGL.С помощью функции glDrawPixels.Эта функция выводит необработанные данные пикселей на экран.Но это, вероятно, не очень быстро.

Итак, начните с прочтения, например, нескольких учебных пособий по SDL.

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

  • Настройка двух текстур: текстура A и текстура B
  • Bindодна из текстур в качестве цели, которую вы визуализируете для
  • Привязка другой из текстур в качестве входной текстуры для шейдера
  • Визуализация полноэкранного четырехугольника с помощью вашего шейдера и показ результатана экране
  • Поменяйте местами текстуры A и B, чтобы вы стали использовать предыдущий результат в качестве следующего ввода
  • Выполните рендеринг снова
0 голосов
/ 22 января 2011

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

void draw() {
  loadPixels();
  for(int x = 0; x<width/2; x++) {
    for(int y = 0; y<height/2; y++) {
      int x_new = 2*x+int(random(2));
      int y_new = 2*y+int(random(2));

      if (x_new < width && y_new < height) {
        int dest_pixel = (y_new*width + x_new);

        color c = pixels[y*width+x];

        if(c == #FFFFFF){
          pixels[dest_pixel] = #000000;
        }
        else {
          pixels[dest_pixel] = #FFFFFF;
        }
      }
    }
  }
  updatePixels();
}

Обратите внимание, что верхние границы цикла делятся на два.Как вы уже писали, 3/4 ваших set() вызовов были для пикселей, которые находятся за пределами окна.Дополнительная if необходима из-за добавления небольших случайных значений к координатам.

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

Если вы используете glDrawPixels с аргументом формата GL_LUMINANCE и аргументом типа GL_UNSIGNED_BYTE, то вы можете довольно легко преобразовать этот код для работы с байтовым массивом, что сохранит потребление памятинесколько ниже по сравнению с использованием 32-битных значений RGBA.

...