RSL
Rotating Texture Space


return to main index



Introduction

There are occasions when a shader will not use 's' and 't' directly but will, instead, use rotated copies of them so that, for example, a texture map or a procedural pattern can be pivoted around a point defined in texture space - figures 1 and 2.



Figure 1


Figure 2 - pivot point s = 0.5, t = 0.5


This tutorial shows how the built-in RSL function rotate() can be used for texture space rotations. Listing 1 provides the code of a simple texture mapping shader. For brevity, the shader only calculates the diffuse lighting component. The tutorial assumes the reader is using the Cutter text editor to compile their shaders and to render their rib files.


Listing 1 (simpleTex.sl)


surface
simpleTex(float  Kd = 1;
         string  texname = "")
{
color    surfcolor = 1;
normal   n = normalize(N);
normal   nf = faceforward(n, I);
  
/* STEP 1 - set the apparent surface opacity */
Oi = Os;
  
/* STEP 2 - read the texture */
if(texname != "")
    surfcolor = texture(texname, s, t);
  
/* STEP 3 - calculate the diffuse lighting component */
color    diffusecolor = Kd * diffuse(nf);
  
/* STEP 4 - set the apparent surface color */
Ci = Oi * Cs * surfcolor * diffusecolor;
}

Listing 2 applies the shader to a polygon, the result of which is shown in figure 1.



Listing 2 (simpleTex.rib)


Option "searchpath" "shader" "@:../shaders"
Option "searchpath" "texture" "../textures"
  
Display "simpleTex" "it" "rgb"
Format 250 250 1
Projection "perspective" "fov" 20
ShadingRate 1
  
Translate  0 0 3.1
Rotate -70 1 0 0
Rotate 0   0 1 0
Scale 1 1 -1
WorldBegin
    TransformBegin
        LightSource "pointlight" 1 "intensity" 25 
                    "from" [1 4 1]
    TransformEnd
    # txmake -mode periodic ../tiffs/IMAGE.tif ../textures/IMAGE.tex
    Surface "simpleTex"
            "Kd" 1.0
            "texname" ["DESTINATION.tex"]
    AttributeBegin
        Polygon "P" [-0.5 0 -0.5  -0.5 0  0.5  
                      0.5 0  0.5   0.5 0 -0.5]
                "st" [0 0  0 1  1 1  1 0]
    AttributeEnd
WorldEnd

Note 1

If Cutter's RenderMan preferences have been set correctly, the editor will automatically add the Option's (lines 1 and 2) prior to rendering the file. Refer to the tutorial "Cutter: Shader Writing" for information about setting Cutter's RenderMan preferences.

Note 2

The line of code shown in red in listing 2 can be executed, as if it were entered into a command window (Windows) or a terminal (Linux/MacOSX), by selecting it and pressing alt + e, control + e or Apple + e. The directories in which the source tiff file and the output texture file are located are assumed to be one level above Cutter's working directory. Alternatively, txmake can be invoked from a command window or a terminal.


Using the RSL rotate() Function

Because the rotate() function operates on 3D points, the 2D 'st' texture coordinates referenced within a shader must be converted to their 3D equivalent. If a shader allows a user to specify a pivot point in 'st' space, for example,

    Surface "rotateTex"
            "Kd" 1.0
            "texname" ["IMAGE.tex"]
            "pivot_s" 0.5
            "pivot_t" 0.5
            "angle" 45

using the rotate() function is reasonably straight forward - listing 3.


Listing 3 (rotateTex.sl)


surface
rotateTex(float Kd = 1,
                angle = 0,
                pivot_s = 0.5,
                pivot_t = 0.5;
        string  texname = "")
{
color    surfcolor = 1;
normal   n = normalize(N);
normal   nf = faceforward(n, I);
  
Oi = Os;
  
/* Use 'st' to define the 'xz' coordinates of a 3D point */
point stPnt = point(s, 0, t);
  
/* The rotate function requires 2 points to define
   an axis of rotation - here represented by the "Y" axis 
*/
point p1 = point(pivot_s, 0, pivot_t);
point p2 = point(pivot_s, 1, pivot_t);
point stRot = rotate(stPnt, radians(angle), p1, p2); 
  
if(texname != "")
    surfcolor = texture(texname, stRot[0], stRot[2]);
  
color    diffusecolor = Kd * diffuse(nf);
Ci = Oi * Cs * surfcolor * diffusecolor;
}

However, if a shader enables a user to directly define the axis of rotation, by specifying two 3D points ie.

    Surface "rotateTex"
            "Kd" 1.0
            "texname" ["swazi.tex"]
            "pnt1" [0 0 0]
            "pnt2" [0 1 0]
            "angle" 45

the use of rotate() is less intuitive. Passing pnt1 and pnt2 directly to the rotate() function ie.

    point stPnt = point(s, 0, t);
    point stRot = rotate(stPnt, radians(angle), pnt1, pnt2); 

will not yield the correct rotation. Listing 4 provides the code for a shader that ensures that points pnt1 and pnt2 are transformed to a user defined coordinate system. By default it is assumed x,y,z values of the points are measured from the origin of the object's coordinate system.


Listing 4 (rotateTex2.rib)


surface
rotateTex2(float Kd = 1,
                 angle = 0;
           point pnt1 = point "object" (0, 0, 0),
                 pnt2 = point "object" (0, 1, 0);
          string space = "object";
          string texname = "")
{
color    surfcolor = 1;
normal   n = normalize(N);
normal   nf = faceforward(n, I);
  
Oi = Os;
  
// Note the use of the transform() function
point stRot = rotate(point(s, 0, t), 
                     radians(angle),
                     transform(space, pnt1),
                     transform(space, pnt2));
  
if(texname != "")
    surfcolor = texture(texname, stRot[0], stRot[2]);
  
color    diffusecolor = Kd * diffuse(nf);
Ci = Oi * Cs * surfcolor * diffusecolor;
}

Section "3D Noise & Coordinate Space" of the tutorial "RSL: Writing Surface Shaders" addresses the issue of using the transform() function when applying noise to a surface.



A User Defined Function

Listing 5 provides the code for a function that encapsulates the conversion of the 'st' coordinates and the center of rotaion to 3D points.


Listing 5 (rotations.h)


void rotateST(float s_in, 
                    t_in, 
                    angle, 
                    pivot_s, 
                    pivot_t;
       output float s_out,
                    t_out)
{
point stPnt = point(s_in,0,t_in);
point p1 = point(pivot_s, 0, pivot_t);
point p2 = point(pivot_s, 1, pivot_t);
point stRot = rotate(stPnt, radians(angle),p1, p2); 
s_out = stRot[0];
t_out = stRot[2];
}

If the function is saved as, say, rotations.h in the same directory as rotateTex.sl, then its use greatly simplifies the shader code - listing 6.


Listing 6 (modified rotateTex.sl)


#include "rotations.h"
  
surface
rotateTex(float Kd = 1,
                angle = 0,
                pivot_s = 0.5,
                pivot_t = 0.5;
        string  texname = "")
{
color  surfcolor = 1;
normal n = normalize(N);
normal nf = faceforward(n, I);
float  ss, tt;
  
if(texname != "") {
    rotateST(s, t, angle, pivot_s, pivot_t, ss, tt);
    surfcolor = texture(texname, ss, tt);
    }
Oi = Os;
Ci = Oi * Cs * surfcolor * Kd * diffuse(nf);
}



© 2002- Malcolm Kesson. All rights reserved.