0
$\begingroup$

Suppose I have previously rendered some set of pixels on the screen that each contain one of the following RGB values:

0.0 0.0 0.0    in other words, black
1.0 0.0 0.0    in other words, red
0.0 0.0 1.0    in other words, blue
1.0 0.0 1.0    in other words, purple

Now I want to be able to selectively toggle the red or blue components of what's on the screen to 1.0 or 0.0, without affecting the other color components of what's on the screen.

For instance, say some pixel contains 1.0, 0.0, 1.0, purple, and I want to turn off the red in that pixel without changing the value of blue.

Assumption here is at this point I don't know if the blue in that pixel is 0.0 or 1.0. I just want to turn the red part of it off.

Also, I don't want to have to segregate the red changes from the blue changes into separate sets of vertex attributes, i.e. I'd like to be able to set just the red component of one set of pixels and set the blue component of another set of pixels in one call to glDrawArrays(). Also, within each set of pixels some of the reds I want to be able to set to 1.0 while others I want to set to 0.0 and likewise for the blue.

I have a hunch this would involve some creative usage of glBlendFunc(), but dang if I can figure out how to do it.

Note that what I am trying to accomplish here is a poor man's 3D rendering on a typical PC screen using a homemade set of goggles with a red lens for the right eye and a blue lens for the left eye. Think how they did 3D for black and white movies back in the 1950s.

Anybody got some suggestions?

Thanks

============================================================

To answer gilgamec's questions:

"where are the pixels coming from? A texture map? An image? Rendered triangles?"

The GLSL part of the application consists of a vertex shader and a fragment shader in one GLSL program and a vertex shader that does transform feedback in a second GLSL program. Pairs of vertices are encoded by the host CPU into a vertex attribute array and passed to the first GLSL program for rendering to the screen via a call to glDrawArrays(GL_LINES,...). The same set of vertices is then passed to the second GLSL program whose vertex shader does the same transformations as the first's but then feeds them back to the host CPU.

"Why can't you alter the input directly?"

Well, I can. The problem is algorithm running in the host CPU code that generates the vertex pairs is very CPU intensive and thus gets delivered to the GPUs rather slowly. Slow enough that there is a visible delay and there's really no way to speed that up much. It's modelling reflections of images off of sets of mirrors as viewed by some observer that moves around virtually among the set of mirrors. Imagine a big polyhedron made of mirrors with their reflective surfaces facing inward and the observer is standing inside of it along with a light source consisting of a collection of line segments. I've copied a non-steroscopic rendering of it as an example below. Here the mirrored polyhedron is a cube that the observer is standing in. The big red triangle is the single light source and everything else is reflections of reflections of the triangle plus reflections of the edges of the cube in blue. The observer himself is transparent so his head doesn't get in the way of the reflections. 8)

cube

I can run this program in the usual manner in which on each pass through the main loop the screen is first cleared via glClear(), transform matrices are computed and vertices are rendered via glDrawArrays(GL_LINES...). The problem there is the vertices representing the reflections get generated slowly with large models, so it takes a visible while for rendering to complete. So I've implemented a second mode of operation in which I only clear the screen once at program start up. On each pass through the main loop after glDrawArrays() and the screen buffer swap I copy the front frame buffer to the back buffer. On the next pass through the loop as new reflections come out of the algorithm I use the feed back data from the second GLSL program to "erase" the old reflections from the screen by painting them black in the fragment shader before each new reflection is painted in red or blue. The effect isn't perfect, there's still lag, but the old reflections get sort of dragged along as the observer moves around until they finally get erased and replaced by new reflections once the observer sits still long enough. It's pretty, which is the whole point of this exercise.

So I've got this second "incremental screen update" mode implemented for non-stereoscopic (1 eyed observer) mode and it works. And I have non-incremental screen mode implemented for red-blue stereoscopic mode, see below:

cube3d

If you happen to have some red and blue transparent sheets lying around, get in a dark room and look at the above with the red in front of your right eye and the blue in front of your left eye. Relax your eyes a bit and you should be able to perceive the reflections in 3D (at least I can see it and my eyes are 65 years old, 8).

But to get incremental screen mode to work in 3D, I need to be able to write 0.0 (to erase to black) or 1.0 to the blue component of pixels on the screen without affecting the red and vis-versa. I think I can do that with glColorMask() but that means segregating the vertex attributes corresponding to left eye (blue) perspective from those corresponding to right eye (red) perspective.

"You mention OpenGL functions, so have you just tried modifying the fragment shader?"

That's what the question is about. Is there a way to write to only the red or blue components of existing pixels in the frame buffer from a fragment shader?

$\endgroup$
6
  • $\begingroup$ You haven't really provided enough information to let us help you: where are the pixels coming from? A texture map? An image? Rendered triangles? Why can't you alter the input directly? You mention OpenGL functions, so have you just tried modifying the fragment shader? $\endgroup$
    – gilgamec
    Aug 9, 2021 at 8:49
  • $\begingroup$ So are you looking to use blend equation of add? $\endgroup$
    – joojaa
    Aug 11, 2021 at 19:43
  • $\begingroup$ joojaa, I don't understand your question. What I'm looking for is a way to specify which color components will be written to the screen buffer from a fragment shader. Something analogous to glColorMask() that can be set in GLSL rather than from the host CPU. $\endgroup$
    – user256552
    Aug 13, 2021 at 2:08
  • $\begingroup$ I really do not understand why you can't use glColorMask. Whatever you are drawing, why can't you draw the red part with a red colour mask and the blue part with a blue colour mask? $\endgroup$ Apr 11, 2022 at 10:23
  • 2
    $\begingroup$ Honestly, this incremental update thing where you keep the old framebuffer data and try to erase bits of it, sounds like way more trouble than it's worth. You can keep the vertex buffer data generated by the CPU and re-render it from scratch on each new frame. GPUs are plenty fast enough for it - there's really no reason to mess around with trying to selectively erase and redraw bits of the previous frame. When the CPU has generated a new vertex buffer you can then swap that in at the beginning of the next frame. $\endgroup$ Apr 12, 2022 at 17:29

1 Answer 1

1
$\begingroup$

Since you only want full brightness and no brightness, sure you can.

Red channel on: additive blend with red
Red channel off: multiplicative blend with cyan
Blue channel on: additive blend with blue
Blue channel off: multiplicative blend with yellow

I also don't understand why you can't just run your rendering code twice: once with a red color mask and once with a blue color mask. It doesn't seem like you need to control it from GLSL. You're allowed to render the same vertices twice with different states - commercial video games do that sort of thing routinely.

Switching the blend mode from additive to multiplicative is a state change, just like setting the colour mask.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.