Saturday, April 6, 2019

Rainbows

Last week was rainy. On my way home in the evening I saw a bright double rainbow in the sky. It made me wonder how the physics of a rainbow works, so I looked it up online the next day. Rainbows are visual artifacts formed by the refraction of light through water droplets in the air back to the viewer. They span a viewing angle from 40 degrees (violet) to 42 degrees (red) around the viewer's field of view in the direction opposite the sun. There's sometimes also a second rainbow from 50 to 53 degrees, called a double rainbow. Rainbows aren't in any real physical location, they're more like projections onto the cloud/mist in the distance.

I'm not aware of any other games that display realistic rainbows, so it seemed like something interesting and unique to add to 3DWorld. I implemented a rainbow as a quad projected opposite the sun, where the colors are generated in a fragment shader as a function of radius using a color spectrum that I matched to images. This is both fast and close to physically accurate. 3DWorld only draws rainbows in the cloudy period after the rain has stopped, during the morning and evening times when the sun is low in the sky. The brightness of the rainbow is determined by integrating along the view ray using the depth buffer. The places where the rainbow falls over distant objects appear brighter than locations where there are nearby objects. Here is a screenshot:

Physically based rainbow drawn in a fragment shader. Those small black specks in the sky are birds flying in flocks.

That looks pretty good to me. It appears to blend correctly against the trees and mountains, and reflects in the water. If you look closely you can see that the center area inside the rainbow's color bands is slightly brighter. I suppose this rainbow looks a bit too perfect though, maybe because it has a nearly constant brightness against the clouds. I could probably add some random intensity variation to improve the realism. Also, I'm only drawing a single rainbow, not a double rainbow. I think the single rainbow is enough for now.

3 comments:

  1. A quick method for varying the intensity would be to multiply it by the cloud pass. That way you could also get the full halo rainbow that you sometimes see looking down into clouds from an airplane.

    ReplyDelete
    Replies
    1. Clouds aren't drawn in their own pass/buffer. Clouds are drawn as 3D geometry (a combination of billboard clouds and flat textured quads) without depth testing or writing. Normally they're drawn before everything else so that they're in the background, and rendered in approximately back to front order. But when the player gets to the altitude at which the clouds start, I swap the order and draw them last, after the terrain, so that they alpha blend correctly. Rainbows are drawn last so that they alpha blend against everything else. So it's not clear how I can access the cloud data in the rainbow shader unless I change the draw order and rendering pipeline.

      I haven't tried looking down at a rainbow from above the clouds. I'm not sure if that works or not. I'll have to experiment with it later and fix it if it doesn't work.

      Delete
    2. No, rainbows don't really work when viewed from above the clouds. The various layers are drawn in the wrong order for that, and the depth buffer doesn't have the data the rainbow shader expects. I have to change the code to even make that testable because the rainbow is only triggered after rain, and the rain isn't enabled/drawn when the player is above the clouds.

      Delete