Sunday, February 5, 2012

LDD Pov-Ray Rendering tutorial

Several people asked me to provide more details on rendering LEGO models like the Trabi or the MiG-15. Until the surprise is built, let me entertain you with a tutorial on how to render LEGO models.

Before we start, let me underline that this is by no means a straightforward process. You should start only if you are ready to edit programming-language-like text files and download, unzip and manually organize files. It needs several hours, some debugging and tweaking, and possibly more hours to compute the final image.

Ok, if you have read so far, then you are at least curious to know what it takes. What follows is based on Koyan's excellent tutorial with some tweaks. Let's see how a typical render is made!

Step 1: I install LDraw. LDraw is an community-maintained database of LEGO bricks that will be needed for our photorealistic renders. As you can guess, this is needed once only. I download and unzip several files:
  • LDraw official parts (Core Files and Libraries / LDraw Parts Library, zip format). I unzip them in a directory on my computer. This is my LDraw directory.
  • Many bricks are not in the official release. I download the LDraw unofficial parts as well. Unzip those files as well and move them in the LDraw directory too. Some files will already exist, I don't overwrite them.
  • LGEO is another parts library that is made especially for POV-Ray renders. It contains fewer parts but those are nicely modeled. I download and unzip it into a separate directory.
| The original dome in  Lego Digital Designer |

Step 2: I convert my model into LDraw format (.ldr). There are several options:
  • Use an LDraw editor. There are several editors listed on the LDraw Downloads page. I am a Mac guy, I use Bricksmith. For Windows I have heard MLCad works well.
  • Use Lego Digital Designer. This is what I mostly do since I find it very easy and intuitive to use it. It can export your creation in LDraw format, but be prepared that you still need an LDraw editor to fix some rough edges: some parts may be missing or incorrectly positioned.
| The LDraw model in Bricksmith after adding missing bricks. |

Step 3 (optional): I verify my model in LDView: I check that your model looks good. It also helps to find good camera positions.

| This is how it looks in LDView. |

Step 4: Once I am happy with how the model looks in my editor, it is time to convert it to POV-Ray format (.pov). It is important to get the .ldr file right, once it is in .pov format then it can be only edited in a text editor. I use L3P to do the conversion. There are several frontends for this tool, I use L3P Launcher (Mac only). I have heard that for Windows L3Pao is a good option.
  • I need the raw model only, so I don't ask for light sources or cameras. The only flag that I really need is the -lgeo to make sure LGEO is used for all bricks for which it is available.
  • Once I have the .pov file, I edit it with a text editor to comment out the following lines towards the beginning of the file using // and /* */:

    // #declare L3Bumps = 0;  // 1=on 0=off
    // #declare L3Ambient = 0.4;
    // #declare L3Diffuse = 0.4;
    // #declare L3Ior     = 1.25;
    // #declare L3NormalBumps = normal { bumps 0.01 scale 20 }
    // #declare L3NormalSlope = normal { bumps 0.3 scale 0.5 }
  • I do the same with the camera and light source settings at the end of the file:

    //// Camera
    // L3P's automatic camera positioning was based on the following:
    // ...
    camera {
    light_source {
    <-286.921,-614.005,165.654>  // Latitude,Longitude,Radius: 60,-120,662.616
    color rgb <1,1,1>
Step 5: I download and unzip some includes that will be used. Like the LDraw installation, this is done once:
Step 6: Install POV-Ray and MegaPOV on your computer. They are different versions of a free, tweakable rendering program:
  • I have mostly used MegaPOV, the latest version is 1.2.1, you can download it from here:
  • POV-Ray is needed even if I do the renders with MegaPOV because we need some of its includes. The latest official release is 3.6.2 or 3.6.1 depending on your system:
  • It is also worth trying the 3.7 Beta releases of POV-Ray. It lacks some of the advanced features of MegaPOV (like adaptive radiosity) but it supports multi-core rendering which makes things substantially faster:
    (For the Mac, see this message.)

Either way, I add the following paths to the system include directories:
  • koyancolors directory
  • lg_colors directory
  • the POV-Ray /include directory
  • the LGEO /lg directory
Step 7: Even if I put my model on an empty plane, it looks much better if you have a non-uniform environment reflected on it. To do this I need some HDR light probes. These probes contain the intensity of light from all directions for a certain scene. HDR means the light can be very different from some directions than others. Several good sites to get probes:
Step 8: Prepare your files. Besides the .pov file I use two other files: specifies the light sources, camera and radiosity settings. Here's what I typically use:

#version unofficial MegaPov 1.20;
// With POV-Ray 3.7 replace the line above with: #version 3.6

// The total amount of light in the scene. If the image is too dim
// or too bright, I can adjust this.
#declare LIGHT = 1.3;

// Radiosity settings.
#declare LDRAW_MTL = 30;
#declare RADIOSITY = 1;

global_settings {
  assumed_gamma 1.4
  max_trace_level LDRAW_MTL
  #ifdef (RADIOSITY)
    radiosity {
      adaptive  // Uncomment this for POV-Ray 3.7
      pretrace_start 0.08
      pretrace_end   0.004
      count 400
      nearest_count 5
      error_bound { 0.2 adaptive 1.5 10 }
      // For POV-Ray replace the line above with: error_bound 0.2
      recursion_limit 1
      low_error_factor 0.7
      gray_threshold 0
      minimum_reuse 0.01
      brightness 1.0
      adc_bailout 0.01/2
  photons {
      count 300000
      autostop 0
      jitter .4

#ifdef (RADIOSITY)
// Defines the light probe as a sphere with radius.
sphere {
  pigment {
    // See
    image_map { hdr "../probes/20061210_hd.hdr" once interpolate 2 map_type 1 }

  // This is pretty important to get right, otherwise your sky will
  // light from below or from the side. Do some test renders with a wide
  // camera angle to see if it looks ok.
  rotate<180, 0, 0>
  finish { ambient 3.5*LIGHT diffuse 0 }

// I tend to use an area light source too.
light_source {
  <200, -5000, 0>;
  color rgb 0.5*LIGHT
  area_light <1000, 0, 0>, <0, 0, 1000>, 20, 20
  adaptive 1

// Settings for the values we have commented out in the pov file.
#declare L3Bumps = 1;  // 1=on 0=off
#declare L3NormalBumps = normal { bumps 0.01 scale 20 }
#declare L3NormalSlope = normal { bumps 0.3 scale 0.5 }
#declare L3Ambient = 0.0;
#declare L3Diffuse = 1.0;
#declare L3Ior     = 1.25;

// Specify multiple cameras this way will let you reuse this file
// with the environment for multiple camera angles.
#switch (CAMERA_ANGLE)
#case (1)
camera {
  // You can use LDView to get reasonable coordinates.
  // Or just experiment.
  location <960, -800, -580>
  look_at  <50, 0, 0>

  sky      -y
  right    -4/3*x
  // Field of view in degrees. Essentially your zoom level.
  angle    40
  rotate   <0,1e-5,0> // Prevent gap between adjecent quads

// We need some floor as well.
object {
  // The value before 'hollow' is the altitude of the plane. Unless
  // you built below your reference pane in LDD this should be 0.
  // Otherwise, adjust.
  plane { <0, 1, 0>, 0 hollow }
  texture {
    pigment { color rgb <1.0,1.0,1.0> }
    finish { ambient L3Ambient diffuse L3Diffuse }

// Include custom color definitions.
#include ""

Model_cam1.pov: A masterfile to include everything:

#declare CAMERA_ANGLE = 1;
#include ""
#include "Model.pov"

There are several things to note in
  • Camera position and destination. I usually use LDView to find good initial settings (in Tools / Show POV-Ray Camera Info) and then experiment. Using the #switch above allows me to define several cameras in the file and create one small master .pov file for each. This keeps the whole thing a bit more structured.
  • Light sources. As a start I use the light probe. It may need some experimentation to get the brightness and the orientation right (this is usually done with a camera with a high field of view). The light probe itself gives me very nice diffuse lighting, but with MegaPOV it can be very tricky to get the radiosity settings right so that there are no artifacts. So I tend to add another light source to the image. Large area lights work well here.
| MegaPOV settings. |

Step 9: Finalize all settings: time to start rendering real images! For a start, I use a small resolution like 320x240. The scene will almost certainly need tweaking: camera, light sources, light levels, radiosity, etc. Doing this at a low resolution allows me to iterate fast. Even so, it may an hour or so to get this right.

Step 10: Do the final render. MegaPOV allows me to do antialiasing but it does not work well in all cases. I typically render the image in a high resolution like 2000x1500 and then scale it down after the rendering is complete.

| The final result. |

Depending on the scene and settings, this may take anywhere from a few minutes to more than 24 hours.

If your scene contains transparent bricks, then the render will be especially slow. In such cases I create a non-transparent version of my model for calibration purposes (Step 9). 

This is really it. It needs quite a few steps, but with some tweaks you get really nice images out of that.

The model used for the renders is the Dome of Eger, created by Yooha.


  1. Excellent post, thank you for so precise and useful instructions!

  2. This comment has been removed by a blog administrator.

  3. I have found only two detailetd tutorials about LEGO rendering with POV-ray. Then I wanted to try both. The 'light recipe' from Peter Dornbach sound more interesting to me because of the light proves he use. I successfully run a rendering with little modifying to the code. But POV ray fail (message:ilegal instruction) when I include transparent bricks. I think is related to the radiosity settings, because in a different test using: (including transparent elements) global_settings {
    max_trace_level 10
    radiosity {
    Rad_Settings(Radiosity_Fast, on, off)
    If I put max_trace_level above 4, POV-ray crashes in the middle of render. Any advice would be very appreciated.