In this tutorial we will learn how to make the above image. Make sure you are set up correctly first and you have read the first Blender tutorial. If you want to start from the raw "Isolated Galaxy" data you should download it and have yt installed.

If you want your object to glow, make sure you have added the two lines to your Blender's obj importer as described in the "Getting Started" section.

Get the Surface Data

We are starting from the blend file "usual.blend" which has a text editor, 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 format the 3D simulation data for input into Blender. There are three options for doing this.

Option 1: If you are familiar with yt you can create your own OBJ files by downloading the ENZO data used to create this images here. Be warned it is ~300 MB, so you may have time to make some toast or something before proceeding. Once it is downloaded and you've activated yt in the terminal window, you can follow the blog post here to make your OBJ file, which amounts to just running the following yt-python script (for version yt-3.x):

import yt
ds = yt.load("/Users/jillnaiman/yt_files_local/galaxy0030/galaxy0030") # change to your directory
rho = [2e-27, 1e-27]
trans = [1.0, 0.5]
filename = '/Users/jillnaiman/yt_files_local/surfaces'

def _Emissivity(field, data):
    return (data['Density']*data['Density']*np.sqrt(data['Temperature']))
ds.add_field("Emissivity", function=_Emissivity, units=r"\rm{g K}/\rm{cm}^{6}")

sphere = ds.sphere("max", (1.0, "mpc"))
for i,r in enumerate(rho):
    surf = ds.surface(sphere, 'Density', r)
    surf.export_obj(filename, transparency = trans[i],
                    color_field='Temperature', emit_field = 'Emissivity',
                    plot_index = i)
This will create "surfaces.obj" and "surfaces.mtl" which are the files you will need to upload into Blender.

Option 2: Download the OBJ & MTL files here. If you use this option, you'll have to change the top few lines of the OBJ file to point to the correct MTL file (the line after "mtllib"). For multiple files you can use "utils/" to automate the process.

Option 3: Use yt directly in Blender to generate a surface. More on this option in the next section.

Import Surface Data Into Blender

In the following sections, I'll show code that you can copy and paste directly into the python console or put in the text editor. In the last section, I'll show how you can write this whole thing in an external file and run the script when you're happy with your program.

Import OBJ Files

First, lets read in the OBJ file and rescale it to a size that is easy to manipulate.

import science

filename = '/Users/jillnaiman/yt_files_local/surfaces.obj' # where obj is stored
myobject = science.Load(filename, scale = (50.0,50.0,50.0)) # load and scale x/y/z by 50 times

Import Simulation Data Directly with yt

Alternatively to this two step process of generating OBJ files and then importing in Blender, we can use the raw simulation data and use AstroBlend in conjunction with yt to generate the surface in Blender. Make sure you have yt installed following this tutorial. You'll also need the large simulation data file found here, which in the following example I store in a file named "data" in my home directory.

import science
import numpy as np

# different data - Enzo
filename = '~/data/IsolatedGalaxy/galaxy0030/galaxy0030'
sphere_rad = 200.0 # in kpc

rho = [2e-27, 1e-27]
transparencies = [1.0, 0.5]

# how is emissivity calculated?
def _Emissivity(field, data):
    return (data['gas','density']*data['density']*np.sqrt(data['gas','temperature']))

# isosurfaces at 2 and 1 x 10^-27 g/cc
myobject = science.Load(filename, scale = (50.0, 50.0, 50.0), isosurface_value = rho, 
                        surf_type='sphere', radius = sphere_rad, 
                        radius_units = "kpc", surface_field="density", 
                        meshname = 'Allen', transparency = transparencies, 
                        color_field='temperature', emit_field=_Emissivity) 

Note that in this case, you'll get two surfaces named "Allen_0" and "Allen_1". I'm not going to go through each step in the above code or how these surfaces can be manipulated individually, but again will refer you to the yt in AstroBlend tutorial for more information.

Using either import method, we do this we should see the following image if we render:

(Recall that we can render a quick image by pressing the little camera and then the "Render" button underneath the Object Selector panel on the far right in "usual.blend". See the first tutorial for more details.)

Independent of import method, its clear this image doesn't look a lot like the top image. To get them to look the same, we have to do a few things:
(1) We have to get rid of the cube
(2) We need to fix the lighting so its not from the lamp in the scene, but from the glowyness of the galaxy
(3) We have to move the camera to our desired location to make the image

Deleting Objects with AstroBlend

We need to delete both the cube and the lamp. We could do this with pointing and clicking (see end of Getting To Know Blender tutorial for details on which keys to press), or we can do it from the python console. The clicking method would work fine for the simple objects we have now, however, deleting this way doesn't delete the objects materials, and if you have a lot of objects in your blend file, this can lead to memory leak. So, the way to do it with AstroBlend is as follows:

science.delete_object('Cube') # delete Cube
science.delete_object('Lamp') # delete Lamp 

Incidentally, you can also delete your uploaded objects, and the best way to do this is to pass the object itself into the deleting routine (don't actually do this unless you want to re-upload!):

science.delete_object(myobject) # delete a previously uploaded object

Now if we render we see something that looks a bit better, but still a bit off. We need to change the lighting format and the camera location to replicate the image on the top of this page:

Lighting in AstroBlend

If you are happy with the default lighting by a lamp in Blender then you can skip this step. Personally, I like my images to be glowy when they can be. To do this, all you need to do is add the following line to your program:

light = science.Lighting('EMISSION') # light by surface emission

Now when we render we see the following:

Which is closer to what we want. One thing to notice is how much slower it takes to render this image. This is because Blender is using an approximate ray-tracing algorithm to calculate the image. That is certainly something to keep in mind when you are rendering many images for a movie. The final thing that is left is to move the camera to a nice location.

Camera Motions in AstroBlend

AstroBlend is setup so that it is easy to move the camera's location and pointing from the command line. Again, you could do this with clicking and hot keys, but since we will probably want to point and move the camera to specific locations in our data sets, its much easier to do with the python console.

cam = science.Camera() # make cam object
cam.location = (-6,0,0) # put it some where nice
cam.pointing = (0,0,0) # where are you pointing this thing?

If you look in the 3D viewer, you'll notice you not only have the galaxy isodensity contours and camera in your space, but you now also have a weird sphere thing in there too:

In the above photo I've enlarged the sphere, but it should be just poking out a little bit in certain places in your 3D viewer. The camera's facing is constrained to always be pointing towards this empty mesh, so by moving the empty, we can point the camera easily instead of always having to calculate rotation angles and whatnot. The sphere is called an "empty" because it will not show up in renders.

Now that we have our camera set up, when we do a quick render we see basically what we want:

Now all we need to do is render this to a file.

Saving Images in AstroBlend

First, you need to specify the location and base file name for your renders. If you are making more than one render, the ending number of the files will start at '0000' by default and increase to '9999' at maximum.

render_directory = '/Users/jillnaiman/blenderRenders/' # save directory, change for your computer
render_name = 'galSurfs_' # base name for renders

render = science.Render(render_directory, render_name)
render.render() # render!

This creates an image in the above directory called "galSurfs_0000.png":

Note if you do render.render() again you'll see a new figure "galSurfs_0001.png" because with each call of render the "frame number" is update. To reset back to the zero frame number simply do the following before your call to render:

render.nframe = 0

Using an External Script

One can copy and paste the above sections of code into the python console, or put them in the text editor and run the full script there. For a reminder of how to run scripts see the first tutorial. This is fine for short scripts, but when they get longer, you might want to save and edit them with an external text editor. For example, on my computer I could save the following combined script in "/Users/jillnaiman/":

import science

filename = '/Users/jillnaiman/yt_files_local/surfaces.obj' # where obj is stored
myobject = science.Load(filename, scale = (50.0,50.0,50.0)) # load and scale x/y/z by 50 times

light = science.Lighting('EMISSION')

cam = science.Camera()
cam.location = (-6,0,0)
cam.pointing = (0,0,0)

render_directory = '/Users/jillnaiman/blenderRenders/' # save directory, change for your computer
render_name = 'galSurfs_' # base name for renders

render = science.Render(render_directory, render_name)

Then to run this script in Blender, all we need to do is put the following two lines into Blender's text editor and run this script with the "Run Script" button as we did before:

filename = "/Users/jillnaiman/"
exec(compile(open(filename).read(), filename, 'exec'))

Get to Know Blender

Previous Tutorial

SPH Movie from Text File

Next Tutorial