CLUT Table for Color Cycling Effects?
BlitzMax Forums/BlitzMax Programming/CLUT Table for Color Cycling Effects?
| ||
Let's say I want to have a copyrect that checks for certain colors and replaces them based on a table (rgb 255,255,254 -> 255,0,0) with that table capable of being changed at runtime. There are a number of ways I've thought of for doing this including precalculating and caching all permutations of the table for given sprites, walking an array containing the image and using drawrect for each pixel, etc (graphics is pixel graphics and the sprites I want to do this with are limited in size so this method isn't as bad as it seems.) Another possibility is using masking and moving around a predrawn plasma bitmap behind the image to mask with. A big part of the problem with the other methods is that they involve a lot of SetColor calls, not to mention a whole lot of drawrects. Any ideas? It's funny that per-pixel color cycling was for so long such a low-rent effect that was taken for granted but now it's not quite so easy because for support in hardware you have to use shaders and I don't want to go that far... or maybe there is a simple way using a postprocess effect? Inquiring minds want to know. One little piece of source I had was this, I don't know where I found this but it doesn't look like my coding style. [Turns out it's Maverick69, here: http://www.blitzbasic.com/codearcs/codearcs.php?code=2083] This doesn't do cycling but it does smooth fading between RGB values. Probably I should just use tileimage with a prerendered plasma and then use masking and call it good. However, the masking method does not do everything I would like it to, like plot on top of detailed backgrounds. Maybe I could grabimage a piece of the background, draw with maskcolor on top of that, draw a rectangle of plasma the same size, then drawrect the masked background piece on top of that. But there you're running into the slowness of grabimage and that's just doubleplus ungood (if you speak C++ or 1984...) At a certain point of diminishing returns with that you're hands down better off with grabpixel, setcolor and plot and that's really saying something. !!?!!?! |
| ||
Well, here is my solution. I get semi-acceptable framerates (around 40 fps) for a whole-screen write from an array with drawrect, with doubled pixels, but that's overkill if only some things are going to be cycling, if you want different things cycling at different rates, etc. So I wrote a sprite class that stores only the pixels you want in a list. Then you call the update method on your sprite instance and all the pixels get cycled depending on your update algorithm. There may or may not be a better way to do this but it's really just for small throwaway effects like little numbers that appear and flash for a moment if you get some bonus or something like that. For that, the speed hit should be negligible. If you wanted to get complicated you could have different colors in the source image get loaded into different colorsprites with different cycling algorithms. Writing this stuff is one thing, figuring out what to do with it is much harder. Should also have a color cycling table, a parameterized version of the copperlist builder from the previous post, so the colors are hand picked. ' colorcycling by list Type Tcolor Field r:Int, g:Int, b:Int Method setcolorto() SetColor r,g,b End Method Method set(myr:Int,myg:Int,myb:Int) r=myr g=myg b=myb End Method End Type Type Tcoord Field x:Int, y:Int End Type Type Tcolorcoord Field mycolor:Tcolor=New tcolor Field mycoord:Tcoord=New tcoord Method setcoord(xx:Int,yy:Int) mycoord.x = xx mycoord.y = yy End Method Method setrgb(r:Int,g:Int,b:Int) mycolor.r=r mycolor.g=g mycolor.b=b End Method Method update() mycolor.r = mycolor.r+4 If mycolor.r > 255 mycolor.r = 0 mycolor.g = mycolor.g+8 If mycolor.g > 255 mycolor.g = 0 mycolor.b = mycolor.b+10 If mycolor.b > 255 mycolor.b = 0 End Method End Type Type Tcolorsprite Global colcoordlist:TList = New TList Field tempcoord:tcolorcoord = New tcolorcoord Method addcoord(xx:Int,yy:Int,r:Int,g:Int,b:Int) tempcoord = New tcolorcoord tempcoord.setcoord(xx,yy) tempcoord.setrgb(r,g,b) colcoordlist.addlast(tempcoord) End Method Method addcoordbytcolor(xx:Int,yy:Int,t:tcolor) ' Same as the above but takes a TColor object instead of r,g,b tempcoord = New tcolorcoord tempcoord.setcoord(xx,yy) tempcoord.setrgb(t.r,t.g,t.b) colcoordlist.addlast(tempcoord) End Method Method updatepixels() ' must be run every tick for cycling to take place For tempcoord=EachIn colcoordlist tempcoord.update() Next End Method Method draw(x:Int,y:Int) For tempcoord=EachIn colcoordlist SetColor tempcoord.mycolor.r, tempcoord.mycolor.g, tempcoord.mycolor.b DrawRect tempcoord.mycoord.x + x, tempcoord.mycoord.y+y, 3,3 Next End Method End Type Graphics 1024,768 'Draw some Text for us to grab SetScale 6,6 DrawText "Too Convoluted X 2",0,0 SetScale 4,4 DrawText ("But what the hell...", 0, 100) DrawText ("Seems to work OK!",0,150) SetScale 1,1 test:tcolorsprite = New tcolorsprite tempTcolor:tcolor = New tcolor ' temp TColor for speed For i = 0 To 1024 Step 4 ' Grab area of screen where we drew For j = 0 To 200 Step 4 tempTcolor = grabpixel(i,j) ' get the color If tempTcolor.r>0 And tempTcolor.g > 0 And tempTcolor.b > 0 ' if it's non-zero tempTcolor.set(Cos(j)*128,Sin(i*4)*128,128) ' set your tempcolor test.addcoordbytcolor(i,j,tempTcolor) ' and add it to the list EndIf Next Next While Not KeyDown(KEY_ESCAPE) Cls test.updatepixels() test.draw(MouseX(),MouseY()) Flip Wend Function GrabPixel:tcolor(x:Int,y:Int) ' Function by Plash Local temp:TPixmap, rgba:Int Local pixcolor:Tcolor = New Tcolor temp = GrabPixmap(x,y, 1, 1) rgba = temp.ReadPixel(0, 0) temp = Null SetColor(255, 255, 255) pixcolor.set (RGBA_GetRed(rgba), RGBA_GetGreen(rgba),RGBA_GetBlue(rgba)) Return pixcolor End Function Function RGBA_GetRed:Int(rgba:Int) Return((rgba Shr 16) & $FF) End Function Function RGBA_GetGreen:Int(rgba:Int) Return((rgba Shr 8) & $FF) End Function Function RGBA_GetBlue:Int(rgba:Int) Return(rgba & $FF) End Function Function RGBA_GetAlpha:Int(rgba:Int) Return((rgba:Int Shr 24) & $FF) End Function Hopefully someone gets benefit out of this but probably not, it's probably too late in the day for this sort of thing to be useful. Hey, 1988 seems farther away every year. |
| ||
I can't test it, since I don't have BlitzMax, but it reminds me of that: http://www.effectgames.com/effect/article.psp.html/joe/Old_School_Color_Cycling_with_HTML5 I read a comment about it that should apply to your solution, too: A slow emulation of a low-tech technique in high-tech to demonstrate what's possible in low-tech BTW, color cycling was a great solution, it allowed on a limited hardware effects that now can be done only with particle systems (using a lot of system resources) |
| ||
It works really well, apart from the massive delay at the start while it grabs the pixels! I'd imagine it would be faster to use an array than a list, but I didn't look too deeply into it. Looks cool, though! |
| ||
I was actually really surprised how fast it is considering it's linked lists. FWIW the grabpixel delay is negligible on a Mac Mini and takes about 4x as long on a quad core i5. I'd imagine it would be faster to use an array than a list, but I didn't look too deeply into it. The problem with that though is that you have to walk a lot of pixels that are black / transparent and decide at runtime whether to plot them or not, or plot them all and use transparency. The bottleneck seems to be the drawrect calls, not the list lookups though that certainly can't help. Of course you could use an arraystack of coords or even five arrays, one each for x,y, r, g and b, and that would probably be the fastest but I don't think the list itself is a particularly bad hit. For the update() and the draw() I'm getting around 1 millisecond total and a raw array walk was 25-30 though it was the full screen area rather than only a part. The inspiration for this is stuff like and demos that are extremely eyepopping and cool especially when you consider the negligible hardware available, something like 1/10,000 what we have on our desktops if there's even any sort of measure at all. As you say Angros it's pretty funny not only that we have to go through contortions to emulate this stuff on modern hardware and that it's surprisingly taxing even then. There's got to be some way to come up with a color cycling shader but that's even MORE ridiculous and difficult that we have to bring to bear the graphics super-ultracomputers that we have as piddly $200 daughterboards. I still don't know how these guys got stuff looking so good on hardware that old but their techniques tend to be pretty simple and the looks are the result of artistic and design talent, which are the things the runners up didn't have. Since this is going to go through a cycle table and is mainly for pretty small sprites instead of full screen crazy demo effects, the next step is to precache the cycling steps but it's probably not worth bothering with considering that they won't be close to filling up the screen. Coppermaster by Corsair above is arguably one of the best demos ever released in terms of design and inventive workarounds and it's aged very well considering it's 25 years old, much better in fact than the demos which used "cutting edge" 3D graphics which are basically unwatchable these days. A recursive partitioning method that would fit the largest possible rects for a given space and then draw rects of that size would vastly decrease the number of draw calls. As for the initial setup time for the grab, perhaps the data structure can be pregenerated and incbinned or stored on disk. That is probably what I will end up doing. Of course what the Old Guard would have done would have been to display a picture (usually pixelpainted by a precocious thirteen year old hanger-on from the BBS) or other sort of flash during the grab which would be easy to do here considering that the pixels are loaded into the data structure piecemeal. You could easily do it during a menu screen. |
| ||
As far as I know, there was an openGL extension that allowed color cycling: https://www.opengl.org/registry/specs/EXT/paletted_texture.txt unfortunately, most openGL drivers don't implement it. I like these old-school tricks, too, and some years ago I wrote a small article about them: http://back2basic.phatcode.net/?Issue_%233:Retro_Tricks http://back2basic.phatcode.net/?Issue_%234:Retro_Tricks_II Another cool effect is the "3d tunnel" effect: http://freebasic.net/forum/viewtopic.php?f=7&t=22884 Is there something similar in BlitzMax? |
| ||
No, I haven't seen tunnel code. I'd be interested in seeing your retro tricks examples implemented in blitzmax. 2D flashery isn't over with yet, there's a lot of life left in it especially on mobile devices which are only going to get more common in the next few years. Angry Birds made BILLIONS OF DOLLARS. Here's a stab at the 3D cube but my brain isn't working correctly yet this morning pre-coffee, the cube flashes for a moment and disappears. ' Vector Test Graphics 640,480 Global x, y, z, x0, y0, s Global rx, ry, rz While Not KeyDown(KEY_ESCAPE) rx = rx + .01 rz = rz + .001 Cls SetColor 0,0,0 Local a = Cos(rz) * Cos(rx) Local b = Sin(rz) * Cos(rx) Local c = Sin(rx) Local d = -Sin(rz) Local e = Cos(rz) Local f = 0 RestoreData thedata While x > -1000 ReadData x, y, z s = 1 oldx = x0 oldy = y0 x0=(x*a + y*b + z*c) / s y0=(x*d + y*e + z*f) / s DrawLine x0,y0,oldx,oldy SetColor 0,255,0 Wend Flip Wend #thedata DefData -50, 0, -50 DefData 50, 0, -50 DefData 50, 0, 50 DefData -50, 0, 50 DefData -50, 0, -50 DefData -50, 100, -50 DefData 50, 100, -50 DefData 50, 100, 50 DefData -50, 100, 50 DefData -50, 100, -50 DefData 50, 0, -50 DefData 50, 100, -50 DefData 50, 0, 50 DefData 50, 100, 50 DefData -50, 0, 50 DefData -50, 100, 50 DefData -1000, 0, 0 |
| ||
Would be so much faster as a shader. |
| ||
I agree! I would love to see someone implement it, also see my Copper Bars post. I'm just happy that it works at all and again for my purposes, in-game at least, I'll probably be working with a total pixel area of 320x32 or something like that which will be negligible even in software. At some point I should stop being a retro-luddite and get with the program. My GL skills (I actually do know some OpenGL) are fixed pipeline and I should sit down with books and remedy that. |
| ||
No, I haven't seen tunnel code There's a tunnel effect here -- in OpenGL, no less. |
| ||
Isn't there a function to draw right out of an array as if it were a bitmap? |