In this tutorial we will learn how to make the frames for a modified version of the above movie (frames were then combined into a movie using Quicktime). Make sure you are set up correctly first and you have read the first AstroBlend tutorial which discusses the general setup of python scripts in Blender and the second AstroBlend tutorial which shows how to use AstroBlend to load data and render images.
Get the Data
We are starting from the blend file "usual.blend" which has an Image editor, python console and 3D viewer in its layout. This can be found on the file download page.
Before proceeding further, you need to download the data we'll be using for this example here. As of this tutorial, all SPH data needs to be formatted in a text file in five columns which are:
(1) the particle identifier number (or a place holder number if there is none), (2)-(4) the x/y/z coordinates of the particle in whatever units you'd like, (5) a number specifying the "type" of particle (more on this in a bit).This huge text file is then read into Blender in serial. This is terribly inefficient, and the above movie took about 1.5 days on 8 cores to render. Ick. Therefore, in this tutorial we'll be using a subset of the data consisting of 1/100th of the particles (so, about 10,000 particles). Updates to AstroBlend will read in parallel, but we're just not there yet.
Also, its worth noting that some SPH codes are supported with direct reading from files via yt. More on that in a subsequent tutorial.
Render A Single Image
Before going about the business of making a whole movie, perhaps it would be best to start with reading in a single data file and figuring out which set of particles we want to render, where to place the camera, etc.
First, lets do the usual setting up of the script as discussed in the first tutorial and the camera and the rendering as discussed in the second tutorial which can be done by copying and pasting the following code into Blender's python console:
import science # import AstroBlend library # set up camera cam = science.Camera() cam.location = (2.0,0,0) cam.pointing = (0,0,0) # initialize render render_directory = '/Users/jillnaiman/blenderRenders/' render_name = 'mysphrender_' render = science.Render(render_directory, render_name) # initialize lighting light = science.Lighting('EMISSION') # light by surface emission
From this typical setting up of stuff we will see the following camera tracked to an empty:
Next, lets choose a file to read in:
ddir = '/Users/jillnaiman/data/sphdec/' # directory where text files are stored dfile = ddir + 'outputs_dec_005.txt' # create filename from directory + name of a single file
In this particular simulation, the following particle types are used:
0 = Gas Particles 1 = Dark Matter Halo Particles 2 = Origional Disk Stars 3 = Origional Buldge Stars 4 = Newly Formed Stars 5 = Central Supermassive Black Holes (only 2 of these particles)
So, lets choose what colors to make the different particle types by specifying their RGB triplets:
colors = [(1,1,0), (0,0,1), (1,0,0), (1,0,0), (1,1,1), (0,1,0)]
This set of simulation files has distances in units of kpc, which will put things at large distances in the Blender volume. Since the camera in Blender will only resolve a specific distance scale (more about camera clipping here), we want to rescale the imported distance coordinates:
scale = (0.1, 0.1, 0.1) # rescale all x/y/z coords by this
The last thing to do before importing the data and rendering is to figure out the size "halo" we want to give each particle. Loading many particles into Blender as individual spheres would be too memory intensive for large scale simulations, so we will be applying a modified form of Dr. Kent's particle loader, optimized for easy importing and deleting. In this scheme, each particle of a certain type is a vertex in a larger "type object", and each vertex is given a little glowing halo to make it look like a particle during the render. We need to choose the best size for our little halos by more or less trial and error - too large and all of the particles will smear together, too small and they won't be rendered. For this simulation, I've found the following to work for the six different particle types:
halo_sizes = (0.0008,0.0008,0.0008,0.0008,0.0008,0.008)
We can now read in the particle data with the simple command:
myobject = science.Load(dfile, scale = scale, halo_sizes = halo_sizes, particle_num=6, particle_colors=colors)
Now you should see all the data loaded into Blender like so:
where the particle types are labeled "output_dec_005particle##" in the Object Selector panel on the right (by default the last particle type imported is selected, in this case "particle05").
So, this looks a bit more like a swarm of particles and less like two galaxies merging, no? That is because the particles we are seeing are the dark matter halo particles, and not the particles making up the galaxies embedded within. To see the galaxy particles, we need to "hide" the dark matter particles with the following lines:
# hide the 1st particle part_hide = (False, True, False, False, False, False) myobject.particle_hide = part_hide
If we hit the render button we see the following in the UV editor window:
which is totally not a galaxy. What went wrong? Well, we can clearly see the central
black holes from our simulation (green dots), but you'll recall we made the halos for
these particular sorts of particles an order of magnitude larger than the rest of the
particles. This is where the camera clipping stuff comes into play - the camera is
simply not able to render the smaller haloed particles. To fix this we can play with
the camera clipping (
cam.clip_begin) the halo sizes (
or simply move the camera in, which is the route we will take here:
cam.location = (1.0, 0.0, 0.0)
If we like this view and want to save this render to file, all we need to do is the usual render command:
Again, one must be careful when deleting objects particle set as Blender will leak memory like crazy if you aren't careful. To delete your particle set do:
science.delete_object(myobject) # delete all particles in set
Before moving onto the movie making portion of this tutorial, I'll just note here that the full script which loads and renders a single image is the "rendersphframe.py" script on the List of Scripts page.
Make A Movie
Now that we know how to render a frame, rendering a whole movie is trivial. All we need to change in our script is to create a list of files to load in instead of a single text file, and we need to call the SPH movie renderer which will iteratively call import, render, and delete particle data. This is encapsulated in the following script (called "sphmovie.py" on the List of Scripts page):
import science # import AstroBlend library # set up camera cam = science.Camera() cam.location = (1.0,0,0) cam.pointing = (0,0,0) # initialize render render_directory = '/Users/jillnaiman/blenderRenders/' render_name = 'mysphrender_' render = science.Render(render_directory, render_name) # initialize lighting light = science.Lighting('EMISSION') # light by surface emission # generate the list of files to iterate over ddir = '/Users/jillnaiman/data/sphdec/' # where data is stored df = 'outputs_dec_' # base name of data files dfiles =  nf = 200 ns = 1 # create list of files for i in range(ns,nf): num = "%03d" % (i) # create strings from numbers, with 3 digits dfiles.append(ddir + df + num + '.txt') # various color and scale parameters colors = [(1,1,0), (0,0,1), (1,0,0), (1,0,0), (1,1,1), (0,1,0)] scale = (0.1, 0.1, 0.1) # rescale all x/y/z coords by this halo_sizes = (0.0008,0.0008,0.0008,0.0008,0.0008,0.008) # hide the 1st particle part_hide = (False, True, False, False, False, False) for i in range(0,nf-1): myobject = science.Load(dfiles[i], scale = scale, halo_sizes = halo_sizes, particle_num=6, particle_colors=colors) myobject.particle_hide = part_hide render.render() science.delete_object(myobject)
This little low resolution movie took about 20 minutes for all the frames to render, again emphasising the need for me to implement parallel particle data importing. Stay tuned for fuller SPH support in the future!