PDA

View Full Version : change color buffer values



raoulduke
06-06-2006, 05:43 AM
Hi everyone.

I want to change the output of my opengl program a little bit. This should be targeted at all the pixels in my output. I want to change the red value of each pixel, which should be newly calculated by multiplying the old rgb-values with r*0,299 , g*0,587 and b*0,114.
The output of this calculation should overwrite the red-value. The green and blue values should stay the same.

Unfortunately i don't have a clue how to do this. Do you guys have any ideas?
Thank you

k_szczech
06-06-2006, 07:03 AM
You would have to render everything to texture, or copy it to texture after rendering (glCopyTexSubImage2D).
Then just draw a fullscreen quad using this texture with GL_MODULATE set as env mode and color set to (0.299, 1.0, 1.0).
If you would only need to overwrite one component then using glColorMask will help, but since you want to modulate it you need to render to texture.

Jackis
06-06-2006, 08:37 AM
Yes, I think, there is no other possibilities to do this, GL blends rgb channels in parallel, with no possibilities to mix them.

Do you want to store luminocity/grey component in red, leaving green and blue as is?

Then k_szczech is right, that you need to save the screen in the texture, then use it.
The only thing is that, as I see, his proposal about modulate constant is not what you are asking exactly.

raoulduke
06-08-2006, 03:34 AM
Thanks for your answers so far.

gl_modulate seems to be a good thing as it multiplies the rgb values. But am i right that the way k_szczech is suggesting, would only multiply my old red-value with .299 ?
I also think this could be done with putting a light with those values into the scene. If it's so, than this is not what i'm intending, otherwise i didn't get it. ;-)
The thing is that i want to store the luminosity/grey-value in the red component, as Jackis said.

An example:
Let's say this pixel should be transformed the way i want it to:
we have red=0, green=1 and blue=1.
the new red value should be calculated by
r*0.299 + g*0.587 + b*0.114 =>
0*0.299 + 1*0.587 + 1*0.114 == 0.701

The final result should be
red=0.701, green =1 and blue=1.

As you can see, the blue and green values should stay as the are - only the red value should have the luminosity value of this pixel stored.

isn't there a function that goes through every pixel in my color buffer and changes it.
i mean, gl_clear doesn't do any different: you define a color and then gl_clear overwrites the buffer with this color...

any ideas? thank you

Overmind
06-08-2006, 08:30 AM
There is no such function. As mentioned by the previous posters you have to copy the color buffer to a texture using glCopySubTexImage (or render to a texture in the first place).

Then you render a full screen quad with the texture on it to get it back to the color buffer. At this point you can modify the values.

One way to do this is write a fragment shader if your card supports it.

Another possibility is to use texture combiners. Look at the ARB_texture_env_combine and ARB_texture_env_dot3 extension (what you have to calculate is exactly a dot product of the color vector and a constant vector).

You can use the color mask to prevent the old green and blue values from being changed (the combiners will output the result on all channels, not only r).

Jackis
06-08-2006, 08:57 AM
Unfortunately, there is no such a function, which goes trough every pixel and updates it in your way ((

There is 2 solutions: using pixel shaders or not using them.

First, in both solutions, you need to copy your framebuffer into the texture.

Then, you must draw a full-screen quad with your texture on it, and with some tricky enabled )

1st solution - fragment shader will do what you need. E.g.
fixed3 tex = x3tex2D(screenTex, uv);
out.color.r = dot(tex, fixed3(0.3x, 0.59x, 0.11x));
out.color.gb = tex.gb;

2nd solution - you can do almost the same thing by texture environment combiners set-up, or via GL_SGI_color_matrix extension (not supported on nVidia). See documentation here - http://oss.sgi.com/projects/ogl-sample/registry/SGI/color_matrix.txt

I would recommend the first case ))

BTW: good luck on football champ ))

jtipton
06-08-2006, 09:50 AM
All of these seem way to complicated. Just turn on blending and make your multipliers use GL_CONSTANT_COLOR. Then you can specify a different color for r, g, and b. When a fragment is drawn, it will be multiplied by the constant color, achieving the effect. Of course this all depends on what you are actually doing.

The brute force method would be to perform a buffer read, scaling the values, then performing a buffer write with the new data.

Jackis
06-08-2006, 10:14 AM
jtipton

actually, you are not right, because raoulduke wants to get summarized color value within one R channel, and your proposed method won't work anyhow.

jtipton
06-09-2006, 09:09 AM
Multiplication is commutative, therefore:

(r + g + b + a)* C = rC + gC + bC + aC

Multiplying each fragement by a constant is the same as multiplying the total sum of the colors. This method does work. I have used this for performing "Overlay Blending" in which the values in the color buffer are multiplied by color channel constants to acheive the effect of a color overlay without changing luminance values.

Jackis
06-09-2006, 09:45 AM
Yes, you're right, but raouldduke needs to get
r_new = c1 * r + c2 * g + c3 * b (in each pixel)
This cannot be done with such a blending you mentioned.

jtipton
06-12-2006, 10:16 AM
You can specify a constant blend color (rc, gc, bc, ac). Then you can specify your blending color (r, g, b, a). You can then set up your blending to multiply each incoming fragment by the constant color, not the constant alpha. This will give (rc * r, gc * g, bc * b, ac * a). Since it would be per fragment instead of on the sum you would divide each constant by the number of iterations and add each iteration together. This will do it if the following are true:

1. You know what the constants are at the time of rendering
2. You know how many items will constitute each pixel
3. You have the extensions available to perform constant color blending.

Overmind
06-12-2006, 10:41 AM
And how exactly are you proposing to get rc*r + gc*g + bc*b? So far you only described how to get each of those seperately, but not the sum...

Hint: It isn't possible with blending alone, but you're welcome to try anyway.

raoulduke
06-13-2006, 04:27 AM
Thank you very much for your answers!
@Jackis: Thank you very much for your best wishes for the Football Championship - we'll need it! ;-)

jtipton: i didn't quite understand your suggestion. How do i get the multiplied green and blue values into the red channel?

Jackis & Overmind:
Am I right - you suggest to render my normal output to texture, create a quad which fills the screen and render the texture on it. Then use a fragment shader to change my red-value.

Probably this is a stupid question as i don't know much about shaders, but isn't it possible to use a shader on the color buffer or in any other way than to draw a cube with texture?

I have a little handicap: I am actually writing an opengl wrapper with a changed opengl32.dll to change the visual output. My goal is to change the output of any given opengl program without changing it. So, putting a cube into the scene would probably be possible but seems complicated...

again thank you!

Jackis
06-13-2006, 06:27 AM
Yes, you are right, we propose to get your framebuffer back to the texture, then access it.

If you're writing wrapper like that you've told about, so this is the universal case - get screen back and do whatever you want - blur, desaturate, etc...

jtipton
06-13-2006, 10:10 AM
You have to make multiple rendering passes and set the blend equation to perform a sum. You change the colors on the different passes. This will work. I have done it. I'm done with this topic. Good luck raoulduke, you seem to be on the path to at least one solution. Let me know if you want a code sample on how to do this. I can't paste my existing code because it is in a commercial product, so I would have to whip something up.

Jackis
06-13-2006, 10:20 AM
jtipton,

nobody understands exactly what are you talking about.
Could you please answer the Overmind's question: "And how exactly are you proposing to get rc*r + gc*g + bc*b? So far you only described how to get each of those seperately, but not the sum..."

Maybe, you know something, what we don't know!
But right now it seems to me that you haven't got right the raoulduke's problem!

Thank you!

jtipton
06-15-2006, 09:45 AM
See the previous post. The sum is performed through mulitple passes.

Jackis
06-16-2006, 01:16 AM
Okey, so, I'm also done with this topic.

I know, that it is impossible to do luminocity blending on a framebuffer, without using screen textures or color matrices.
You say you do, o'key, we ask you to explain how, and you say nothing. You say - multiple passes, but the problem is that we can't do multiple passes - user has already drawn some picture. Blending can't use different color components in one result component, only the same components.

It is a discussion and help forum, I hope.

jtipton
06-16-2006, 12:11 PM
Send me your email and I will send you a screenshot of luminance retaining color overlays using the blending functionality in OpenGL. This is an approximation to the 'overlay blending' function in Adobe Photoshop. This blending technique is used to overlay a colored material on top of a grey-scale image, without changing the luminance values of the final frame buffer. How is this different, other than only working in a single color channel? Either I am misunderstanding the problem, or you are not familiar with all of the blending capabilities in OpenGL. Here is a piece of the code that does this. Like I said before, I can't disclose all of it since it is commercialized.

The code coming into this block draws a grey scale image out to the framebuffer. This code then draws a colored polygon over the image as an overlay. The goal is to change the shape of each color ramp so that the sum luminance value remains the same. The existing luminance values in the framebuffer are accessed through the DST_COLOR parameters.


glPushAttrib(GL_COLOR_BUFFER_BIT | GL_CURRENT_BIT);
{
glColor4fv(inlayColor);
glBlendFunc(GL_DST_COLOR, GL_ONE);
glBlendEquationEXT(GL_FUNC_ADD_EXT);
glDrawArrays(GL_POLYGON, 0, 5);

glColor4fv(inlayInverseColor);
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquationEXT(GL_FUNC_REVERSE_SUBTRACT_EXT);
glDrawArrays(GL_POLYGON, 0, 5);

glBlendFunc(GL_DST_COLOR, GL_ONE);
glBlendEquationEXT(GL_FUNC_ADD_EXT);
glDrawArrays(GL_POLYGON, 0, 5);
}
glPopAttrib();The blending functions are used in multiple passes to achieve the approximation of an exponential color ramp, instead of the default linear color ramps. The sums are computed as the different passes are added or subtracted into the framebuffer. Like I said before, this is very specific to the effect you are trying to achieve. I hope this helps. Again, if you send me your email, I can send you snapshot so that you can see the final result.

eteq
06-18-2006, 11:19 AM
The problem with this method, jtipton, is that the input image is not greyscale - the original user has differend color-component values, but wants to convert those to luminance and THEN do what you're saying. Do you know of a way to do that?

I'm running into exactly the same problem. My solution was to use glReadPixels with the GL_LUMINANCE format argument, which reads out the frame buffer into memory as luminance. then, use glPixelTransfer to set GL_RED_SCALE to whatever the value is supposed to be. Then, read back from memory usinbg glDrawPixels.

The problem with this method is that pixel write/read functions are relatively expensive - for a simple application, there was a factor of 10 slowdown when I added that code...

So to break this down a little, lets try a crucial step that no one's mentioned... Does anyone know if there is any way to change the pixels in the draw buffer into pure luminance values WITHOUT calling glReadPixels and glDrawPixels?

eteq
06-18-2006, 11:24 AM
Oh and I know you can do the GL_LUMINANCE trick with glCopyTexSubImage2D, but I'm wondering if there's a way to do this without using a texture (i.e. copy to an auxiliary buffer or something)

jtipton
06-19-2006, 09:41 AM
If the input is a pixmap you could change the pixel transfer options to achieve this as well.

raoulduke
06-20-2006, 03:05 AM
Thanks for all your Posts - seems to be an interesting Thread! ;-)

eteq: the way you described seems to be what i was searching for in the first place, but unfortunately this seems to be too expensive, as you said.

btw: is there a function that does greyscale-rendering? This could probably help, too.

thanks

jtipton
06-20-2006, 08:21 AM
glDrawPixels or texturing with GL_LUMINANCE or GL_INTENSITY as the format. If you have a greyscale color buffer, everything would be grey, if that is what your are looking for.

Jackis
06-20-2006, 10:25 AM
Or just set-up final combiner to do luminocity color transform before you draw your scene (it is actual only if you scene doesn't enable fragment shaders). I can give an example for nVidia cards using register_combiners extension.

OFFTOPIC: nice played against Ecuador )) don't loose you winning velocity ))

eteq
06-20-2006, 11:40 AM
I tried using glCopyTexImage2D with GL_LUMINANCE, but instead of determining the luminance values (as glReadPixels does with GL_LUMINANCE), it seems to be just using the red value to color the texture... any way around this? And is there some kind of generic register_combiner like the nVidia one (other than an actual shader, of course)?

and jtipton, how would the pixel transfer options help here? The values would have to change for every pixel, which would be very inefficient...

jtipton
06-20-2006, 04:37 PM
Luminance values are not computed for you by using a luminance texture. This means that the data is already in a luminance format.

I don't see why you would need different pixel transfer options for each pixel. You can set up unique options for each color channel. I'm not even sure what this thread is about anymore. I must be on a different planet.

eteq
06-20-2006, 06:37 PM
but glReadPixels DOES do that computation if you specify GL_LUMINANCE - I guess you're right (i.e. its only drawing the first component), but then how do I do the conversion? Is it shader or nothing? k_szczech's post seems to suggest that it is possible...

jtipton
06-21-2006, 11:08 AM
The quick way is to average the r,g,b values. I think this is waht ReadPixels does. There are other ways which include weighting each value according to the sensitivity of the human eye. We are more sensitive to blue than red and green. You can find that through a web search. Not of this will be fast, but it is an alternative to shaders if they are not available.

eteq
06-21-2006, 08:57 PM
I know that's what glReadPixels does - I already said I did that and it works, but reading to memory is slow - I want to do the same thing, but to a texture rather than CPU memory... But it sounds like that just isn't possible without using a fragment shader.