next up previous contents
Next: A Simple Scene With Up: How Do Several Shaders Previous: RenderMan   Contents

Mental Ray

Now let's make some experiments with mental ray. For light shaders please read chapter 3.13 of the book [5], for material shaders please read chapter 3.8 of the same book. Here is a simple scene similar to the RenderMan example in mental ray's MI file format:

verbose off
link "base.so"
$include <base.mi>
link "contour.so"
$include <contour.mi>
link "physics.so"
$include <physics.mi>

options "opt"
        samples         -1 2
        contrast        0.1 0.1 0.1
        object space
end options

camera "Camera"
        frame           1
        output          "rgb" "test.rgb"
        focal           12.0
        aperture        32.0
        aspect          1.33333333333
        resolution      508 380
end camera

instance "Camera_inst" "Camera"
        transform
                        1.0 0.0 0.0 0.0
                        0.0 1.0 0.0 0.0
                        0.0 0.0 1.0 0.0
                        0.0 0.0 0.0 1.0
end instance

light "Lamp"
        "mib_light_point" (
                "color" 1.0 1.0 1.0,
                "shadow" false,
                "factor" 1,
                "atten" false,
                "start" 0,
                "stop" 1
        )
        origin  0.0 0.0 0.0
end light

instance "Lamp_inst" "Lamp"
        transform
                        1.0 0.0 0.0 0.0
                        0.0 1.0 0.0 0.0
                        0.0 0.0 1.0 0.0
                        0.0 0.0 0.0 1.0
end instance

material "mtl"
        opaque
        "mib_illum_lambert" (
                "ambient" 0.0 0.0 0.0,
                "diffuse" 0.8 0.8 0.8,
                "ambience" 0.0 0.0 0.0,
                "mode" 1,
                "lights" [ "Lamp_inst" ]
        )
end material

object "Plane"
        visible trace shadow
        tag 1
        group
                1.0 1.0 0.0
                1.0 -1.0 0.0
                -1.0 -1.0 0.0
                -1.0 1.0 0.0
                v 0
                v 1
                v 2
                v 3
                p "mtl" 0 3 2 1 
        end group
end object

instance "Plane_inst" "Plane"
        transform
                        1.0 0.0 0.0 0.0
                        0.0 1.0 0.0 0.0
                        0.0 0.0 1.0 0.0
                        0.0 0.0 1.0 1.0
end instance

instgroup "rootgrp"
        "Camera_inst"
        "Lamp_inst"
        "Plane_inst"
end instgroup

render "rootgrp" "Camera_inst" "opt"

You should check that this MI file renders fine with your local mental ray installation before you proceed. Here is a Makefile which can be used to render the scene by simply typing make in your Unix shell. Later we will add commands to compile the shaders etc. but for now the Makefile looks like this:

# Makefile

MENTALRAY       = /usr/local/mentalray
RENDER          = $(MENTALRAY)/linux-x86/bin/ray
INCLUDE         = $(MENTALRAY)/common/include
LDPATH          = $(MENTALRAY)/linux-x86/shaders
EXT             = so

all: test.rgb

test.rgb: test.mi
        $(RENDER) -include_path $(INCLUDE) -ld_path $(LDPATH) test.mi

clean:
        -rm -f *~ *.$(EXT) test.rgb

Let's change the light shader first. This way we are sure that the material shader does call the light shader and later we can replace the material shader as well. We don't need shadows at the moment. The light shader looks like this:

/* myLight.c */

#include <shader.h>

struct myLight
{
  miColor color; /* color of the light source */
};

DLLEXPORT miBoolean 
myLight(
        register miColor* result,
        register miState* state,
        register struct myLight *paras
        )
{
  *result = *mi_eval_color(&paras->color);
  return(miTRUE);
}

To compile the shader I changed the Makefile:

# Makefile

MENTALRAY       = /usr/local/mentalray
RENDER          = $(MENTALRAY)/linux-x86/bin/ray
INCLUDE         = $(MENTALRAY)/common/include
LDPATH          = $(MENTALRAY)/linux-x86/shaders
OBJEXT          = o
SOEXT           = so
CC              = gcc
LINK            = ld
DEFINES         = -DLINUX -DLINUX_X86 -DX86 -DEVIL_ENDIAN -D_GNU_SOURCE \
-D_REENTRANT -DSYSV -DSVR4 -Dinline=__inline__
CFLAGS          = -ansi -fPIC -O3 -mpentiumpro -fexpensive-optimizations \
-finline-functions -funroll-loops -fomit-frame-pointer -frerun-cse-after-loop \
-fstrength-reduce -fforce-mem -fforce-addr $(DEFINES)
INCPATH         = -I. -I$(INCLUDE)

all: test.rgb

myLight.$(OBJEXT): myLight.mi myLight.c
        $(CC) -c $(CFLAGS) $(INCPATH) -o myLight.$(OBJEXT) myLight.c

myLight.$(SOEXT): myLight.$(OBJEXT)
        $(LINK) -shared -export-dynamic -o myLight.$(SOEXT) myLight.$(OBJEXT)

test.rgb: test.mi myLight.$(SOEXT)
        $(RENDER) -include_path $(INCLUDE) -ld_path $(LDPATH) test.mi

clean:
        -rm -f *~ *.$(SOEXT) *.$(OBJEXT) test.rgb

One thing I forgot to mention is that you need a header file for your shader as well. In this case I named it myLight.mi and it looks (so far) like this:

# myLight.mi

declare shader
        color "myLight" (color "color")
        version 1
        apply light
end declare

We are ready to use the light shader now. So please edit the original MI file and add respectively change the following lines:

...
link "myLight.so"
$include <myLight.mi>
...
light "Lamp"
        "myLight" (
                "color" 1.0 1.0 1.0
        )
        origin  0.0 0.0 0.0
end light
...

Compile the shader with make myLight.so and copy (or softlink) the files myLight.so and myLight.mi to a place where mental ray can find it before you render. For my local installation the compiled files go to /usr/local/mentalray/linux-x86/shaders and the header files can be found in /usr/local/mentalray/common/include. You could also specify the full path to your files in the MI file.

It's time to change the material shader. We add a few more lines to the Makefile:

...
mySurface.$(OBJEXT): mySurface.mi mySurface.c
        $(CC) -c $(CFLAGS) $(INCPATH) -o mySurface.$(OBJEXT) mySurface.c

mySurface.$(SOEXT): mySurface.$(OBJEXT)
        $(LINK) -shared -export-dynamic -o mySurface.$(SOEXT) mySurface.$(OBJEXT)

test.rgb: test.mi myLight.$(SOEXT) mySurface.$(SOEXT)
        $(RENDER) -include_path $(INCLUDE) -ld_path $(LDPATH) test.mi
...

The header file for the new material shader looks like this:

# mySurface.mi

declare shader
        color "mySurface" (
                color           "ambient",
                color           "diffuse",
                color           "specular",
                scalar          "spec_exp",
                integer         "mode",
                array light     "lights"
        )
        version 1
        apply material
end declare

The material shader takes three colors as parameters for the ambient, diffuse, and specular color contributions. In the RenderMan shader I just set them in the latest version of the shader without giving the user the choice to change the values from outside (in the RIB file). Go back and make the modifications needed, if you want to. The mental ray material shader itself needs a bit of explanation:

/* mySurface.c */

#include <shader.h>

struct mySurface
{
  miColor ambient;   /* ambient color */
  miColor diffuse;   /* diffuse color */
  miColor specular;  /* specular color */
  miScalar spec_exp; /* Phong exponent */
  int mode;          /* light mode: 0..2 */
  int i_light;       /* index of first light */
  int n_light;       /* number of lights */
  miTag light[1];    /* list of lights */
};

DLLEXPORT miBoolean 
mySurface(
          miColor* result,
          miState* state,
          struct mySurface* paras
          )
{
  int i;
  int n_light; /* number of light sources */
  int i_light; /* offset of light sources */
  int samples;
  miColor* diffuse;
  miColor* specular;
  miColor color;
  miColor sum;
  miScalar spec_exp;
  miScalar dot_nl;
  miScalar phong_specular;
  miTag* light;
  miVector dir;

  /* ambient */
  *result  = *mi_eval_color(&paras->ambient);
  diffuse  =  mi_eval_color(&paras->diffuse);
  specular =  mi_eval_color(&paras->specular);
  spec_exp = *mi_eval_scalar(&paras->spec_exp);
  n_light  = *mi_eval_integer(&paras->n_light);
  i_light  = *mi_eval_integer(&paras->i_light);
  light = mi_eval_tag(paras->light) + i_light;
  for (i = 0; i < n_light; i++)
    {
      sum.r = sum.g = sum.b = 0;
      samples = 0;
      while (mi_sample_light(&color, &dir, &dot_nl, state, *light, &samples))
        {
          /* diffuse */
          sum.r += dot_nl * diffuse->r * color.r;
          sum.g += dot_nl * diffuse->g * color.g;
          sum.b += dot_nl * diffuse->b * color.b;
          /* specular */
          phong_specular = mi_phong_specular(spec_exp, state, &dir);
          if (phong_specular > 0.0)
            {
              sum.r += phong_specular * specular->r * color.r;
              sum.g += phong_specular * specular->g * color.g;
              sum.b += phong_specular * specular->b * color.b;
            }
        } /* while (mi_sample_light(...)) */
      if (samples) 
        {
          result->r += sum.r / samples;
          result->g += sum.g / samples;
          result->b += sum.b / samples;
        } /* if (samples) */
    }
  return(miTRUE);
}

First of all you will notice a difference between the header file and the structure used in the C file. Every parameter translates easily from the description in the header file to the C equivalent. Except the light array which results in three variables in C.

The ambient contribution does not come from a call to evaluate all ambient light sources like in RenderMan but is simply an input parameter. Which means that every material shader which does take ambient contribution into account should have a parameter to let the user set the color etc.

The material shader loops over a light list which comes from the input parameter as an array of lights. There is no such looping mechanism as the illuminate statement in RenderMan.

Area lights are taken into account by the loop using mi_sample_light because that function must be called in a loop until it returns miFALSE. Every sample of the light is summing up within the loop. That's why you have to divide by the number of samples. For lights which are not area lights there should be only one sample taken. But be careful not to divide by zero.

The result of the call mi_sample_light to the light shader will be in the variable color. There are two other return values provided if the pointers are nonzero: dir is the direction from the current intersection point in the state to the light, dot_nl is the dot product of this direction and the normal in the state.

For the specular contribution I took the Phong factor but there are other choices22:

Finally we make some adjustments to the MI file to render a sphere:

...
link "myLight.so"
$include <myLight.mi>
link "mySurface.so"
$include <mySurface.mi>
...
light "Lamp"
        "myLight" (
                "color" 1.0 1.0 1.0
        )
        origin  0.0 0.0 0.0
end light
...
material "mtl"
        opaque
        "mySurface" (
                "ambient"  1.0 0.0 0.0,
                "diffuse"  0.0 1.0 0.0,
                "specular" 0.0 0.0 1.0,
                "spec_exp" 40,
                "mode" 1,
                "lights" ["Lamp_inst"]
        )
end material
...
$include "sphere.mi"

instance "sphere_inst" "sphere"
        transform
                        0.2 0.0 0.0 0.0
                        0.0 0.2 0.0 0.0
                        0.0 0.0 0.2 0.0
                        0.0 0.0 2.0 0.2
        material        "mtl"
end instance

instgroup "rootgrp"
        "Camera_inst"
        "Lamp_inst"
        "sphere_inst"
end instgroup

render "rootgrp" "Camera_inst" "opt"

Figure 4: The hair primitive
\includegraphics[scale=0.75]{hair.ps}

The MI file sphere.mi comes with the examples of the book [4] and you can download the examples and all shaders from mental images' FTP site. I made a softlink to this file at a location where mental ray can find it. In contrast to RenderMan and other renderers mental ray does not support other primitives beside the usual polygons, NURBS surfaces, and subdivision surfaces. There is one exception as you can see in figure 4.


next up previous contents
Next: A Simple Scene With Up: How Do Several Shaders Previous: RenderMan   Contents
Jan Walter 2004-02-09