Ignoring Out-of-range Texture Values
_lightingstart() and _needsurface()
Message Passing for Shader Execution
This section describes the specific features and limitations of the implementation of the RenderMan Shading Language present in PhotoRealistic RenderMan.
As of version 3.8, PRMan allows you to write new built-in SL functions in C or C++. Such functions overcome many of the limitations of SL-defined functions. Full documentation on the syntax, programming requirements, capabilities and limitations of this new feature are provided in the extensive on-line document RenderMan Shading Language Plugins.
PRMan 10.0 and higher support a new ignoreabove option for all texture-related shadeops. This allows texture filtering to ignore values over a user specified threshold. The option specifies a single float as an argument. All values which exceed the specified value are not averaged into any filtering that is done. This is mostly useful for non-mipped map floating point shadow or zfile textures. Often infinite values cause strange shadow behavior along silhouette edges, this may help alleviate some of this.
PRMan 10.0 and higher support string constant concatenation, using C syntax:
string s = "Hello" "World";
In PRMan, the pieces of attribute state that can be retrieved via the attribute() function are:
Name Type Comment "ShadingRate" float "Ri:ShadingRate" float Same as ShadingRate. This form is preferred "Matte" float "Ri:Matte" float Same as Matte. This form is preferred "Sides" float "Ri:Sides" float Same as Sides. This form is preferred "Ri:Color" color "Ri:Opacity" color "Ri:DetailRange" float[4] "Ri:TextureCoordinates" float[8] "Ri:Orientation" string Either inside or outside "Ri:ShadingInterpolation" string Either smooth or constant "Ri:Transformation" matrix The object to world transformation "cull:backfacing" float "cull:hidden" float "derivatives:centered" float "derivatives:extrapolate" float "dice:binary" float "dice:hair" float "dice:rasterorient" float "displacementbound:sphere" float "displacementbound:coordinatesystem" string "grouping:membership" string The name of the tracegroup or an empty string if there is no group (NOTE: due to an optimization, this is not implemented for surfaces with a ray depth of zero). "geometry:backfacing" float 1.0 if the geometry has had its orientation reversed (normals flipped), which may be due to various factors (negative determinant, orientation, or double shading). 0.0 otherwise. "GeometricApproximation:motionfactor" float "identifier:name" string "irradiance:handle" string "irradiance:filemode" string "irradiance:maxerror" float "irradiance:maxpixeldist" float "photon:estimator" float "photon:globalmap" string "photon:causticmap" string "photon:shadingmodel" string "sides:backfacetolerance" float "sides:doubleshaded" float "shade:strategy" string "stitch:enable" float "stitch:group" float "stochastic:sigma" float "trace:bias" float "trace:displacements" float "trace:maxspeculardepth" float "trace:maxdiffusedepth" float "trace:samplemotion" float "user:myname" variable allows a shader to access a user defined attribute name myname, and requires a variable with type matching the RiDeclare declaration.
"visibility:camera" float "visibility:diffuse" float "visibility:specular" float "visibility:transmission" float "visibility:photon" float
In PRMan, the pieces of option state that can be retrieved via the option() function are:
Name Type Comment "BucketSize" float[2] returns the bucket size "limits:bucketsize" float[2] Same as BucketSize. Preferred. "limits:gridsize" float "limits:texturememory" float "limits:brickmemory" float "limits:geocachememory" float "limits:zthreshold" color "limits:othreshold" color "limits:extremedisplacement" float "Clipping" float[2] see RiClipping "Ri:Clipping" float[2] Same as Clipping. Preferred. "CropWindow" float[4] see RiCropWindow
"Ri:CropWindow" float[4] Same as CropWindow. Preferred. "DepthOfField" float[3] see RiDepthOfField
"Ri:DepthOfField" float[3] Same as DepthOfField. Preferred. "DeviceResolution" float[3] returns the resolution in x and y and the pixel aspect ratio. These are usually the three numbers passed to RiFormat, but may be different when FrameAspectRatio or ScreenWindow are non-square;
"Format" float[3] returns the current resolution as specified in RiFormat
"Ri:Format" float[3] Same as Format. Preferred. "Frame" float returns the current frame number as specified in RiFrameBegin
"Ri:FrameBegin" float Same as Frame. Preferred. "FrameAspectRatio" float returns the current frame aspect ratio
"Ri:FrameAspectRatio" float Same as FrameAspectRatio. Preferred. "Hider" string returns the name of the current hider
"RiHider:name" string Same as Hider. Preferred. "RiHider:jitter" float "RiHider:mpcache" float "RiHider:mpmemory" float "RiHider:mpcachedir" string "RiHider:samplemotion" float "RiHider:subpixel" float "RiHider:maxvpdepth" float "RiHider:zfile" string "RiHider:reversesign" float "RiHider:depthbias" float "RiHider:emit" float "RiHider:secondarycaustics" float "RiHider:extrememotiondof" float "RiHider:sigma" float "RiHider:sigmablur" float "Shutter" float[2] see RiShutter "Ri:Shutter" float[2] Same as Shutter. Preferred. "searchpath:resource" "searchpath:shader" "searchpath:texture" "searchpath:archive" "searchpath:procedural" string returns the indicated searchpath
"shadow:bias" float "shutter:offset" float "statistics:endofframe" float Enable end-of-frame statistics. "statistics:filename.xml" string The name of the statistics XML file "texture:enable gaussian" float "texture:enable lerp" float "trace:maxdepth" float "trace:specularthreshold" float "user:myname" allows a shader to access a user defined option name myname, and requires a variable with type matching the RiDeclare declaration.
The additional arguments that may be used in PRMan with the texture() function:
Attribute Value Comment "filter" "box" this is the default filter if not specified
"filter" "disk" optimized for large blur sizes, and (unlike the other filters) is free of mipmap artifacts because it uses all mipmap levels in its computations; it is about twice as expensive as the other filters.
"filter" "gaussian" "gaussian" should produce higher quality results.
"filter" "lagrangian" The Lagrangian filter is designed from the ground up for speed and sharpness. Texture files with MIP map pyramids are required, and full pyramids are recommended. For each sample, the system selects the next highest resolution image plane and performs a 4x4 bicubic interpolation combined with a 4x4 decimation filter in one pass. The result is equivalent to a point sample of an exactly rescaled and subpixel-shifted image.
"filter" "radial-bspline" specifically designed for optimally filtering displacement textures.
"filter" "ewa" The elliptical weighted average filter uses an elliptical region in S-T space to filter textures. The ellipse may be specified explicitly using the four-point invocation of the texture() call, where the four points define the convex hull of the ellipse. The order of the four S-T points does not matter.
"lerp" 0,0
selects whether to interpolate between adjacent resolutions of a multi-resolution texture in order to smooth the transition between resolutions.
"lerp"takes a uniform float value. Possible values are 0.0 to disable the interpolation and 1.0 to enable it. The default is 0.0 to not interpolate.
In PRMan, the specular and diffuse shadeops respond to two special parameters of the light shader. Any light shader which contains the following parameter
float __nondiffuse = 1
will be ignored by diffuse (note that there are two underbars). Similarly, any light which contains the following parameter
float __nonspecular = 1
will be ignored by both specular and phong. Please note that if the value of the parameter is 0.0, it is not ignored, so this behavior can be controlled both from the RIB file, or procedurally from inside the light shader, if desired. These variables may be either uniform or varying. These special parameters can also be accessed within illuminance statements, as described in the the Specification.
The shadow() function in PRMan has been enhanced to support soft shadows with true penumbral fadeout, simulating shadows of area light sources. The method uses multiple rendered shadow maps to infer visibility information from a light source whose extended geometry is also specified in the shadeop.
The enhanced shadow call is fully backwards-compatible with the Specification, but supports a new form to allow specification of multiple shadow maps in a single call:
float shadow(string maplist, texture coordinates[,parameterlist])
where maplist is a comma-separated list of shadowmap filenames.
In the past, the location of the point light source (as far as shadows were concerned) was implicit in the shadow map matrices. When using multiple maps to simulate an area light source, the user must also specify the size and shape of the intended light source in the shadow shadeop. The following new parameters to shadow support this new soft shadow method:
The old parameters "bias" and "samples" still apply when using the new soft-shadow form:
This new method of generating soft shadows requires multiple shadow maps to infer geometry between the surface being shaded and the light source. For best results, the views should be placed on or near the light source, pointing in the direction of the objects that will cast shadows. All views should fully contain the shadowing objects, although they will contribute slightly different information (due to their differing viewpoints). Specifying more views gives the renderer more information to work with, and thus can improve the quality of the shadows rendered. However, too many views can be unnecessarily expensive, so caution is advised. For most scenes, three to five shadow maps will be sufficient to capture the geometry sufficiently.
The shadow maps that are used for soft shadow information contain additional mip-map information that makes the soft shadow evaluation more efficient. When creating the .shad files, instead of txmake -shadow file.z file.shad you should use txmake -minmaxshadow file.z file.shad which will generate an extended shadow map file. These files are backwards-compatible with the normal shadow call (the new files can be used in the standard calls), but are required when using the soft shadow form.
An example of the soft-shadow form of shadow, using four shadow maps and a triangular light source would be:
attenuation = shadow("view1.shad,view2.shad,view3.shad,view4.shad", Ps, "source", Pl1, Pl2, Pl3, "gapbias", 0.0015, "bias", 0.001);
where Pl1, Pl2, and Pl3 are uniform points (the vertices of the triangle), and view1.shad, view2.shad, view3.shad and view4.shad are new-format shadow map files.
In PRMan 11.0 support for "deep shadowmaps" was added. This output format stores more information per pixel than a normal shadowmap, and has three main advantages over normal shadowmaps:
Creation of these new shadow files is achieved with a new display driver, deepshad.
Starting in 11.0, deep shadow files are supported by the shadow() shadeop, which means that existing shaders can use these shadow files. In addition, the shadow() shadeop can now return a color (like texture()), which is necessary to take advantage of any color information in a deep shadow file.
Currently, the only way to access deep texture maps is through the shadow() shadeop.
The shadow call can also trace rays to compute whether a surface point is in shadow or not, as an alternative to using shadow maps. This functionality was added in release 11.0. Ray tracing is used when the special keyword "raytrace" is supplied as the "shadow map" name. For example, a light source shader might contain a line like this:
attenuation = shadow("raytrace", Ps, "samples", samples);Since the shadow map name is usually passed in as a shader parameter from the RIB file, many light source shaders can be used for ray tracing just by supplying "raytrace" as the map name, without even needing to recompile the shader. However, because ray traced shadow blur and other parameters can add new effects, most people will probably want to write new shaders.
Note: the transmission function provides a direct interface to ray-traced shadow determination, with additional capabilities and options.
Note that shadow rays will only intersect objects that have their transmission visibility enabled. This attribute controls whether shadow and transmission rays can see particular primitives, which is similar to including or excluding particular objects from the shadow map generation pass when using the traditional map-based shadow call.
Ray traced shadows can be used to obtain colored shadows from semi-transparent objects. These colored results are obtained by assigning the results of the shadow call to a color rather than the typical float; this is similar to the deep shadow behavior described above.
The ray-tracing version of shadow() accepts the following optional parameters:
"blur" (float)
- controls the ray traced shadow blurriness.
Units are radians, describing the half-angle of the sample cone.
The effect is similar to sampling a spherical light source of
finite size. You should almost always increase the number of
samples as the blur cone increases to reduce noise."samples" (uniform float)
- causes supersampling of the underlying
transmission function."label" (uniform string)
- applies a label to the shadow rays, it can
be queried by shaders on hit surfaces.
"subset" (uniform string)
- shadows rays will only be tested against objects that
are members of the named groups.
The environment call usually looks up reflection colors in a previously rendered environment map. Starting with release 11.0, it can alternatively trace rays to reflections. This functionality overlaps signficantly with the trace and gather calls, but is provided so that existing shaders that make environment calls can continue to be used. Ray tracing will be used when the special keyword "raytrace" is provided as the environment map name. For example, a surface source shader might contain a line like this:
color e = environment("raytrace", dir);Since the environment map name is usually passed in as a shader parameter from the RIB file, many existing surface source shaders can be used for ray tracing just by supplying "raytrace" as the map name from the RIB file, without even needing to recompile the shader.
Note that environment rays will only intersect objects that have their trace visibility enabled. This attribute controls whether environment, gather, and trace rays can see particular primitives, which is similar to including or excluding particular objects from the environment map generation pass when using the traditional map-based environment call.
When the function return value is assigned to a float, rather than a color,
then environment returns occlusion, which is the fraction
of rays that hit something; when samples=1
then the result
will be either 0 or 1, when samples > 1
then the result will be fractional.
When ray tracing environments, the following optional parameters are supported:
blur
"
(float)
- controls the samplecone of the underlying trace call.samples
"
(uniform float)
- controls the samples of the underlying trace call.bias
"
(uniform float)
- an offset used to prevent self intersection artifacts.occlusion
"
(output varying float)
- returns the alpha value of the environment map
or the coverage of the underlying trace call.opacity
"
(output varying color)
- returns the opacity of the environment or the
underlying trace call.label
"
(uniform string)
- applies a label to the underlying trace call.
subset
" (uniform string)
-
specifies that traced environments only consider objects that are
members of the provided group name.
maxdist
" (uniform float)
-
specifies that traced environments only consider objects closer than the
provided distance.
In addition to the standard data names listed in Table 15.3 in the Specification, the textureinfo() shadeop supports additional query modes.
The exists option can be used to query whether a texture map is actually present, with the shadeop itself returning 1 if the texture exists or 0 otherwise. The variable passed in as the third argument will be ignored. This allows for the following usage:
if (textureinfo(texturename, "exists", 0)) { Ci = texture(texturename); } else { Ci = color(1, 0, 0); /* Mark color as red if texture map is missing */ }
The pixelaspectratio returns a float, which is the original aspect ratio of the source image used to generate the texture map (useful because a resize operation is often involved when creating the texture map). This lookup does not work on environment maps.
PRMan 10 and higher support two special no-op shadeops which serve as optimization hints to the Irma re-renderer.
The _lightingstart() shadeop marks the end of light-independent calculations (texturing and noise for example), and the beginning of light dependent calculations (such as calls to diffuse(), illuminance(), etc); this allows Irma to cache all shading results which occur before lighting.
The _needsurface() shadeop works in the same way for atmosphere shaders, except that it should be inserted just prior to any call which sets the value of the output color Ci.
The shader compiler attempts to place both shadeops in reasonable parts of the shaders. For complicated shaders, the result may not always be optimal and can be overridden by explicitly adding the statements to the shader sourcecode. For example:
surface matte( float Ka=1, Kd=1 ) { normal Nf; Nf = faceforward(normalize(N),I); Oi = Os; _lightingstart(); Ci = Os * Cs * ( Ka*ambient() + Kd*diffuse(Nf) ) ; }
Sending parameters with rays to shaders on hit objects
When gather()
is used to trace rays into the scene,
it can send arbitrary values with each ray. The sent values are
used to override shader parameters with matching names in the
shaders on objects hit by the rays. The syntax is:
float val = (some override value); gather(..., "send:surface:Ks", val, ...)which means take the local (shooting shader) variable "val" and use its value to override parameter "Ks" of the hit surface shader. These sent overrides are far more general than the alternative string-only "label" mechanism, and the receiving shader need not use
rayinfo()
or be otherwise
"ray-aware" to use them.
Values sent in this way can be of any type.
If a particular shader does not have the given parameter (same name
and type), then the send is ignored.
Gather retrieves values, such as the color of the hit surface, using similar variable-fetching syntax.
Sending parameters to LightSource shaders
Similarly, surface and volume shaders calling illuminance()
can now send parameter overrides to lightsource shaders directly.
Illuminance() can also fetch
back auxilliary values computed inside lightsource shaders as well.
These extensions, which share the
gather() messaging syntax,
complement the existing lightsource
and surface
message passing functions: the new "forward" send requires no
special knowledge in the lightsource, and variables other than
predefined "output" parameters can be fetched.
Here is an example which sets the light's "intensity" parameter and retrieves the light's "temperature" value after execution, if it exists:
float k = 0; float val = (some override value); illuminance(..., "send:light:intensity", val, "light:temperature", k) { ... illuminance block, using k }Just as with gather, above, if the lightsource shader does not have the specified variables, then sends are ignored and fetches do not change the current local value. Note that
illuminance
sends and fetches are restricted to the "light:" namespace
of queries.
Irradiance maps
Irradiance maps are sparse volumetric files containing samples representing
an irradiance (incoming illumination) function. Irradiance maps
are superseded by the new and more general point cloud files, and is no
longer supported.
float irradiancecache( string
operation, string handle, string filemode, point P, normal N, ... )
This function is no longer supported.
Explicit Light Cache Control
As an optimization, PRMan has always cached the results of
light shader invocations between multiple calls to
illuminance. This may sometimes lead to the wrong
result if rerun light shaders change their output values due to
a combination of message passing and changes in the invoking
surface or atmosphere shader. In prior releases, this
behaviour could be defeated by the use of "P = P"
prior to new calls to illuminance, or by using
illuminance("category", P+vector(0), ...).
PRMan 12.5 now allows explicit light-cache control via the
illuminance parameter "lightcache". The new parameter
is used this way:
illuminance("category", ..., "lightcache", "reuse")
or
illuminance("category", ..., "lightcache", "refresh")
The default setting is "reuse", which is the existing
behavior: to use previously cached output values for all light
shaders which match the category, if they exist from previous
invocations of the light shaders (due to previous illuminance
statements). The setting of "refresh" clears the
output values and reruns the light shaders, again for only
those light shaders which match the category.
Using "lightcache" "refresh" is more efficient than
the old method of using "P=P" before the illuminance
call, because only the lights matching the category are
affected. It is similar to using illuminance("category",
P+vector(0)), but it is more explicit, easier to
understand, and possibly more efficient.
gridpattern()
float gridpattern( string pattern, float context, ... )
Gridpattern returns a one or a zero for each shading point,
according to the specified pattern and context. Like other varying
functions, gridpattern
returns a result which may be
different at each shading point on the underlying grid; however,
unlike most other functions, the output is intentionally a function
of the shading grid's structure. It is typically used to produce
a mask using one of the built-in pattern generators,
the mask is then used in a varying conditional to apply specific
operations to the on subset of points. Furthermore,
some patterns can be applied iteratively, in conjunction with a
preserved context, and will become successively more
refined in terms of the sets of points selected, until
all of the points on the grid have been visited.
The available patterns are:
"gridpoint"
one shading point on at a time
convergent
"binaryrefinement"
sparse-to-dense sampling
convergent
"concentric"
rings around the periphery of the grid ¹
convergent
"checkerboard"
alternating on and off points ¹
non-convergent
"random"
a random distribution of on and off points
non-convergent
¹ some
patterns have undefined results on non-rectangular internal grid
types
Note: The internal structure and content of shading
grids are inherently implementation dependent, and they are strongly affected
by the input geometry types, shading rate, camera settings, and numerous other
tuning parameters. There are no guarantees about the specific
behavior or reproducibility of patterns.
The context variable is used to carry state between iterative
calls to gridpattern
; the meaning and usage of context
depends on pattern. Usually context should be initialized
to zero prior to first use. Each pattern will modify context
differently and it should generally be assumed to be private data owned
by gridpattern
. Some patterns guarantee convergence in the sense
that after a series of calls, all grid points will have been "visited" and
further calls will return zero for all points.
The shading language supports several familiar conditional constructs
which cause operations to occur on grid subsets; consider:
if (s < 0.5) k += 1;
The varying variable k will only be incremented at the points which
satisfy the condition. Similarly, gridpattern produces a varying
float mask which is 1 for some points, 0 for others. This mask
might be used directly (as a float) or used in further conditional expressions.
For example, for a shader executing on a tiny 5x5 grid:
varying float patctx = 0; /* initialize the context */
varying float f = gridpattern("concentric", patctx);
the variable f will now contain the following pattern:
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
the patctx variable allows gridpattern to track a particular
context through repeated calls, so a subsequent call to:
varying float g = gridpattern("concentric", patctx);
would produce, in g,
and f+g would be
0 0 0 0 0
0 1 1 1 0
0 1 0 1 0
0 1 1 1 0
0 0 0 0 0
1 1 1 1 1
1 1 1 1 1
1 1 0 1 1
1 1 1 1 1
1 1 1 1 1
After another iteration (in which only the center spot would be one), the
pattern will have converged, meaning that all of the gridpoints
will have been visited and subsequent calls to gridpattern (with
the same context variable) will return all zeros.
Another pattern type, named "binaryrefinement", tries to select
a few widely spaced points for the first pass, and then more points which
are less widely spaced for the second pass, etc., until all points have been
visited.
The while
loop usually has the desirable behavior that
only shading points which satisfy the specified condition continue to be
evaluated within the loop. When all points finally fail the condition,
or have had break applied to them, then the loop is complete.
There are a few rare cases, such as with
gridpattern, where it is instead desirable
for every point which was active when the loop started to remain
potentially active if any shading point passes the conditional.
In these situations, the addition of the "any"
modifier string after while
causes the success/fail state
at every shading point to be reset (rather than preserved) each time
through the loop.
The while "any" construct allows looping on
gridpattern calls until a convergence occurs. In this mode a
different set of gridpoints will be active during each iteration. So a
typical convergence loop might look like this:
while "any" (0 != gridpattern("binaryrefinement", patctx)) {
/* do things on this subset */
}
See the gridpattern example
for an illustration.
Limitations
The Shading Language compiler and interpreter in
PhotoRealistic RenderMan has certain limitations which
must be taken into consideration when shaders are written for
PhotoRealistic RenderMan. If these are not understood,
shaders which seem to compile correctly will produce incorrect
results.
Area Functions Inside Conditionals
One shouldn't use any area or neighborhood-sensitive functions
inside conditional blocks whose conditional test depends upon
varying expressions. The sensitive Shading Language
functions are: texture(), environment(), bump(), shadow(),
Deriv(), Du(), Dv(), area(), calculatenormal(). These
functions depend on comparing various values at more than one
point on the surface. A conditional will partition the object
being shaded into two sets, those that passed the conditional
and those that did not. Values are undefined in places where the
conditional failed, but they are still referenced by their
neighbors who did pass the conditional. This causes the
comparisons to be undefined, and leads to visible artifacts. As
a result, the following:
if (u < 0.5) {
s0 = u * 2;
Ci = texture("foo", s0, t);
} else {
s0 = (u - 0.5) * 2;
s0 = s0 * s0;
Ci = texture("foo", s0, t);
}
should be coded as:
if (u < 0.5) {
s0 = u * 2;
} else {
s0 = (u - 0.5) * 2;
s0 = s0 * s0;
}
Ci = texture("foo", s0, t);
This evaluates the texture access outside the conditional,
allowing it to filter correctly.
Note that this is not a problem for conditional blocks whose
conditional test depends upon a uniform expression,
since the object cannot be partitioned into two sets by a
uniform expression, by definition.
Broad Solar Lights
The solar block is used by distantlight to
specify a light which is emitted from infinity along a
particular axis direction. In this use, the solar block
takes two arguments, the axis vector A, and
0.0, which is the angular width of the emission
cone. The RenderMan specification refers to the fact that this
cone can have non-zero width, and specifically mentions the use
of solar with no arguments to write an environment
mapping light source using a 360 degree cone.
PhotoRealistic RenderMan does support solar cones of
any angular width, including the zero-argument omnidirectional
case. The meaning of a non-zero width solar cone is extremely
confusing to explain. Nonetheless, imagine a distant light
source which is willing to emit light in any of a range of
directions (not just down its axis vector), and merely needs the
renderer to tell it which direction is pointed most favorably
toward the specular highlight of surface. Another way of
thinking of it is a large area light source, located at
infinity, where each point on the area light is emitting a
little spotlight.
The most obvious example is an environment-mapping light. The
light wants to cast the environment texture map like a slide
projector from infinity, and just needs to know which pixel on
the map will reflect back into the camera. The pixel to choose
is based entirely on the reflection direction R of the
surface; the light itself is willing to send light in any
direction as needed.
The second example is a diffuse skylight, where light is
arriving on the surface from everywhere above it, and would like
to be colored by all of it (radiosity-style). In this case, the
solar light cone would be a hemisphere pointed down.
At this time, PhotoRealistic RenderMan will not
point-sample the area light source, but it will attempt to find
the most favorable direction by considering the size of the
solar cone and the surface's reflection direction. The light
direction vector L inside the solar block will
be this direction. In the specific case of an omnidirectional
light, this L will be exactly equal to -R. For
narrower solar cones, it will be some vector between -R
and A, such that it falls within both cones and is
somewhat central to the intersection of the cones.
Here is a light source which casts an environment map onto a
surface. Notice the use of the __nondiffuse flag to put
the environment only in the specular component (since
environment maps are really specular reflections of the
worldsphere).
light envir ( string mapname = ""; float __nondiffuse = 1; ) {
solar() {
Cl = environment(mapname, vtransform("world", -L));
}
}
Note: Some surface shaders use non-standard reflection
directions for their specular highlights. For example, some
anisotropic shaders (such as brushedmetal) have
multiple specular highlights, none of them in the "normal"
place. solar's guess about which direction is the most
favorable direction will be wrong in these cases.
Pixar Animation Studios
(510) 752-3000 (voice)
(510) 752-3151 (fax)
Copyright © 1996-
Pixar. All rights reserved.
RenderMan® is a registered trademark of Pixar.