Implementation notes Programming metal tubs for non-fiction. ---------------------------------------------------------------------- 0. Concept Finished watching "Yokuwakaru Gendai Mahou" series... what got me hooked was the very first line in the preview episode earlier this summer, where Kaho said "this is the story of a girl who changes all magic to tubs". Tubs?! I thought I was mistaken, but the series proceeded to deliver all the tubs that was promised, along with lots of cute girls, and absolute abuse of software engineering jargons. So naturally I have to watch it, especially when programming for non-fiction is my job ;) It was a good series, definitely worth watching. Although what was most memorable about this series in the end, besides Nonaka Ai's voice, was all these metal tubs. I just had to make those tubs. ---------------------------------------------------------------------- 1. Tubs Making the tubs was quite trivial -- draw the contour of a typical looking tub, and make a surface of revolution out of that, making sure that all normals are pointing in the right direction. The tubs produced here look rather bland, as tubs generally are. I don't think any fancy lighting or camera work is going to make them look less bland, so I decided to add some texture. Most of time from start to finish was actually spent on thinking about how to texture these tubs. ---------------------------------------------------------------------- 2. Metal tubs I eventually settled on using the environment mapping features of OpenGL to texture these tubs, which generates nice looking reflections without too much work. The only bit of work involved was creating this sphere reflection map. The recommended way to generate these maps would be to take pictures of shiny metal balls. This might have been a good idea, but then I would have to embed a potentially large bitmap in the code, and I still have the problem of finding what these metal balls should reflect. So I decided to generate the reflection map at run time. I had some plans on drawing some random colored squares onto a plane, project that onto a sphere. The problem is that rays that bounces off the edge of the sphere tend to go toward infinity, and I didn't have an infinitely large plane. That, and the results looked awful. This experiment is implemented in generate_env_map1.c Then I thought: I can make two infinitely large planes by reflecting the ground and the sky. Basically I had a 1D texture that is a gradient from green to white to blue, and I would pick the texture color based on where the reflected ray hit this gradient. If it's too far off the edges, it will be either solid blue (sky) or green (ground). The result doesn't look too shabby. This experiment is implemented in generate_env_map2.c Had I stopped here, the tubs would look reflective, but still rather dull. You can see how that would have looked by change line 46, columns 104-106 in koyomi.c from "h<1" to "0". More cleverness is needed to add some horizontal variety to the reflected map. ---------------------------------------------------------------------- 3. Shiny metal tubs The sky and ground looked okay if you just looked up and down, but if you looked sideways, there isn't much variation. I thought I can make it a bit more interesting by adding some clouds. Many people have done fractal clouds before (divide, average, perturb, recurse), and that is the approach I have taken. The results looked quite decent. Experiments here are implemented in cloud*.c Remember the earlier bit about not having an infinitely large plane? This time around, I decided that I would generate the cloud texture such that they would tile, and there would be no hard edges when I modulate in the tiled texture. As the tiles repeat infinitely, I get my infinitely large plane to reflect off of. On tiling the clouds: simply mirroring the cloud two ways would do it, but the clouds would look like Rorschach's inkblot test. It might have been interesting on other occasions (I think some people around me needs psychiatric evaluation, seriously), but quite useless for my metal tubs. This experiment is implemented in tile_cloud1.c. A bit more cleverness is needed to generate more decent looking tiles. The final approach taken was this: - Run one pass to generate the clouds as usual. - Replicate the edge pixels. - Run a second pass to regenerate new clouds, this time keeping the edge pixels unchanged. Because the fractal cloud algorithm works by averaging neighbor pixels, pixels on the locked edges will influence all pixels in the middle. The end result tiles perfectly, with only minor artifact on the corners. The final cloud is implemented in tile_cloud2.c, and the final texture map with clouds modulated in is implemented in generate_env_map3.c. And it does look shiny. I was going to add the clouds just to the sky parts of the environment map, but since it looked just fine when it's added to the ground, I thought I would just keep it. ---------------------------------------------------------------------- 4. Bouncing shiny metal tubs After all that, we finally have a environment map that can be applied to make shiny metal tubs. It would be a waste to make still images of these shiny metal tubs, so I made them bounce around the screen. You might be thinking that falling metal tubs would have been more appropriate, but since I have already written a falling snowflake program before, I thought I didn't need another falling thing, so the tubs bounce instead. One thing worth noting is the up vector of the camera, which is actually pointing downwards (but it's all relative, of course). This minor detail actually makes a difference: when looking at the inside of a tub, the bottom side tends to be blue, because it reflects the sky above. The opposite happens when you look at the outside of the tub. Thus it's possible to tell if you are looking at the inside or outside of a tub because the camera's vector matches the texture's coordinate system. In practice, it's often difficult to tell if you are looking at the inside or outside of a tub, especially if your eyes switch from tub to tub. The effect is similar to M.C. Escher's lithograph "Kubus met banden" (also known as "Cube with Magic Ribbons"). Since I like Escher's work very much, I made no effort to fix it. It's a feature ^_^; Another feature is that the animation is is done with keyframes, so the shiny metal tubs bounce at the same rate regardless of how fast your video card renders things. Koyomi will output a frames per second number when she exits, so you can brag to everyone about how you can render high-resolution shiny metal tubs at 1000fps ;) ---------------------------------------------------------------------- 5. Known issues The tubs often intersect each other as they bounce around the screen. This is because I never bothered to implement collision avoidance. You can live with it if you just imagine that these tubs are ghosts with no physical object. Because of all the time spent thinking about environment maps, these days whenever I see a shiny object, I tend to see what the object is reflecting, rather than the object itself. Oh, I guess that's just an issue with me and not my program. ---------------------------------------------------------------------- 6. Finally... This time around, I decided to do some halftones with Koyomi's hair. Have you ever heard people doing halftones in ASCII art? By hand?! I tell you, it's a lot of work. Making the reflection map took the most time just thinking about it, but doing the halftones took the most effort. But wait, there's more! I had quite a bit of space left, so I embedded a BF program in koyomi.c. Two programs in one package, what a deal. Other than that, I think the end source code looks very nice, and the shiny metal tubs looks pretty good, so I am quite happy ^_^; Metal tubs made in wonder. -- omoikane@uguu.org - http://uguu.org/