Hair Geometry

Rendering hair is a difficult problem for triangle rendering because the number of hairs is typically very large (hundreds of thousands or millions), and each hair has a very large bounding box that it fills poorly. For this reason a special hair primitive was introduced which is based on a curve description of hairs with a compact data layout to avoid the storage overhead in comparison to triangle strips.

object "object_name"
    [ visible [on|off] ]
    [ shadow [on|off] ]
    [ shadowmap [on|off] ]
    [ trace [on|off] ]
    [ reflection [mode] ]
    [ refraction [mode] ]
    [ transparency [mode] ]
    [ select [on|off] ]
    [ tagged [on|off] ]
    [ caustic [on|off] ]
    [ globillum [on|off] ]
    [ caustic [mode] ]
    [ globillum [mode] ]
    [ finalgather [mode] ]
    [ box [xmin ymin zmin xmax ymax zmax] ]
    [ motion box [xmin ymin zmin xmax ymax zmax] ]
    [ max displace value ]
    [ samples min  max ]
    [ data (null|"name") ]
    [ tag number ]
    hair
        [ material "name" ]
        [ radius radius ]
        [ approximate segments ]
        [ approximate hair curve_approx ]
        [ degree degree ]
        [ max size size ]
        [ max depth depth ]
        [ hair n ]
        [ hair m hm ]
        [ hair t ht ]
        [ hair u hu ]
        [ hair radius ]
        [ vertex n ]
        [ vertex m vm ]
        [ vertex t vt ]
        [ vertex u vu ]
        [ vertex radius ]
        scalar [ nscalars ]
            scalar_list
        hair [ nhairs ]
            hair_offset_list
    end hair
end object

The object header is similar to the headers for all other geometry types, but the standard group...end group block is replaced with hair...end hair. This block begins with a common header, followed by a scalar list, followed by a hair list. The header has the following optional statements:

material
specifies the material to use when shading a hair. It is shared by all hairs in this object. If missing, the material is inherited from the instance.
radius
specified the global radius of all hairs. This number may be overridden by hair or vertex radii if defined. It is defined in object space and typically a very small number. The default is 1.
approximate
specifies the number of segments used to approximate each Bézier hair segment, if the degree is 2 or 3. The default is 1.
approximate hair
allows to attach a regular curve approximation for adaptive tessellation, like length, distance, and angle criteria, with optional view-dependency. Parametric approximations depending on the degree of the hair curves are supported as well, providing similar functionality as the simpler form above but is more consistent with general approximations. Especially for curved hair and hair at higher viewing distance, the new approximation support allows to automatically adjust the approximation detail to the area of interest. A lower number of hair segments results in reduced memory consumption and increased rendering speed.
To see benefit from curvature-dependent approximations, hair needs to be represented with a degree > 1.
degree
is the degree of the Bézier curves that approximate each hair. Valid values are 1 (linear), 2 (quadric), or 3 (cubic). Each hair must have (1 + degree·segments) vertices, where the number of segments is a positive number.
max size
defines the leaf size of the hair BSP tree that is created by mental ray to facilitate finding hairs during rendering. The default is 32. For a discussion of BSP trees, see the options block, which defines parameters for the BSP tree used for triangle intersection.
max depth
defines the depth of the hair BSP tree. The default is 20.
hair n
if present, specifies that each hair has a normal vector associated consisting of three scalars.
hair m
if present, specifies that each hair has hm motion vectors associated consisting of three scalars each.
hair t
if present, specifies that each hair has ht texture scalars associated.
hair u
if present, specifies that each hair has hu user scalars associated.
hair radius
if present, specifies that each hair has a radius scalar value associated. No global radius value may be present.
vertex n
if present, specifies that each vertex has a normal vector associated consisting of three scalars. If present, hair n is ignored.
vertex m
if present, specifies that each vertex has vm motion vectors associated consisting of three scalars each. In this case, any hair m is ignored.
vertex t
if present, specifies that each vertex has vt texture scalars associated.
vertex u
if present, specifies that each vertex has vu user scalars associated.
vertex radius
if present, specifies that each vertex has a radius scalar value associated. No global radius may be present. If present, hair radius is ignored.
scalar
the list of scalars starts with the number of expected values enclosed in [] brackets, followed by scalar_list containing the actual scalar values.
hair
the list of hairs starts with the number of expected values enclosed in [] brackets, followed by hair_offset_list containing the actual offset integer values.

It does not make sense to specify hair n if there is also a vertex hair because all hair normals will be overridden. Similarly, if vertex radii are used, no hair radii (or the global radius) should be present. If any normals are specified, the hairs are not intersected like cylinders but like oriented flat ribbons.

The scalar list defines a sequence of hairs. Each hair consists of a certain number of scalars that describe the entire hair, followed by another number of scalars that describe each vertex of the hair. The layout of these sequences of scalars is identical for all hairs, except that each hair may have a different number of vertices. It is not possible to have one hair with three texture scalars and another with two, for example. Here is the exact sequence of scalars for a single hair:

Header:

Vertices:

All vertices begin with three scalars for the location. The order of the other vertex scalars, and the order of the header scalars, is determined by the order of hair and vertex statements. The lists above correspond to the syntax listing at the beginning of this section: first n, then m, then t, then u, then radius.

Each hair has only one header but multiple vertices. As described above, the number of vertices must be (1 + degree·segments), where segments may be different for each hair. This number is not encoded in the hair but in the separate hair list. Note, that hairs do not use texture vectors like polygons and free-form surfaces but texture scalars. It's up to the shader to interpret state→tex_list as a list of scalars, and properly use them in groups of one, two, three, or whatever is appropriate. This makes it possible to avoid the third null component if only two-component UV textures are needed, for example, which can save a lot of memory because hair objects tend to have a very large number of vertices, probably millions.

The hair list specifies where in the scalar list each hair begins, by offset in the scalar list such that 0 is the first scalar, 1 is the second scalar, and so on. At the end of this offset list, one extra offset specifies the last scalar plus one, where the next hair would begin if there were another one. If all scalars are used (which is normally the case), the first offset is 0 and the extra one at the end equals the number of scalars. Here is a simple example for a hair object:

object "hair1"
    visible trace shadow
    tag 1
    hair
        material "mtl"
        radius 0.3
        degree 1
        hair t 2
        vertex t 1
        scalar [ 42 ]
            11 22
            0.0     0.0     0.0 1111
            0.0     1.0     0.0 1112
            1.0     1.0     0.0 1113
            33 44
            0.0     0.0     0.0 1114
            0.0     -1.0    0.0 1115
            -1.0    -1.0    0.0 1116
            55 66
            0.0     0.0     0.0 1117
            0.0     -1.0    0.0 1118
            -1.0    -1.0    0.0 1119
        hair [ 4 ]
            0 14 28 42
    end hair
end object

This example consists of three hairs, each with three vertices. The header of each hair consists of two texture scalars (11 22, 33 44, and 55 66, respectively). Each vertex consists of four scalars, three for the location in object space and one more for a vertex texture. The shader will receive three texture scalars, two from the hair and one more from the vertices. The former are copied from the hair that was hit, and the latter is interpolated from the nearest two vertices. They are stored in state→tex_list as if it were a scalar array, header texture scalars first, so the two hair texture scalars end up in tex_list[0].x and tex_list[0].y, and the vertex texture scalar ends up in tex_list[0].z. It is best to re-cast tex_list to a miScalar pointer in the hair shader.

Hair objects may use the same material shaders as any other object, but often special hair material shaders are used because although hair may be a cylinder, it is too thin for properly resolving the upper and lower edge and the diffuse terminator and the highlight. There is a simplified hair illumination shader in the base shader library called mib_illum_hair that implements a much more effective hair shading model.

When rendering hair with ray tracing, mental ray uses the ray origin to generate a temporary, flat surface which is as ray-facing as possible while lying on the hair axis. A corresponding normal is generated. When rendering with the rasterizer, the hair geometry is converted to camera-facing geometry, and intersected in the same manner as other geometric types. Note, that a switch from the latter to the former is performed if both the rasterizer and the ray tracing are active. Hair intersections cannot happen on the same hair strand twice in a row, thus avoiding surface acne from this switch. This does have the consequence that individual hair strands cannot cast shadows on themselves.

Copyright © 1986, 2013 NVIDIA Corporation