PDA

View Full Version : Changing pixel shades.



Alfryd
03-03-2009, 03:55 PM
I'm trying to put together a relatively simple isometric-perspective game, and I'd like the ability to 'ghost out' various portions of the screen as part of UI feedback. Specifically, when the player is trying to place a specific building, I'd like to have the building-preview (and possibly underlying terrain) turn red or green depending on whether it's over a legal or illegal location. (I mean imagine a standard RTS, where you're placing a new building- the little image of the building will turn different colours depending on where you're trying to put it.) How would I go about this?

ZbuffeR
03-03-2009, 04:10 PM
This is quite easy to do with glcolor.
Set it to white for normal textured visual.
Set it to greenish to modulate the texture color to green.
You can even use some alpha transparency when the building is not yet created.
Something like :
glColor4f(1.0f,0.4f,0.4f,0.5f); // illegal place
glColor4f(0.4f,1.0f,0.4f,0.5f); // legal place
glColor4f(1.0f,1.0f,1.0f,1.0f); // placed building

Alfryd
03-04-2009, 12:30 AM
Thanks for the tip, but I actually need something a little more complex- I mean, if the object in question has no R component to the texture, glColoring it red means it'll turn black. What I actually need to do is average the R, G and B values for a given pixel, and *then* set the intensity of colour (R, G or B) on that basis. I was hoping there'd be some simple way to do this with the stencil/accumulation buffers...

ZbuffeR
03-04-2009, 02:20 AM
Avoid accum unless you have a real need, it will be slower.
I don't see how stencil could help by the way.

With glsl shaders this kind of stuff is very very easy to do.
Without, you should have a look to the other texenv modes (modulate is simply the default) :
http://www.opengl.org/sdk/docs/man/xhtml/glTexEnv.xml
The GL_BLEND looks interesting.

For real conversion to grayscale, look at this, there is a good sum up of all your alternatives :
http://www.pouet.net/topic.php?which=5167

enunes
03-04-2009, 02:25 PM
Hi,

sorry, by what i understood its something simpler.

you mean 'ghost out', do you mean you need "transparency"?

if so, take a look at Blending, and it's quite easy to set up the standard way..

if that's not it, nvm :P

Alfryd
03-04-2009, 07:15 PM
No, I don't mean transparency. Thanks anyway.

I don't see how stencil could help by the way.
Well, in theory I'd use the stencil buffer to designate which pixels on-screen need to have their colour tinted.

I'll definitely take a look at the other texenv modes, but I really don't wan to use shaders unless absolutely necessary. I've seen game 10 years old do this trick, so there must be some old-fashioned workaround.

ZbuffeR
03-05-2009, 02:23 AM
Do you have screenshots of these games ?
The easiest way, as explained on the pouet link, is the generate beforehand the grayscale version of the texture.

Alfryd
03-05-2009, 12:06 PM
Well, I'm pretty sure Caesar III (http://en.wikipedia.org/wiki/Caesar_III) did the trick, and that's 10 years old now. So did majesty. And they weren't even properly 3D.

I really don't want to have to include (or generate) duplicates of every building/terrain texture before use, given this should be so simple.
On another forums, it's been suggested I use glTexEnv(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB... but I'm a little confused about how the parameters would be given?

Also, is there no way of doing this pixel-by-pixel, rather than texture by texture?

ZbuffeR
03-05-2009, 02:03 PM
pixel-by-pixel : old style is unaccelerated CPU doing the work. New style is shaders.

I recommend GLSL over texture/env/combine etc any time, unless you like pain.

You have to understand that some things are easy in CPU world, but hard in the GPU world. And reverse is true too.

Are you constrained by the texture requirements ?
Luminance textures are only 1/4 the size of rgba, and can be easily generated at runtime :

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,GL_RGBA,GL_UNSIGNED_BYTE, buf); //load color texture

glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE8, width, height, 0,GL_RGBA,GL_UNSIGNED_BYTE, buf); // load same data as grayscale

S.Seegel
03-05-2009, 04:55 PM
My recommendation would be to use shaders, too.

But, for the sake of completeness, it can be done pre-shader-style :

With the already mentioned additional precomputed gray scale textures.

With GL_ARB_imaging (does any hardware support this ?), you can use the color matrix.

With texture_env_combine and texture_env_dot3, using two one-pixel textures and four additional texture stages. (At least thats what I came up with, out of curiosity. ;) )

But you should _really_ consider using shaders.

Alfryd
03-07-2009, 05:50 AM
Here's the other topic (http://www.javagaming.org/index.php/topic,20010.0.html).
"If by "practical" you mean "won't work on the majority of laptop computers" then yes, using a shader is practical."


Luminance textures are only 1/4 the size of rgba, and can be easily generated at runtime :
That's a good point, actually. I might try that out.

Alfryd
03-08-2009, 03:21 PM
No, wait, actually- luminance textures are no good to me, I need to keep the alpha channel. Well, shoot.

ZbuffeR
03-08-2009, 04:58 PM
GL_LUMINANCE8_ALPHA8 instead of GL_LUMINANCE8

You know this useful link, right ?
http://www.opengl.org/sdk/docs/man/

Alfryd
03-08-2009, 05:51 PM
Sort of (http://www.opengl.org/documentation/specs/version1.1/glspec1.1/node1.html)... but thanks for the ref, alphabetic lookup could be handy. :)

Problem is LUMINANCE8_ALPHA8 would use 2 bytes/pixel, so the memory saved is minimal.

Wait a minute- could I use readPixels (http://www.opengl.org/documentation/specs/man_pages/hardcopy/GL/html/gl/readpixels.html) with GL_LUMINANCE, plus the stencil buffer, to blit pixels right back to the screen in tinted-greyscale format? Read to a fullscreen texture, or something?

S.Seegel
03-09-2009, 02:13 PM
Although this should work, it will certainly result in a performance hit.
Copying data back and forth between system and graphics memory is never a good idea.

Is multitexturing in combination with texture_env_combine and texture_env_dot3 (all core since opengl 1.3) an option ?

Alfryd
03-13-2009, 05:55 AM
Sorry for the delay in replying- yeah, it's been suggested that I use texture_env_dot3 for this, but it would help if I had an exact code snippet to look at. I think that should work.

S.Seegel
03-16-2009, 01:27 PM
Yes, it works. Kind of ;)

The described effect can be done in a few post-processing steps, which means you can render your models "as normal" (using whatever lighting and/or texturing configuration).

The first post-processing step is a conversion to grayscale. This is done as a weighted sum of the fragments red/green/blue components:
gray = 0.299*red + 0.587*green + 0.114*blue ,
which looks exactly like dot product.
Unfortunately, texture_env_dot3 doesn't simply compute a dot product, but
4*((Arg0_r - 0.5)*(Arg1_r - 0.5) + (Arg0_g - 0.5)*(Arg1_g - 0.5) + (Arg0_b - 0.5)*(Arg1_b - 0.5)), the result being placed in red/green and blue.
(Just in case you're curious: the extension was introduced to allow an effect called dot3 bump mapping, which requires those extra computations due to the way normals were stored in textures).

So in order to compute a normal dot product, the operands have to be properly mapped (multiplied by 0.5 and 0.5 added), so the additional computations done by the dot product are nullified.

So a first texture stage will multiply the colour by 0.5,
a second stage will add 0.5, and
a third will computed the dot product with { 0.299*0.5 + 0.5, 0.587*0.5 + 0.5, 0.114*0.5 + 0.5 }

A last (fourth) texture stage can then modulate the grayscale value by whatever colour you want.

So far the theory. In practice I came across a problem with my opengl driver (ati catalyst 9.2 on linux x86_64):
it was not possible to change the operands at the individual texture stages to something different than the default GL_TEXTURE (Arg0) and GL_PREVIOUS (Arg1) (more precisely it was not possible to use GL_CONSTANT instead of GL_TEXTURE).
So I ended up using one-texel-textures instead of constants.

So this is what the texture stages setup looks like (assuming only one stage for normal operation, stages 1 to 4 used for post-processing):

/*** prepare parameter textures ***/
glActiveTexture( GL_TEXTURE1 );

GLuint tex[4];
glGenTextures( 4, tex );

glBindTexture( GL_TEXTURE_2D, tex[0] );
GLfloat half_data[] = {0.5f, 0.5f, 0.5f};
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_FLOAT, half_data );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glBindTexture( GL_TEXTURE_2D, tex[1] );
GLfloat grayscale_factor[4] = { 0.299f*0.5f + 0.5f, 0.587f*0.5f + 0.5f, 0.114f*0.5f + 0.5f };
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_FLOAT, grayscale_factor );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glBindTexture( GL_TEXTURE_2D, tex[2] );
GLfloat illegal_colour[4] = { 1.0f, 0.4f, 0.4f, 0.5f };
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_FLOAT, illegal_colour );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

glBindTexture( GL_TEXTURE_2D, tex[3] );
GLfloat legal_colour[4] = { 0.4f, 1.0f, 0.4f, 0.5f };
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_FLOAT, legal_colour );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );


/*** setup texture stages ***/
/*** (1) color * 0.5 ***/
glActiveTexture( GL_TEXTURE1 );
glBindTexture(GL_TEXTURE_2D, tex[0] );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glEnable( GL_TEXTURE_2D );

/*** (2) color + 0.5 ***/
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, tex[0] );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
glEnable( GL_TEXTURE_2D );

/*** (3) color dot greyscale_factor ***/
glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, tex[1] );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE );
glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_DOT3_RGB );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB, GL_TEXTURE );
glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB, GL_PREVIOUS );
glEnable( GL_TEXTURE_2D );

/*** (4) add single color back ***/
glActiveTexture(GL_TEXTURE4);
//glBindTexture( GL_TEXTURE_2D, tex[2] );
glBindTexture( GL_TEXTURE_2D, tex[3] );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glEnable( GL_TEXTURE_2D );

Alfryd
05-21-2009, 02:05 AM
Whoa! I missed this... Sorry, I've been kind of busy.

Thanks a lot for the code sample- I'll be sure to try it out once I get the chance. It's also occurred to me that I could use a luminance texture as a decal on the original sprite (assuming no partial transparency is involved,) which might be simpler...
Thanks either way- I'll post again once I have some working solution done.