Approaching Planets

Planetary distances are difficult to accurately render in Blender because the distances are so great. One way to get around this is to scale the planet using its apparent size.

The following code provides an example of how to:

  • Scale a planet using its angular diameter.
  • Render an animation using Python code.
  • Change text during an animation.

The number of frames between the planets is calculated based on the speed of a fast solar sail (80 km/s) and then reduced by a constant factor to make the journeys between the gas giants take minutes, rather than months.

It is presumed that the sphere is located exactly 2 Blender Units from the camera and has been scaled in Edit mode by 0.5 Blender Units. The result is that the sphere should exactly fill from the top to the bottom of the camera’s view port.

The result:

http://youtu.be/wM0JMaM_tdQ

See also:

######################################################
#
# Importing modules
#
######################################################

import bpy
import datetime
import math

######################################################
#
# Constants
#
######################################################
RADIUS_SUN     = 695630.00
RADIUS_MERCURY = 2439.70
RADIUS_VENUS   = 6051.80
RADIUS_EARTH   = 6378.10
RADIUS_MARS    = 3396.20
RADIUS_JUPITER = 71492.00
RADIUS_SATURN  = 60268.00
RADIUS_URANUS  = 25559.00
RADIUS_NEPTUNE = 24764.00

DISTANCE_MERCURY = 44607842
DISTANCE_VENUS   = 60388451
DISTANCE_EARTH   = 41123114
DISTANCE_MARS    = 101374519
DISTANCE_JUPITER = 526211061
DISTANCE_SATURN  = 648678232
DISTANCE_URANUS  = 1512317091
DISTANCE_NEPTUNE = 1594769151

# Scale Z = (scale - (scale * oblate))
OBLATE_MERCURY   = 0.0000
OBLATE_VENUS     = 0.0000
OBLATE_MARS      = 0.0065
OBLATE_EARTH     = 0.0034
OBLATE_JUPITER   = 0.0649
OBLATE_SATURN    = 0.0980
OBLATE_URANUS    = 0.0229
OBLATE_NEPTUNE   = 0.0171

TRAVEL_FRAMES_MERCURY = 155
TRAVEL_FRAMES_VENUS   = 262
TRAVEL_FRAMES_EARTH   = 178
TRAVEL_FRAMES_MARS    = 440
TRAVEL_FRAMES_JUPITER = 2284
TRAVEL_FRAMES_SATURN  = 2815
TRAVEL_FRAMES_URANUS  = 6564
TRAVEL_FRAMES_NEPTUNE = 6922

TRAVEL_DURATION_DAYS_MERCURY = 5.163
TRAVEL_DURATION_DAYS_VENUS   = 8.737
TRAVEL_DURATION_DAYS_EARTH   = 5.950
TRAVEL_DURATION_DAYS_MARS    = 14.666
TRAVEL_DURATION_DAYS_JUPITER = 76.130
TRAVEL_DURATION_DAYS_SATURN  = 93.848
TRAVEL_DURATION_DAYS_URANUS  = 218.796
TRAVEL_DURATION_DAYS_NEPTUNE = 230.725

PLANET_NAME      = "Saturn"
PLANET_RADIUS    = RADIUS_SATURN
PLANET_DISTANCE  = DISTANCE_SATURN
PLANET_OBLATE    = OBLATE_SATURN
CAMERA_DISTANCE  = PLANET_RADIUS * 2

TRAVEL_FRAMES        = TRAVEL_FRAMES_SATURN
TRAVEL_DURATION_DAYS = TRAVEL_DURATION_DAYS_SATURN

RENDER = False
WARP = True

# TRAVEL to Saturn (1 - 2815)
BEGIN_DATETIME = datetime.datetime( 2153, 1, 24, 1, 17, 39, 767000 )

# Calculation used to set the seconds when at a planet, not travelling.
#
TRAVEL_DURATION_SECS = TRAVEL_DURATION_DAYS * 24 * 60 * 60
SECONDS_PER_FRAME    = round( TRAVEL_DURATION_SECS / TRAVEL_FRAMES )

# Calculation used when travelling between planets.
#
DISTANCE_PER_FRAME   = PLANET_DISTANCE / TRAVEL_FRAMES

# Increases by SECONDS_PER_FRAME every frame.
#
current_datetime = BEGIN_DATETIME

current_frame = bpy.context.scene.frame_start

# Calculates the apparent distances and diameters to obtain a scaling
# factor for the sphere.
#
def scale_factor( radius, distance ):
    rs = math.pow( radius, 2 )
    blender_distance = radius * 4
    
    simulated_apparent_diameter = 2 * radius * math.sqrt( 1 - rs / math.pow( distance, 2 ) )
    simulated_apparent_distance = distance - rs / distance
    
    blender_apparent_diameter = 2 * radius * math.sqrt( 1 - rs / math.pow( blender_distance, 2 ) )
    blender_apparent_distance = blender_distance - rs / blender_distance
    
    simulated_ratio = simulated_apparent_diameter / simulated_apparent_distance
    blender_ratio = blender_apparent_diameter / blender_apparent_distance
    
    return simulated_ratio / blender_ratio

def set_text( text_object, text_value ):
    bpy.ops.object.select_name( name=text_object, extend=False )
    
    bpy.ops.object.editmode_toggle()
    bpy.ops.font.delete()
    bpy.ops.font.text_insert( text=text_value )
    bpy.ops.object.editmode_toggle()

def update_text():
    d = current_datetime.strftime( '%Y-%m-%d' )
    t = current_datetime.strftime( '%H:%M:%S' )
    
    set_text( "Date", d )
    set_text( "Time", t )

def render_frame():
    filename = "renders/sun/" + ("%04d" % current_frame) + ".png"

    if RENDER:
        bpy.ops.render.render()
        bpy.data.images["Render Result"].save_render( filepath=filename )

    #print( "Saved: ", filename )

def scale_planet( planet_object, scale ):
    bpy.ops.object.select_name( name=planet_object, extend=False )
    ob = bpy.data.objects[ planet_object ]
    ob.scale = (scale, scale, scale - (scale * PLANET_OBLATE))

def render_animation():
    global current_frame
    global current_datetime
    
    distance = PLANET_DISTANCE
    scale = 0

    # No planet in the initial frame for travelling.
    #
    if WARP: scale_planet( PLANET_NAME, scale )
    
    while current_frame <= bpy.context.scene.frame_end:
        bpy.ops.anim.change_frame( frame=current_frame )
        print( current_frame, ' ', current_datetime )
        update_text()
        render_frame()
        current_frame = current_frame + 1
        
        distance -= DISTANCE_PER_FRAME
        scale = scale_factor( PLANET_RADIUS, distance + CAMERA_DISTANCE * 2 )

        print( "Distance: " , distance, " Scale: ", scale )
        
        # Travel to the planet by scaling it up using its apparent size.
        #
        if WARP:
            if distance > 0: scale_planet( PLANET_NAME, scale )
        
            current_datetime = current_datetime + datetime.timedelta( seconds=SECONDS_PER_FRAME, microseconds=496000 )
        else:
            current_datetime = current_datetime + datetime.timedelta( milliseconds=round( 1000/30 ) )

update_text()

render_animation()

# render_frame()

The script fails on line #172 update_text() the context is incorrect.

Using Blender 2.5.8 r38533.

Thanks for the note, Atom.

The script is a partial example of calculating apparent sizes.

You would have to include two text objects (“Date” and “Time”) and a sphere (“Saturn”). Or remove the call to set_text entirely. (The script, as written, updates the date and time to show that something is happening while travelling from planet to planet. Otherwise the journey would be rather boring: merely seeing stars.)

If there is interest in a fully working blend file, I will post one.

A few people have asked about the script. Here is a fully working .blend file, with complete instructions and documented source code:

http://www.pasteall.org/blend/9105

Thanks for the BLEND file, the script seems to be working but quite frankly it is the slowest calculation I have seen in a while.

Is it really worth the wait? I had to give up because I ran out of time. So I still so not know what the script actually does.

Couldn’t you just scale a sphere with a couple of key frames and be done with it?

how long does it takes ?

i mean ir educe the number of rame down to 3 and waited a minute and nothing

could not cancel it with ctrl-C or esc key !

thanks

I was able to cancel it on Windows by toggling the console to appear before running script. Then I clicked on the console and pressed CTRL-C.

ok but if you set the frame qty= 3

how long this should take is it 1 hour or a lot less?

would like to test this and see it work nornally!

thanks

If you run Blender from a command prompt (Start >> Run >> cmd >> Enter), you should see something like:

Frame:  1 Distance:  526211061  Scale:  0
Frame:  2 Distance:  525980670.86821365  Scale:  0.0005261350568172955
Frame:  3 Distance:  525750280.7364273  Scale:  0.0005263654902286949

This will create 3 black PNG images. (The scale needs to hit 0.001… before any pixels of the sphere will be drawn.)

The script doesn’t alert you to when it is finished. No sound, no pop-up, nada.

You could get an approximation using key frames, Atom. But my questions would remain unanswered: How long would it take, relatively speaking, to travel between the planets at a high velocity? What would it actually look like if you were approaching a planet at that same speed?

Jupiter: http://www.youtube.com/watch?v=wM0JMaM_tdQ#t=4m32s
Saturn: http://www.youtube.com/watch?v=wM0JMaM_tdQ#t=6m40s

Calculating apparent sizes and apparent distances allows these questions to be answered accurately.

Notice how long the planets remain point-sized as they approach geometrically. I don’t think a couple of key frames could correctly simulate this behaviour. I could be wrong, though: I have not looked at the math behind how IPO curves are smoothed.

(Note: The video depicts the voyage at what appears to be faster-than-light speeds. The calculations are based on an object flying at 80 km/s, then the visualizations are sped up 100-fold so that the viewer need not watch 30 years of video, with most of it being the same view of empty space.)

Either let the render go to 2284 frames, or you will have to do more than simply change the number of frames in Blender’s UI.

For example:

RADIUS_JUPITER = 71492.00
DISTANCE_JUPITER = 526211061
TRAVEL_FRAMES_JUPITER = 2284

# Name of the Object in the scene.
#
PLANET_NAME     = "Jupiter"
PLANET_RADIUS   = RADIUS_JUPITER
PLANET_DISTANCE = DISTANCE_JUPITER

These constants define the real-world size of the object (in this case, the planet Jupiter), the real-world distance the camera is from the object (in this case, the closest distance Mars ever approaches Jupiter), and the number of frames to take to get to the object (in this case, the number reflects the velocity of the camera as it travels from Mars to Jupiter: 80 km/s sped up 100-fold).

So if you wanted to create a shorter render, but still wanted to keep the same realistic approach effect, you would have to think:

  • What speed will the camera travel?
  • How far must the camera travel?
  • How large is the destination object?

The smaller the destination object, the closer the camera can be set and the slower the camera can move to get the same apparent effect.

I am sorry if I misled you into thinking you could simply change the number of frames to see anything different. You cannot. The real world is a bit more complicated than that. :wink:

ok it’s just that if you wait to get 2800 frame done it’s gone take hours before you see the end of it !
but you forgot to mention this little lag in time!

now i understand your desire to show a realistic fly by with camera which is a nice effect
but i was not really looking for this effect more how to get the render done with different frames
it’s a good example as such

so to get it running faster wihtout the realistic effect you can go to the render panel and change the final frame number to something lower like 10 or 24 frames which will give the number of images generated and be done inside a few secondes only!

and you can find the rendered images in the Sub folder called “render” under the installed SVN folder on windows

thanks for sharing

have fun with planets

Depends on many factors. Using a hyper-threaded quad-core, 2284 frames takes about an hour. Rendering ~2300 frames, by default, will probably take a while on most computers. :wink:

In that case, you really only need the render_frame() and render_animation() functions. And even then, most of their guts should be removed. Everything else is quite specific to this particular problem.

Correct. You would need to change the Blender UI to 24 frames and change “TRAVEL_FRAMES_JUPITER = 2284” to “TRAVEL_FRAMES_JUPITER = 24”. I have no idea how that will turn out, though.

That also depends on how you run Blender and on which operating system you are using.

I would recommend changing the following line:

filename = "render/" + ("%04d" % current_frame) + ".png"

Change “render/” to something else (e.g., “/home/user/blender/renders/approach/”). Just be sure to keep the trailing slash to separate the last directory name from the file name.

the only thing is there a way for the script to set up the render last frame function of the # of frame in script!

pretty certain there is a python var for this that can be set

First frame is
Scene.frame_start

final frame is
Scene.frame_end

steps

Scene.frame_step

i’v seen one or 2 threads trying to do this with planets this year alone in 2.5

but no easy to get the proper scaling in our solar system planets are so small !

nice work anyway

are you planning to write a small tut on this
it would be interesting

thanks

bpy.context.scene.frame_start and bpy.context.scene.frame_end, I think.

I did not use them for determining the end point of my animation (for various reasons). However, it is possible to adapt the given blender file by changing the following line:

RADIUS_JUPITER = bpy.context.scene.frame_end

That’s a sub-optimal trick. What you really want to do is replace RADIUS_JUPITER with bpy.context.scene.frame_end, everywhere.

It’s tricky.

This thread is all the tutorial I plan to write.

if you only want to see a few frames of the approaching jupiter
change following line in the code from


#        distance -= DISTANCE_PER_FRAME
        distance = PLANET_DISTANCE - current_frame * DISTANCE_PER_FRAME

uncomment the distance-calculation line and add the calculation using the multiplication.
Then change the frame-range to be rendered
from 0 to 2280
to starting with
2260 to 2270
and run the script.
It will now render only 10 frames with the first frame as black, but the next frames will have the calculated view of the planet.
Its possible to change the “while-loop” to do the calculation for the first frame too … but this is an easy task for python-starters … :wink: