How This Document Is Organized
First of all, the methods for translucency described within this document may not be the only methods. The methods described are the ones that I have discovered, developed and explored on my own. Second of all, I am not responsible for what ever happens to you or your property (or anyone else or their property) because of the use of this package. Use at your own risk. Any trademark mentioned in this package is the property of their respectful owner. You have the right to distribute this package, so long as you distribute it in it's entirety, without modification. You have the right to use this package in the development of commercial/shareware/freeware products without royalties, so long as the author is given credit. This package is owned and maintained solely by the author, Jesse Towner.
2. Included Files
Download : trans.zip
You should have gotten the following files with this package:
NOTE!: When using the example program, push the ESC key to skip through each section and use the arrow keys to move the current object around.
Welcome to my first tutorial package to do with computer programming. I have decided to start with color translucency as I've found that not very many people know how to do this. Hell, I've even talked to people that don't think it is possible to do, in software, running in an 8 bits per pixel graphics mode; however, just look at StarCraft, by Blizzard Entertainment, to prove to yourself that it is possible! Besides, translucency and various other lighting effects associated with it can make a great addition to your graphics library!
I assume you have knowledge of basic graphics programming such as drawing primitives, clearing the screen, drawing pictures, drawing sprites, that sort of thing. I also assume you can write code in C because that's what I'm writing the examples with. In fact, almost all of the code is in C except for some of the drawing routines, which I will provide in x86 Assembly Language using the inline assembler. Normally, I code all of the drawing functions in external assembly (including clipping), since I'm an ASM freak ;-). Also, I'm writing this code for Watcom v11.0 (sorry, the inline "_asm" statement isn't available in previous versions) DOS 32-bit Protected Mode. However, it should be very simple to port it to other platforms and compilers. I'm only going to be using Mode 13h, since it is the simplest video mode to code for. Personally, I use various other standards (don't you just love the amount of standards out there!) such as the VESA BIOS Extension and the 3Dfx Glide v2.0 SDK for my old DOS graphics library. But DOS is unfortunately dead as far as the software market is concerned. Sure, DOS is still a good learning platform, but if you're looking at getting a job as a PC computer programmer, you're going to have to learn Win32 coding at some point. The reason why I chose DOS as the platform for this package is because I wanted to use Mode 13h for simplicity. Anyway, if you're programming in Pascal, or even in BASIC, and you don't understand C, well I'm sorry, but you're out of luck... Once I started coding in C and C++, I could NOT turn back to Pascal or BASIC: the freedom that C/C++ offers makes Pascal and BASIC seem primitive, boring and just plain stupid. Advice from me to you, learn C and C++, and if you're going to get into serious optimizing, learn x86 and x87 assembly language programming.
Now, I bet you're itchin' to write some heavy duty code utilizing translucency (I am!), but first we're going to give ourselves a little background about translucency. So read on and enjoy!
4. Translucency Primer
Now it's time for some definitions and terminology. Many people are mixing up transparency with translucency and this REALLY bugs me. IT ESPECIALLY BUGS ME WHEN THE BIG-NAME GAME COMPANIES ARE MIXING IT UP CAUSING OTHERS TO USE THEIR DEFINITIONS INSTEAD OF THE ONES IN THE ENGLISH LANGUAGE! So I've taken the liberty to look them up for you. Here are the definitions straight from Webster's Third New International Dictionary that apply to the transmission of light.
By using 0.1% of your brain, you should be able to interpret this. If you can't then you shouldn't be reading this document =). Anyway, you will see that you use transparency when you draw sprites and you skip the zero pixels for example (or whatever your mask value is). Thus, the image behind where these 'zero' pixels should be is completely visible, and therefore the 'zero' pixels are considered transparent. Translucency is when you take two colors and you blend them, drawing the result. Therefore you would take the color of the background pixel that was already there you and blend it with the color of the pixel you want to draw. The end result would be a pixel that has a color somewhere in between the destination pixel color (the pixel that you drew) and the background / destination pixel color.
There are a few names that are given to translucency when applied to computer graphics, here they are:
5. Preparing Oneself To Use Translucency
Before we delve into actually writing the translucency routines, we're going to need some routines to help us out a bit. And with routines comes structures. First of all, we're going to need a simple RGB structure which will hold the RGB values in a palette. Then we're going to need a 256 color palette type.
As you can see, the palette is just an array of type gfxRgb_t. Now we're also going to need a special function that will determine the color in a palette closest to a set of RGB values (AKA RGB tuple). So you call the function passing the RGB values of the color you want and it returns the color value which is the closest match. It doesn't need to be fast, so we're just going to write something that gets the job. Also note that we will only be needing this for 256 color video modes since high/true color modes use direct color mapping.
There we are. As for palette access routines, I will assume you know how to do these. In the example code, I include some routines for accessing the VGA palette, such as GetPalette, SetPalette, GetColor, and SetColor. Now, I believe we can get started on our translucency code.
6. Alpha Blending Demystified
So, you want to learn how to use alpha blending, huh? Well, this is where you're going to learn how. There are two methods to perform alpha blending in software that I know of, alpha blending the color on the fly and using lookup tables. As you may have guessed, the lookup table method is the fastest. However, alpha blending on the fly is quite feasible, so long as you don't use a lot of it, and you have a somewhat fast computer. You can use both methods in all color depths, it's just based on how you set it up. But before we get into the different methods in depth, lets look at how alpha blending is done in general.
To blend two colors together, one must use I N T E R P O L A T I O N. To get an idea of how to, we are just going to interpolate two values, but then we will extend that to interpolate an entire RGB tuple with another. Anyway, lets call these two values "intensities", like the intensities of each vertex on a gouraud or smooth shaded polygon. To get an intensity value at a certain point on a polygon edge, you would interpolate the two intensities (the left intensity i1, and the right intensity i2) along the polygons edge based on the height delta. The same idea is used here too, we interpolate each color's RGB values along the alpha delta. The alpha delta is just the highest alpha value. In our example here, and in the source code provided, the alpha value ranges from 0-255. Therefore, 255 is the highest value, and thus is our alpha delta. So, the difference between the intensities is the dividend, the alpha delta is our divisor, and the resulting quotient is our interpolant.
INTERPOLANT = (i1 - i2) / (255);
But what do we do with the interpolant? Well, we just interpolate it to the position, or in our case, alpha, that we want. To do that, simply multiply the interpolant by the alpha value, which can range from 0 to 255! We then take the product of the two and add our base to it, i2, and we then have the resulting alpha blended value!
new_i = i2 + (INTERPOLANT * alpha);
So, when it comes to alpha blending two colors together, we simply extend the above rule to include the three RGB values of each color, blend them together, and we get the resulting RGB values of the blended color. Here's our formula, where r1, g1, and b1 are the first color's RGB values, r2, g2, and b2 are the second color's RGB values, and new_r, new_g, and new_b are RGB values of the resulting alpha blended color:
Very simple, as one can see. This code would work fine and dandy if we were using floating point. But when it comes using floating point for alpha blending, it can be slow since we must convert the RGB values of each color to floating point before we can blend them and then we have to convert them back after to form the resulting color value. So instead, we're going to modify our code a bit to include a form of fixed point math.
Now we're going to flip our code around a bit so that the RGB values don't have to be signed.
As you should notice, the above equation gives you the same results as the previous equations. You might think this equation to be a bit slow, but if your compiler is worth it's dime, the multiply's and divide's by 256 should be optimized to shifts of 8. Also, when we apply this equation to the alpha blend on the fly method, everything in the equation is constant, so that the only variables are the two sets of RGB values to blend together. Therefore, in the end, it results in only being a few ADDs, SHIFTs, ANDs and OR's after the compiler is through with it. This is not bad at all. So to blend two colors together, one must do the following:
7. Applying Translucency
Well, now that we know how alpha blending works, lets use it! Lets look at the Alpha Blending on the fly method first. This method works on the basis of special casing. What we are going to do is write a special cased routine for each alpha, for a resulting number of 256 different functions. This may sound very extreme and a waste of time, but let me finish. In reality, we only have to write one routine, then we let the compiler take care of the other 255 functions. Before we get into how these functions are created, lets look at how the functions are setup and called. So we have all of our 256 functions. What we do now is put pointer to them all into a lookup table or array. The function for alpha zero would the first function in the array, the function for alpha one would be the second in the array and so on. So we're going to need a structure to hold our functions.
As you can see, our blending functions take two parameters, the two colors we want to blend together and it returns the blended color back two us. What each function must do is take the two colors, get the RGB values of each color, blend them together, and convert the resulting RGB values back into the proper color value at which point it returns it. In the example code, we aren't going to use this method, but I will provide an example Alpha Blend Map and a single putpixel routine in both C and x86 ASM for 24-bits per pixel video modes. Using this as a base, you should be able to make alpha blending maps for each color depth. You should take note that the 24-bits per pixel blending map can also be used for 32-bits per pixel modes, but the actual routines for drawing to a 32-bpp surface must be different. It all has to do with the fact that the alignment byte in a 32-bpp pixel isn't used for color, but only to align each pixel on a DWORD boundary. When compiling the blending map, in blend24.c, it will take a while to compile (maybe 20-30 secs, which really isn't that long), so don't restart your computer when you think it has hanged ;-).
Now, lets talk about the lookup table method, which will be used to blend colors in 8 bits per pixel modes. Take note that this isn't the only method to blend colors in 256 color modes, but it is probably the simplest and fastest method for performing it. Since we only have 256 colors at one time, we can generate a lookup table. This table is a bit big, but in today's world of computers, a table that's 64k in size is nothing when the user has 32MB (64MB or even 128MB) of RAM to waste. The problem with this method is that you can only calculate the colors for a single alpha at a time. Of course, you can have more than one lookup table (where each table is of a different alpha), but you're only limited to so many. Anyway, lets have a look at the table we will be putting our data into first...
Looking at the above definition, you should see that it is an array of 256 colors by 256 colors. In essence, all we have to do is calculate every possible color combination for a particular alpha. To get the blended color result once the table has been created, we just plug in the two color values we want to blend into the array, and the resulting indexed value will be the blended color we want. Another problem with this method is that once a table has been filled with data, the palette can't be changed or you will have to regenerate the CLUT (which takes a bit of time) or load it in from disk (or you could pre-load it in from disk, but remember, you've only got so much RAM to work with, unless of course, you have a few Gigs!). So, when using this method, try to use a single palette, (or at the very least, only a few palettes) or you may find your program spending most of it's initialization/loading time generating CLUT's. Back to alpha blending... What we need for a function to generate the CLUT's is something that will take the palette we want to generate the CLUT for, plus the alpha. Using those, it will find every possible combination for alpha blending the 256 colors together and it will store them in the CLUT. We're also going to use a simple callback function, which can be used as a progress indicator (this is illustrated in the test program). Here's the function listing...
Simple and elegant, just the way I like it! In the source code, we will use a global gfxClut_t pointer called 'gfxActiveClut' to point to the active CLUT. This way, we can use different CLUT's without a bunch of hassle and extra code. One last thing, in order to properly blend the colors together, one must do the following.
That should be about it. If you're a bit confused, have a good look at the source code and that should clear things up. Hope this document helps you out at your graphics programming endeavors. If you find this stuff useful, and if you use it in the production of a commercial/shareware/freeware product, I would appreciate it if you gave me some credit for my work and efforts. If you have any suggestions or modifications for this package, then contact me through one of the methods below.
8. Who Are You?
My real name is Jesse Towner. I eat, drink, and sleep coding, but sometimes I manage to make it out of the house =). I'm currently working for Digital Vortex, Inc. (IDEA Software and F.A.S.T Projects). You just might be seeing our games on the shelves by year 2000. Well, time to hit the sack and dream about new graphics algorithms... See ya, and have a good hack at this stuff!
9. How Can You be Reached?
You can reach me via the following ways (note that the following info may change fairly soon, I'll update it when it does change):