This section of the program will probably be the most difficult part for you to understand. This part of the program is concerned with spinning, i.e. clockwise and counterclockwise, and translating, i.e. forward/reverse movement, though the tunnel as well as doing the necessary prep work for applying motion blur to the tunnel image. The first topic that I want to cover is how to make the tunnel spin and how do we move through the tunnel.
First of all, you should know that nothing related to the tunnel texture map pixels or the viewport ever moves or changes at all. These things are only created once and they are completely stationary. What "moves and spins" is how we think about the tunnel and its associated origin. I consider the origin of the tunnel to be the same place as the origin of the texture map that is applied to the tunnel. This origin is located in the upper left hand corner of the texture map. See the diagram below.
Motion blur is the process of streaking an image through a frame of animation. The streaking is caused because a foreground object literally moves so much during the time the frame is exposed, that other parts of the surrounding background environment that were initially obscured by the main object, are later able to be seen as the foreground object moves out of the way. The film reacts to this by summing all the light that fell on it during the time the frame was exposed. I add motion blur to my hyperspace tunnel by averaging all the light that was recorded during the frame's exposure instead of summing it. Click here to view a website that illustrates the three main methods of creating motion blur. Also note that the description presented here encompasses both the creation of a motion blurred image as well as writing that image out to the external disk file. The writing of the image out to the external file is actually covered on another webpage but I have chosen to include everything here for completeness.
There are three main ways to accomplish motion blur inside a computer. One way, the way I did it, is called Scene Motion Blur. This method basically involves dividing a physical frame of film up into a series of time slices. During each time slice, the entire image that is currently in the viewport is stored somewhere. Then time advances to the next time slice. During the time between the time slices, the image on the viewport changes as one or more objects in the scene change their position. At the next time slice, whatever image that is currently on the viewport is again stored somewhere. This procedure is then repeated time and time again until the entire amount of time the frame is to be exposed is finished.
When all the time slices for a frame of animation are completed, you end up with stack of complete frames of animation. Each of these frames, called a sub-frame, shows the scene as it was at particular slice of time during which the larger frame of animation was being recorded. The larger frame of animation is called the super-frame because all the sub-frames must be averaged together to form the resulting super-frame. It is the super-frame that is actually viewed by the user.
To motion blur the super-frame, all we need to do is take each of the sub-frames and average them all together to form the resulting super-frame. To make things simple, lets just examine one pixel in each of the sub-frames. But the pixel we choose in each of the sub-frames must be the same corresponding pixel in all the other sub-frames. To average the corresponding pixels in each of the sub-frames together, we do the following.
For each corresponding pixel in each sub-frame, we determine its red, green, and blue component color values. This will be a number between 0-255 in each of the red, green and blue components of each corresponding pixel. We then add up each of the red component values in each of the cooresponding pixels in each sub-frame. We then divide this resulting number by the number of sub-frames making up the super-frame. This will become the red component value for this pixel in the resulting super-frame. We then do the same thing for the green and blue components. Then we do the same thing for all the other pixels in each of the sub-frames. When we get through, what we have done is averaged together all the sub-frame images and we have just applied motion blur to this super-frame of animation. Now we just display this frame of animation on the screen.
Now as the programmer, I must decide how many time slices I want to allow for each super-frame. The more time slices I allow, the smoother the resulting motion blur looks. If I don't allow many sub-frames to be generated for each super-frame, then the animation will have a kind of strobe effect to it. This won't look very real. If I opt for the maximum number of sub-frames, then I will end up with a very fluid and smooth look to the streak. The disadvantage to opting for as many sub-frames as possible is that each sub-frame represents yet another complete rendering of the scene as it looks at that time slice. This can make the total rendering time for the animation take many dozens of times more time to complete.
I am currently rendering at 24 fps and I have specified that I want 69 sub-frames per super-frame. This means that each frame of animation will require approximately four minutes per frame or somewhere around 16 hours to generate 10 seconds worth of animation at 24 fps. That's fine but how did I accomplish the actual motion blur procedure in software?
Implementing a motion blur feature in the rendering software was not particularly difficult, and was well worth the effort. As stated above, the programmer must first determine how many sub-frames per super-frame is desired. To determine what acceptable sub-frame values are, we first must determine what the sub-frame limits are. The lower limit will always be 1. A sub-frame value of 1 represents no motion blur at all. The upper limit is determined by the values specified by the rendering program's constants HT_SECS_TO_ANG_CYCLE and HT_TRANSPORT_INCHES_PER_FRAME.
The HT_SECS_TO_ANG_CYCLE constant specifies how many seconds are required to completely roll the hyperspace tunnel through 360 degrees. I currently have this constant set at 3 seconds. This results in a 360 degree roll of the tunnel in 72 frames. This also means that for each super-frame of animation, we must move the conceptual origin 5026/72 or 69 pixels horizontally between super-frames.
The other constant HT_TRANSPORT_INCHES_PER_FRAME, specifies how many inches we are to either forward or backwards translate through the tunnel for each super-frame. This constant is currently set to 0.173333333333 inches. This equates to 400 * 0.173333333333 or 69 pixels of transport through the tunnel per super-frame. That means that between frames we are moving the same distance down the tunnel as we are rotating the tunnel.
What does this mean as far as motion blur is concerned. Well, from super-frame to super-frame we are moving 69 pixels both vertically and horizontally through the tunnel. That means that if we wanted to, we could have up to 69 sub-frames worth of motion blur per super-frame. Remember that each sub-frame represents another image of the scene between super-frames. And between super-frames we are skipping 69 pixels down the tunnel. At each one of those 69 pixel locations we could position the conceptual origin but we are not. We can use each of those pixel locations to position another sub-frame and then take another snapshot of the tunnel. So the sub-frame limit is really based on how fast we want to move along the tunnel. So for my example, the sub-frame limits are between 1-69. I have chosen to use 69 sub-frames to generate my motion blur because I want the best motion blur possible.
But what happens if the number of sub-frame amounts between the tunnel rotation and translation are different? Lets say that the HT_SECS_TO_ANG_CYCLE results in 69 sub-frames and HT_TRANSPORT_INCHES_PER_FRAME results in 140 sub-frames. In this case we are translating through the tunnel at twice the rate that the tunnel is spinning. When the two sub-frame values are different, then the upper sub-frame value will always be the limit. In this case the sub-frame limits would be between 1-140.
Now remember that I said that to do motion blur, we need to render the entire scene at each sub-frame position and then store each of these sub-frame images off somewhere until we have rendered each sub-frame. Where did I store all of these images?
First, I set where the origin is for a particular sub-frame. Then I run through the VAA one row/column pixel at a time. For each VAA pixel, I run through all the texture map pixels, up to 200 of them, that projected into the current VAA row and column pixel. For each VAA texture map pixel, I ask the VAA what the texture map row and column coordinates are at that pixel. I then add to these base texture map coordinates where the current origin is located. This procedure spins and translates the tunnel to the current sub-frame location. These now adjusted texture map coords must be checked and perhaps adjusted yet again to make sure that they are within the texture map coordinate space.
Using these adjusted texture map coordinates, I ask the texture map what the color index is at this location. Once I have the color index at the final texture map coordinate location, I can take this value and then ask the CLUT for what the red, green, and blue color component values are. I then store these red, green and blue components in a set of three temporary integer variables. I then go on to the next texture map pixel in the current VAA row and column and again determine red, green and blue component values. I then add these new values into the three temporary variables used to hold the red, green and blue component values. After I go through this procedure up to 200 times for a particular VAA row and column, I take the resulting red, green and blue values and divide them by the number of cells there were in this particular VAA row and column position. I have now formed one pixel of one sub-frame image. This procedure is then repeated for all the other pixels in this sub-frame.
I then take each of the sub-frame pixel's red, green and blue component values computed above and assign them to another 2D structure array called the Motion Blur Accumulator Array, i.e. the MBAA. The dimensions of the MBAA are the same as the VAA except that its depth is only one. Each cell of the array is composed of a three component structure. Each component of the structure is an integer which holds either an accumulated red, green or blue value.
The MBAA's job is to average all the individual sub-frame images together to form the resulting motion blurred super-frame. The array does this by adding all the red, green and blue component values for corresponding pixels in each of the sub-frames together. Then at the end, the component values are then all divided by the number of sub-frames. This averages all the sub-frames together and creates one motion blurred super-frame image. Repeat this procedure for however many frames of animation there are. The below pseudo code describes the above procedure.
// Start function -------------------------------------------------------------- Create_motion_blur_image() { for (each sub-frame image in this super-frame) { for (each row in the hyperspace tunnel image) for (each column in the hyperspace tunnel image) { Clear out the color_accumulator's red, green and blue accumulator values. for (the depth of the VAA[row][column]) { Determine the VAA[row][col][depth].row/column values. Adjust these VAA values to where the current sub-frame is located in the tunnel. Adjust these VAA values again if they are outside the texture map coord space. Using the texture coords, ask the texture map what the color index is at this location. Use this VAA texture map color index to look up the actual RGB color in the CLUT. Add this new color to the color_accumulator's RGB accumulator values. } Divide the color_accumulator's RGB accumulator values by the depth of the VAA for this pixel. We now have the color of one pixel in one sub-frame image. Add this sub-frame image pixel into the same pixel location within the MBAA. } We have now formed one complete sub-frame image and added it to the MBAA. Move the conceptual origin to the next sub-frame location within the tunnel texture map. Make sure this new origin location is within the tunnel texture map coord space. } //The following code can be used at this point to create a resulting frame of animation. Create a new external RAW format animation frame file to store this motion blurred image. for (each row in the hyperspace tunnel image) { for (each column in the hyperspace tunnel image) { Extract the MBAA's RGB color values at this row and column. Divide the RGB values by the number of sub-frames there are for each super-frame. Put this resulting RGB pixel value into an output buffer that has the width of the resulting image. } Write the image buffer out to the external file. } Close this RAW file. } // End function --------------------------------------------------------------