Planet Bigshot
While playing around with Stellarium[a], I noticed that if you looked straight down, the ground would roll up into a small "planet" in the center of the screen. When I made the Granholmstoppen Stellarium Landscapes, I decided I wanted to create preview images that looked like little planets. So, I got on with it, and added a bigshot.MakePlanet
class to Bigshot.
1. Theory
The "planet" projection is quite simple. You load an equirectangular map, and for each point in the output square, you determine its polar coordinates, theta (the angle) and rho (the distance from the center). Then we set phi = f(rho) and use the resulting phi to look up a pixel in the equirectangular map. The trick to getting a good output image, as I noticed, is to pick a good function to map distance from the center of the image to the angle phi .
2. The Mapping
If the mapping from rho to phi is linear, then the output image is dominated by the view straight down, and everything interesting on the horizon would be squashed into a thin ring outlining the planet. I wanted some way to stretch the image around the horizon, and so I settled on a piecewise curve. First, I normalized the distance from the center of the image to the nearest edge (half the width of the image) to the interval [0, 1] . This placed the corners at sqrt(2) . Second, we'll also normalize the range of phi to [0, 1] , for simplicity's sake.
Then I could select a point at which the "stretch" would be at its maximum, and the amount of stretch. Let's call this point P , with P_(rho) being the point where the stretch is the maximum, and P_(phi) is the output of the function at that point. This gives us a piecewise function with two intervals, let's call them phi_(g) for ground and phi_(s) for sky. We also decide where we want to place the zenith - should the planet be a disc that just about touches the edges of the square, or should it cover the image? If the former, we set rho_(max) = 1 , if the latter rho_(max) = sqrt(2) ~~ 1.42 .
phi_(p)(rho) = {( phi_(g)(rho), if rho < P_(rho)), ( phi_(s)(rho), text(otherwise)):}
Since the function must be continuous and map to [0, 1] , we get the constraints:
phi_(g)(0) = 0
phi_(g)(P_(rho)) = phi_(s)(P_(rho)) = P_(phi)
phi_(s)(rho_(max)) = 1.0
Then, choosing an exponent for each - c_(g) and c_(s) , respectively - the two functions become:
rho_(g) = rho / P_(rho)
phi_(g)(rho_(g)) = rho_(g)^(c_(g))P_(phi)
rho_(s) = (rho - P_(rho)) / (rho_(max) - P_(rho))
phi_(s)(rho_(s)) = P_(phi) + rho_(s)^(c_(s))(1.0 - P_(phi))
Stopping here and using phi_(p) worked out OK, but I still had the problem that I got an ugly "pinch" effect in the center of the image, as the phi values would climb so fast there. So I blended the output with a linear mapping from zero to L_(max) :
w(rho) = rho / L_(max)
phi(rho) = w(rho)phi_(p)(rho) + (1 - w(rho))rho
Which is a simple linear blend on the interval [0, L_(max)] . The result looks like this:
We can see that the output climbs sharply only to level off around 0.5, which is where we find the horizon, and the continues to climb all the way to rho = rho_(max) = 1.42 (the corners) where phi = 1 and we look straight up.
To see the amount of "stretch", it's helpful to plot the inverse of the derivative of φ with respect to ρ:
High values means that we sweep through the angles slowly (much stretch), while a low derivative means that we move quickly (little stretch). As we can see, the stretch reaches its minimum at 0.5, which is precisely at the horizon and my choice for P_(rho) . The other parameters are P_(phi) = 0.5 , L_(max) = 0.5 , c_(g) = 1/3 , c_(s) = 1.2 and rho_(max) = 1.42 .
3. Example Images
Links
http://www.stellarium.org/ |