Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Learning shaders, one and for all.

This thread is locked; no one can reply to it. rss feed Print
Learning shaders, one and for all.
nshade
Member #4,372
February 2004

So in the game I'm porting, I have a function where I need recolor pixels in a particular area except for black. It looks something like this

Recolor (x1,y1,x2,y2,color);

Where x1,y1 is the upper left corner of the area and x2 and y2 is the lower left. Now, how I'm doing it now, is copying the area from the screen to a temp bitmap, and then using a for/next loop to check the color of every pixel and then set that pixel color if it's not black. After that is done, I copy that bitmap section back to the screen.

It's super slow, and large bitmaps are causing my game to drop to below 5 FPS. That in itself is strange because, I mean, I'm pushing 3.4Ghz here and someting a trivial as changing a pixel color is bogging down my system. I've been told that I should be using a pixel shader, and doing a quick skims about them they seem over complicated for what I'm trying to accomplish. But now it's serously impacting my game and I need to learn them once and for all.

The problem I'm having is that every time I try and research info about what a pixel shader it's either really vague or goes in some crazy OpenGL/Direct3D low level graphics card mathematics junk.

The Allegro API is not helpful. It shows me how to make a shader object, but then say I need to attach the source code to a shader to make it work. What source code? What language? How do you "program" a shader? When I look up a shader language it, it gives me like 30 of them with stuff like "fragmets" and you have to pass vectors and stuff. It seems so complex.

I guess the upshot is, can someone show me the "front door" on all of this. No 3D OpenGL/DaretX stuff, no low level graphics card gunk, I don't need "vectors" or "fragments" or "tessellation", I am just looking for a fast way to recolor an area of the screen to another color (except for black).

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

What you have to realize is that allegro uses video bitmaps by default. This means they are stored on the GPU. Whenever you modify a video bitmap by using al_lock_bitmap, it has to download the texture region you lock off of the video card. Once you're done modifying it, you have to upload it back to the gpu. This is very slow.

You can use a shader to alter the color for sure. Trouble comes when you need to read that data off of the gpu. This is where the bottleneck comes from.

nshade said:

The Allegro API is not helpful. It shows me how to make a shader object, but then say I need to attach the source code to a shader to make it work. What source code? What language? How do you "program" a shader? When I look up a shader language it, it gives me like 30 of them with stuff like "fragmets" and you have to pass vectors and stuff. It seems so complex.

You'll find the manual answers several of your questions.

https://liballeg.org/a5docs/trunk/shader.html

There is an example program called ex_shader.c that demonstrates simple use of a shader.

https://github.com/liballeg/allegro5/blob/master/examples/ex_shader.cpp

The source code for the shaders used is in the data directory.

https://github.com/liballeg/allegro5/tree/master/examples/data

You should really try to learn this yourself.

However, I will try to make a simple demo sometime later today.

kenmasters1976
Member #8,794
July 2007

Sounds like you want to do a palette effect. Those were quite easy to achieve in the past with Allegro 4 but on modern graphics using hardware acceleration I guess shaders are the only way to go.

Someone posted this page a while ago, it has a good introduction to shaders and it should be easy to adapt to using the Allegro shader addon.

Also, have you tried locking your bitmaps before manipulating the pixels?.

nshade
Member #4,372
February 2004

This helps actually. It looks like shaders are linked to a language called "glsl" and I should be looking at that. The "vectors" are just ways of addressing variables.
Looking for "glsl tutorial" is being much more fruitful then "shader tutorial" which was way much more technical.

Looking at allegro, it appears that you write a glsl program and then attach that to Allegro. Then you turn it on when you want to use it.

I even found a hello world program and a canvas to experiment with on online. (Evidently you can do shaders in a web browser!) neat...

As for locking the surfaces.. Yea, I do that... Here is the function in question

#SelectExpand
1*! 2* \brief Replace a color a particular region, ignoring black 3* \param d Destination buffer 4* \param s Source buffer 5* \param dx Destination X 6* \param dy Destination Y 7* \param sx Source X 8* \param sy Source Y 9* \param w Width 10* \param h Height 11* \param col1 Color to replace 12* \param col2 Color to replace it to 13*/ 14void ReplaceColor(int d, int s, int dx, int dy, int sx, int sy, int w, 15 int h, int col1, int col2) 16{ 17 ALLEGRO_BITMAP *replacebuf; 18 replacebuf = al_create_bitmap(GameWidth, GameHeight); 19 20 //The native resolution is 1280x800 which is 4x the original 320x200 resolution 21 int scaledSrcX = sx * 4; 22 int scaledSrcY = sy * 4; 23 int scaledDestX = dx * 4; 24 int scaledDestY = dy * 4; 25 int scaledW = w * 4; 26 int scaledH = h * 4; 27 28 int maskx; 29 int masky; 30 unsigned char r1, g1, b1; 31 unsigned char r2, g2, b2; 32 unsigned char r3, g3, b3; 33 34 //deconstruct color 1 and 2 35 al_unmap_rgb(palette[col1], &r1, &g1, &b1); 36 al_unmap_rgb(palette[col2], &r2, &g2, &b2); 37 38 al_set_target_bitmap(replacebuf); 39 al_clear_to_color(al_map_rgb(0, 0, 0)); 40 al_draw_bitmap_region(bufmap[s], scaledSrcX, scaledSrcY, scaledW, scaledH, scaledDestX, scaledDestY, 0); 41 42 al_lock_bitmap(replacebuf, ALLEGRO_PIXEL_FORMAT_ANY, ALLEGRO_LOCK_READWRITE); 43 for (masky = 0; masky < scaledH; masky++) 44 { 45 for (maskx = 0; maskx < scaledW; maskx++) { 46 //get pixel color 47 masktemp = al_get_pixel(replacebuf, maskx + scaledDestX, masky + scaledDestY); 48 al_unmap_rgb(masktemp, &r3, &g3, &b3); 49 if (!(r3 == 0 && g3 == 0 && b3 == 0)) { 50 if (r3 == r1 && g3 == g1 && b3 == b1) { 51 al_put_pixel(maskx + scaledDestX, masky + scaledDestY, palette[col2]); 52 } 53 } 54 } 55 } 56 57 al_unlock_bitmap(replacebuf); 58 59 al_set_target_bitmap(bufmap[d]); 60 al_draw_bitmap_region(replacebuf, scaledDestX, scaledDestY, scaledW, scaledH, scaledDestX, scaledDestY, 0); 61 al_set_target_bitmap(al_get_backbuffer(DISPLAY)); 62 if (d == SCREEN) { UpdateScreen(); } 63 al_destroy_bitmap(replacebuf); 64}

Edgar Reynaldo
Major Reynaldo
May 2007
avatar

https://learnopengl.com/Getting-started/Shaders has a good intro to shaders.

As for why your program is slow, there are several reasons.

1) You're creating a temporary ALLEGRO_BITMAP* buffer every time you draw something in a different color. The constant allocation and destruction of this buffer slows your code down.

2) The buffer is unnecessary, because you're using a locked region.

3) ALLEGRO_LOCK_READWRITE causes the texture to be downloaded off the gpu on locking, and uploaded back to the gpu when unlocked. This is slow. Reading causes download. Writing causes upload.

4) Just lock bufmap[i] directly, using only the region you need to recolor.

5) A shader could replace the color as it is drawn on the gpu, which would avoid all this nonsense altogether. It's perfect for what you want.

Go to: