-
OpenGL: Pixel addition
Hey guys,
I'm trying to figure out a way to render content in my scene such that it gets rendered *additively*. I've been experimenting with glBlendFunc() and glTexEnvi(), but I always seem to end up with an ordinary transparency effect with the resulting pixel value somewhere between the source and destination.
Can someone post a snippet of code with working pixel addition, i.e. where the resulting pixel is *brighter* than either the source or destination? I'm sure it's not as difficult as I'm making it, but I can't quite get it right.
-
Additive blending is as simple as:
Final = Pixel1 + Pixel2;
This will cause values to go out of range very fast and usually this type of operation will need some limits and/or coefficients imposed on it. You could only add 50% of pixel1 with 50% of pixel2 and so on.
I'm not sure how to do it in OpenGL but in Direct3D HLSL it's as simple as this:
Code:
float4 PixelShader(float2 texture_uv : TEXCOORD0,float2 texture2_uv : TEXCOORD1) : COLOR
{
float4 texcolor1 = tex2D(TextureSampler1,texture_uv);
float4 texcolor2 = tex2D(TextureSampler2,texture2_uv);
return (texcolor1 + texcolor2);
}
You could also do an exposure filter:
Code:
float4 PS(float2 tex : TEXCOORD0) : COLOR
{
return 1.0f - exp(-tex2D(ColorMapSampler,tex) * exposure) * 1.75f;
}
This is:
1.0f - e^(-color * exposure) * <some_brightness_constant>
If you are doing a post process bloom you can set exposure fairly high and the bloom filter will take care of the rest. Without shaders you will have to perform this operation on each channel of the color. This shader also assumes the human eye sees RGB in equal amounts which is not true in real life.
Note that the following color channels are in range 0.0f to 1.0f to make things much simpler.
In C++ code additive pixel blending is:
Code:
RGB color1;
RGB color2;
RGB final_color;
final_color.red = color1.red + color2.red;
final_color.green = color1.green + color2.green;
final_color.blue = color1.blue + color2.blue;
Color modulation (dark colors in color2 darken the final color, light colors in color2 do not affect the final color since any number * 1.0f is the same number)
This is also known as light mapping.
Code:
RGB color1;
RGB color2;
RGB final_color;
final_color.red = color1.red * color2.red;
final_color.green = color1.green * color2.green;
final_color.blue = color1.blue * color2.blue;
Linear interpolation between colors:
Code:
float lerp(float v1,float v2,float lerp)
{
return v1 + lerp * (v2 - v1);
}
RGB color1;
RGB color2;
RGB final_color;
float lerp_factor = 0.5f;
final_color.red = lerp(color1.red,color2.red,lerp_factor);
final_color.green = lerp(color1.green,color2.green,lerp_factor);
final_color.blue = lerp(color1.blue,color2.blue,lerp_factor);
Color averaging:
Code:
RGB color1;
RGB color2;
RGB final_color;
final_color.red = (color1.red + color2.red) * 0.5f;
final_color.green = (color1.green + color2.green) * 0.5f;
final_color.blue = (color1.blue + color2.blue) * 0.5f ;
Bilinear interpolation of 4 colors
Code:
float lerp(float v1,float v2,float lerp)
{
return v1 + lerp * (v2 - v1);
}
RGB color1;
RGB color2;
RGB color3;
RGB color4;
RGB temp_color1;
RGB temp_color2;
RGB final_color;
float lerp_factor = 0.5f;
temp_color1.red = lerp(color1.red,color2.red,lerp_factor);
temp_color1.green = lerp(color1.green,color2.green,lerp_factor);
temp_color1.blue = lerp(color1.blue,color2.blue,lerp_factor);
temp_color2.red = lerp(color3.red,color4.red,lerp_factor);
temp_color2.green = lerp(color3.green,color4.green,lerp_factor);
temp_color2.blue = lerp(color3.blue,color4.blue,lerp_factor);
final_color.red = lerp(temp_color1.red,temp_color2.red,lerp_factor);
final_color.green = lerp(temp_color1.green,temp_color2.green,lerp_factor);
final_color.blue = lerp(temp_color1.blue,temp_color2.blue,lerp_factor);
Ok so I don't have any OpenGL code. But hopefully the color formulas can help later on.
-
Additive blending in OpenGL: glBlendFunc(GL_SRC_ALPHA, GL_ONE) -- as far as I could work out :-). [edit]Oops, you want brighter.[/edit]
Or you can read from the frame buffer, just don't try and call glReadPixels() many times per frame if you're wanting the destination pixel colour. It's very slow.
-
Heh thanks Bubba, I understood this much already ;)
zacs:
GL_SRC_ALPHA, GL_ONE should theoretically be (Cs * As) + Cd, right? In which case I'm pretty sure it should work.
My test code is the following:
Code:
glClearColor(0,0,0,1);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glColor4f(1,1,1,.19f); //.19f just on the offchance that overflow mucks things up. 0.2f has same result.
drawUntexturedSquareAtOffset(0);
drawUntexturedSquareAtOffset(0.1);
drawUntexturedSquareAtOffset(0.2);
drawUntexturedSquareAtOffset(0.3);
drawUntexturedSquareAtOffset(0.4);
What I expect to see is 5 overlapping squares spaced 0.1 apart, with the overlapping region going progressively from near-black to near-white as I move from left to right. While the squares do show up and the overlap gets progressively brighter, it ends at a dark gray color instead of white. The same happens if I use glColor4f(0.2,0.2,0.2,1).
If I use glColor4f(0.4,0.4,0.4,1) then it simply reaches the 'max' dark gray value on the 2nd or 3rd square and remains at that color for the 4th and 5th. However if I use glColor4f(0.7,0.7,0.7,1) then it seems to saturate at a higher brightness, all the way up to (1,1,1,1) where everything is saturated at white.
If I add either of the following lines, while setting glColor4f(0.2,0.2,0.2,1):
Code:
glTexEnvi(GL_TEXTUER_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //default GL setting
or
glTexEnvi(GL_TEXTUER_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
or
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
GL_ADD causes it to hit white on the 4th square (i.e. early), but GL_REPLACE seems to give the desired/expected result. What I don't understand is why GL_MODULATE causes the color to hit saturation at a dark gray. I don't have any lighting enabled, and nothing else is rendered to the screen.
I really gotta get this sorted out, it's driving me nuts. Thoughts please?
-
I still don't know what was wrong. But, I created a new project, and rewrote the damn thing while testing it line by line, and it worked. *shrug*