I am trying to implement (in C#) an image perturbation algorithm presented in the book "Texturing and modeling - K. Perlin et al" (page 91 if anyone has it), which distorts an image. The following code is in Renderman language: The texture access

Ct = texture("example.tx", s, t);

is replaced by

point Psh;
float ss, tt;
Psh = transform("shader", P);
ss = s + 0.2 * snoise(Psh);
tt = t + 0.2 * snoise(Psh+(l.5,6.7,3.4));
Ct = texture("example.tx", ss, tt);

transforming the image on the left to that on the right. enter image description here

From what I undestood, instead of accessing coordinate $(s,t)\in[0,1]$ we access slighty perturbed coordinates $(ss,tt)$ and display them at place $(s,t)$, thus creating an image that looks slightly perturbed.

$snoise(x)$ is defined as $(noise(x)*2)-1$, mapping noise from $[0,1]$ to $[-1,1]$, and in the RenderMan documentation $noise(P)$ where P is a point, returns a value based on some noise (most likely perlin or lattice). (http://renderman.pixar.com/resources/current/RenderMan/noiseFunctions.html)

What I don't understand is what the transform function does, which is supposed to map the 3d point P into the "shader" space, and how can it be implemented. Also, I'm not sure whether noise(x) returns a 3d point, a float (would make more sense) and if I can use a simple 2d implementation of Perlin's noise to reach the same desired effect.


As you've surmised, the transform() function transforms points from one co-ordinate space to another. (There are also vtransform() and ntransform() for transforming direction vectors and normal vectors, respectively.) The string argument names the co-ordinate space to transform into.

The Renderman Shading Guidelines have this to say about it:

At the start of shader execution, all point, vector, normal, and matrix variables are expressed in the "current" coordinate system. Exactly which coordinate system is "current" is implementation-dependent. It just so happens to be that "current" is "camera" for PRMan*, but you should never count on this behavior - it is entirely possible that other RenderMan compliant renderers (including future renderers from Pixar) may use some other space (like "world") as "current" space.

It goes on to give a case like this as an example. Most lighting calculations should be done in camera space, but evaluating a noise function should be in the object's co-ordinate system, because you want the noise to stay the same as the object moves through world space.

In your C# implementation, you'll also need to transform the point being shaded from camera space to the object's co-ordinate system. Maybe you've done this already before computing the texture co-ordinates. If not, you'll need to multiply by the object's transformation matrix. Remember that the only use of this transformed point is as an input (like a seed) to the Perlin noise generator. It sets the domain that the noise varies over: world space co-ordinates.

In RSL, the noise() function can return any type you like: a float, a color, a point, or a vector. As you're adding it to another float (u or v), you'll get a float in this code. Really, the two noise() calls, added to s and t, are acting to generate a single 2D noise vector. In your own code, if you are using a 2D vector to store your texture co-ordinates, you can use a single noise function that returns a 2D vector, to get the same effect in one line of code.

If you're interested in making a nice noise generator, Shadertoy has a lot of noise shaders featuring different variants of Perlin noise with different properties (isotropic or not, configurable smoothness and bandwidth) and is worth looking at for inspiration as well as implementation hints.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.