The aim of this sample is to show how CySlice generated displacement and texture space normal maps can be combined in a shader for use with Renderman® compliant renderers. The ZIP archive below contains these files:
cyslice_subd_matte.sl
Surface shader. This piece of code is the bit that does the normal
mapping:
# get normalized surface normal Nn = normalize( N); # get normal encoded in color map Ct = color texture( cyslice_subd_normS, s, 1 - t, ...); Nt = normal( (Ct * 2) - 1); # calculate surface tangent in the S texture coordinate direction Vs = normalize( (dPdu * Dv( t)) - (dPdv * Du( t))); # make tangents perpendicular to the surface normal Vt = normalize( Nn ^ Vs); Vs = normalize( Vt ^ Nn); # build texture space matrix from the three orthogonal vectors M = matrix( comp( Vt, 0), comp( Vt, 1), comp( Vt, 2), 0, comp( Vs, 0), comp( Vs, 1), comp( Vs, 2), 0, comp( Nn, 0), comp( Nn, 1), comp( Nn, 2), 0, 0, 0, 0, 1); # rotate the normal map vector into the current texture space N = ntransform( M, Nt); In plain english, the dense polymesh surface normal, encoded into the normal map by CySlice, is rotated into current world space and replaces the surface normal calculated by the renderer. The fact that its a texture space normal map means that, even though surface changes shape during animation, the same map can be used; object space normal maps are invalidated once the object changes shape. Note: A common assumption we've seen in online discussions about texture space normal maps is that the surface tangent can be accessed by calling Deriv(P,s). It certainly returns a tangent, but not the S direction tangent. When questioned about Deriv(), Pixar support replied with; "Its use is pretty much discouraged because it's not clear that it computes any useful quantity." The formula we're using, dPdu * Dv(t) - dPdv * Du(t), was distilled from some code found in the nVidia SDK (see nvIO/NVMeshMender.cpp). It may seem a little odd (like, why would the T derivatives give the S direction tangent?) but the following comparison shows that it does actually work. |
![]() N |
![]() dPdu |
![]() Deriv(P,s) |
![]() dPdu * Dv(t) - dPdv * Du(t) |
||||||
The black arrows are small pieces of surface displaced along the direction of the labeled vector. The red and green stripes are calculated by step'ing across some random S and T coordinates. The correct S tangent should point in the direction of the red stripes. |
cyslice_subd_disp.sl
Displacement shader. A standard shader, apart from this piece of code
at the end:
if( (cyslice_subd_normS == "") && (cyslice_subd_normU == "")) { N = calculatenormal( P); } What that's saying is that, if no normal map is defined, the surface normal should be recalculated after P is displaced. If a normal map is defined there's no need to recalculate the normal; it'll be replaced in the surface shader anyway, but also we need the unmodified normal for correct construction of the texture space matrix (see above). |
subd-256_1.5.tif
Displacement map, 256x256 by 16bit. We decided to use a low
resolution displacement map to illustrate how, when combined with
normal mapping, there doesn't have to be any loss in quality.
| |
subd-1024.normS.tif
Texture space normal map, 1024x1024 by 8bit.
| |
head-0256D.rib/jpg
Displacement only scene file and render.
Notice how the surface quality is affected by the low resolution of
the displacement map, but the silhouette doesn't look to bad. In
particular, the left eye ridge is clearly textured.
| |
head-1024S.rib/jpg
Normal map only scene file and render. Notice how the surface
quality is good, but the silhouette is smooth.
| |
head-0256D+1024S.rib/jpg
Combined displacement and normal map scene file and render. Notice
how the surface quality is still good and the silhouette is
textured.
| |
Thanks to Paul Hourmouzis for encouragement during the development of the normal mapping shader, and for running several batches of test renders.
File | Description | md5sum.txt What's this? | |||||||
1,666,634 | a45aa83e137b55628d53534fd92855dd
| |
Note: To grab a binary file, first move the mouse pointer over the link, click the right mouse button, then select "Save Link As..." or "Save Target As...". |
![]() |