Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<blockquote> <p>If i draw 1 white pixel and move it around each frame for one pixel to some direction, each frame the screen pixels will get one R/G/B value less (of range 0-255), thus after 255 frames the white pixel will be fully black. So if i move the white pixel around, i would see a gradient trail going from white to black evenly 1 color value difference compared to previous pixel color.</p> </blockquote> <p>Before I explain how to do this, I would like to say that the visual effect you're going for is a terrible visual effect and you should not use it. Subtracting a value from each of the RGB colors will produce a <em>different color</em>, not a darker version of the same color. The RGB color (255,128,0), if you subtract 1 from it 128 times, will become (128, 0, 0). The first color is brown, the second is a dark red. These are not the same.</p> <p>Now, since you haven't really explained this very well, I have to make some guesses. I am assuming that there are no "objects" in what you are rendering. There is no state. You're simply drawing stuff at arbitrary locations, and you don't remember what you drew where, nor do you want to remember what was drawn where.</p> <p>To do what you want, you need two off-screen buffers. I recommend using <a href="http://www.opengl.org/wiki/Framebuffer_Object" rel="nofollow noreferrer">FBO</a>s and screen-sized textures for these. The basic algorithm is simple. You render the previous frame's image to the current image, using a blend mode that "subtracts 1" from the colors you write. Then you render the new stuff you want to the current image. Then you display that image. After that, you switch which image is previous and which is current, and do the process all over again.</p> <p>Note: The following code will assume OpenGL 3.3 functionality.</p> <h2>Initialization</h2> <p>So first, during initialization (after OpenGL is initialized), you must create your screen-sized textures. You also need two screen-sized depth buffers.</p> <pre><code>GLuint screenTextures[2]; GLuint screenDepthbuffers[2]; GLuint fbos[2]; //Put these definitions somewhere useful. glGenTextures(2, screenTextures); glGenRenderbuffers(2, screenDepthbuffers); glGenFramebuffers(2, fbos); for(int i = 0; i &lt; 2; ++i) { glBindTexture(GL_TEXTURE_2D, screenTextures[i]); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SCREEN_WIDTH, SCREEN_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); glBindRenderbuffer(GL_RENDERBUFFER, screenDepthBuffers[i]); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCREEN_WIDTH, SCREEN_HEIGHT); glBindRenderbuffer(GL_RENDERBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[i]); glFramebufferTexture(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, screenTextures[i], 0); glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, screenDepthBuffers[i]); if(glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { //Error out here. } glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } </code></pre> <h2>Drawing Previous Frame</h2> <p>The next step will be drawing the previous frame's image to the current image.</p> <p>To do this, we need to have the concept of a previous and current FBO. This is done by having two variables: <code>currIndex</code> and <code>prevIndex</code>. These values are indices into our GLuint arrays for textures, renderbuffers, and FBOs. They should be initialized (during initialization, not for each frame) as follows:</p> <pre><code>currIndex = 0; prevIndex = 1; </code></pre> <p>In your drawing routine, the first step is to draw the previous frame, subtracting one (again, I strongly suggest using a real blend here).</p> <p>This won't be full code; there will be pseudo-code that I expect you to fill in.</p> <pre><code>glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[currIndex]); glClearColor(...); glClearDepth(...); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); glActiveTexture(GL_TEXTURE0 + 0); glBindTexture(GL_TEXTURE_2D, screenTextures[prevIndex]); glUseProgram(BlenderProgramObject); //The shader will be talked about later. RenderFullscreenQuadWithTexture(); glUseProgram(0); glBindTexture(GL_TEXTURE_2D, 0); </code></pre> <p>The <code>RenderFullscreenQuadWithTexture</code> function does exactly what it says: renders a quad the size of the screen, using the currently bound texture. The program object <code>BlenderProgramObject</code> is a GLSL shader that does our blend operation. It fetches from the texture and does the blend. Again, I'm assuming you know how to set up a shader and so forth.</p> <p>The fragment shader would have a main function that looks something like this:</p> <pre><code>shaderOutput = texture(prevImage, texCoord) - (1.0/255.0); </code></pre> <p>Again, I strongly advise this:</p> <pre><code>shaderOutput = texture(prevImage, texCoord) * (0.05); </code></pre> <p>If you don't know how to use shaders, then you should learn. But if you don't want to, then you can get the same effect using a <a href="http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml" rel="nofollow noreferrer">glTexEnv</a> function. And if you don't know what those are, I suggest <a href="http://alfonse.bitbucket.org/oldtut/" rel="nofollow noreferrer">learning shaders</a>; it's so much easier in the long run.</p> <h2>Draw Stuff As Normal</h2> <p>Now, you just render everything you would as normal. Just don't unbind the FBO; we still want to render to it.</p> <h2>Display the Rendered Image on Screen</h2> <p>Normally, you would use a swapbuffer call to display the results of your rendering. But since we rendered to an FBO, we can't do that. Instead, we have to do something different. We must blit our image to the backbuffer and then swap buffers.</p> <pre><code>glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[currIndex]); glBlitFramebuffer(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, SCREEN_WDITH, SCREEN_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST); glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); //Do OpenGL swap buffers as normal </code></pre> <h2>Switch Images</h2> <p>Now we need to do one more thing: switch the images that we're using. The previous image becomes current and vice versa:</p> <pre><code>std::swap(currIndex, prevIndex); </code></pre> <p>And you're done.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload