循环遍历所有像素并在OpenGL中获取/设置单个像素颜色?

时间:2011-01-22 01:34:57

标签: macos opengl screensaver pixels

我在Processing上写了一些东西,我现在想制作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 个答案:

答案 0 :(得分:2)

OpenGL的基本思想是你永远不会手动设置单个像素的值,因为这通常太慢了。相反,你渲染三角形并用它们做各种技巧,比如纹理,混合等。

为了自由编程每个像素在OpenGL中的作用,您需要使用一种称为着色器的技术。如果你之前没有做过类似的事情,这并不容易。着色器的想法是GPU执行它们而不是CPU,这会产生非常好的性能并从CPU中消除负载。但在你的情况下,使用CPU而不是使用着色器和OpenGL可能是一个更好的主意,因为这种方法更容易入手。

我建议您使用像SDL(或可能是glfw)这样的库,它可以让您在没有硬件加速的情况下处理像素。不过,您仍然可以使用OpenGL执行此操作。通过使用函数glDrawPixels。该函数将原始像素数据绘制到屏幕上。但它可能不是很快。

首先阅读一些关于SDL的教程,例如。

编辑:如果你想使用着色器,它们的难度(除其他外)是你不能指定设置像素值的坐标。并且您无法直接从屏幕获取像素值。使用着色器的一种方法是:

  • 设置两个纹理:纹理A 纹理B
  • 将其中一个纹理绑定为将所有内容呈现为
  • 的目标
  • 将另一个纹理绑定为着色器的输入纹理
  • 使用着色器渲染全屏四边形并在屏幕上显示结果
  • 交换纹理A和B,以便您将之前的结果用作下一个输入
  • 再次渲染

答案 1 :(得分:2)

如果您不想使用着色器路径,请尝试在2D内存阵列上对CPU进行所有像素修改,然后每帧使用glDrawPixels将像素推送到屏幕。它不会非常硬件加速,但它可能适合您的目的。另一件事是使用glTexImage2D将每帧的新像素数据绑定到纹理,然后将纹理四边形渲染到整个屏幕。我不确定哪个更快。我的建议是在进入着色器的复杂性之前尝试这些事情。

答案 2 :(得分:0)

您的代码中存在一些错误,使得逆向工程和移植更加困难,让我想知道您是否真的发布了正确的代码。假设产生的视觉效果是您想要的,这里更有效,更正确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();
}

请注意,循环的上限除以2。正如您所写的,set()次调用的3/4是针对超出窗口范围的像素。额外的if是必要的,因为在坐标中添加了小的随机值。

这段代码的整体效果可以描述为图像的就地拉伸和反转,并带有一点随机性。因为它是就地转换,所以不能轻易地并行化或加速,所以你最好将它作为CPU上的位图/纹理操作来实现。您可以在不必从GPU读取像素的情况下执行此操作,但是您必须将每帧完整像素的屏幕推送到GPU。

如果使用glDrawPixels格式参数为GL_LUMINANCE且类型参数为GL_UNSIGNED_BYTE,那么您可以非常轻松地将此代码转换为对字节数组进行操作,这将保留与使用32位RGBA值相比,内存消耗有所下降。