开发者

Loop through all pixels and get/set individual pixel color in OpenGL?

I wrote a little thingy with Processing that I now would like to make a Mac OS X Screen Saver of. However, diving in to OpenGL was not as easy as I thought it would be.

Basically I want to loop through all pixels on screen and based on that pixels color set another pixels color.

The Processing code looks like this:

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++)开发者_C百科{
     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);
     }
   }
 }
}

It's not very pretty and nor is it very effective. However, I'd like to find out how to do something similiar with OpenGL. I don't even know where to start.


The basic idea of OpenGL is that you never set the values of individual pixels manually, because that's often too slow. Instead you render triangles and do all kinds of tricks with them, like textures, blending, etc.

In order to freely program what each individual pixel does in OpenGL, you need to use a technique called shaders. And that's not very easy if you haven't done anything similar before. The idea of shaders is that GPU executes them instead of CPU, which results in very good performance and takes the load off from the CPU. But in your case it is probably a better idea to do it with CPU and not with shaders and OpenGL, as that approach is much easier to start with.

I recommend you use a library like SDL (or possibly glfw), which lets you do things with pixels without hardware acceleration. You can still do it with OpenGL too, though. By using the function glDrawPixels. That function draws raw pixel data to the screen. But it's probably not very fast.

So start by reading some tutorials about SDL, for example.

Edit: If you want to use shaders, the difficulty with them (among other things) is that you can't specify coordinates to which set pixel values. And you can't get pixel values directly from the screen either. One way to do it with shaders would be the following:

  • Set up two textures: texture A and texture B
  • Bind one of the textures as a target that you render everything to
  • Bind another one of the textures as an input texture for the shader
  • Render a full-screen quadrangle using your shader and show the result on the screen
  • Swap textures A and B so you became to use your previous result as your next input
  • Render again


If you don't want to go the shader route, try doing all your pixel modification in the CPU on a 2D memory array, then use glDrawPixels every frame to push your pixels to the screen. It won't be very hardware accelerated, but it might be fine for your purposes. Another thing to try is to use glTexImage2D to bind your new pixel data every frame to a texture and then render a textured quad to the entire screen. I'm not sure which will be faster. My advice is to try these things before jumping into the complexity of shaders.


There are a few bugs in your code that make reverse engineering and porting it harder, and make me wonder if you actually posted the correct code. Assuming that the visual effect produced is what you want, here is a more efficient and more correct 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();
}

Note that the upper bounds of the loop are divided by two. As you wrote it, 3/4 of your set() calls were for pixels that are beyond the bounds of the window. The extra if is necessary because of the addition of small random values to the coordinates.

The overall effect of this code could be described as an in-place stretch and invert of the image, with a little bit of randomness thrown in. Because it's an in-place transformation, it can't be easily parallelized or accelerated, so you are best off implementing this as bitmap/texture operations on the CPU. You can do this without having to ever read pixels from the GPU, but you will have to push a screen full of pixels to the GPU each frame.

If you use glDrawPixels with a format argument of GL_LUMINANCE and a type argument of GL_UNSIGNED_BYTE, then you can pretty easily convert this code to operate on a byte array, which will keep the memory consumption down somewhat as compared with using 32-bit RGBA values.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜