FORUMS

# Writing a Vector Displacement Shader

By
-
-
Industry
• Film & VFX
• Games
• Design Visualization
Subject
• Rendering
• 1.0
Products
• Arnold
Skill Level
• Beginner
Duration
30 min

Arnold allows shaders to displace vertices in a polymesh node. The vertices are displaced in the direction and magnitude of the vector returned by the shader.

In this example shader, we compute a nonlinear displacement based on a noise function. This can displace along the normal of a surface like a traditional noise displacement shader, but it also has the option of blooming out the peaks and valleys of the displacement. This is done by computing a noise function and its delta in the direction of two surface derivatives. This delta determines the amount the derivatives would be deflected by the displacement. By repeatedly deflecting the derivatives, we create a non-linear displacement that arcs out from the surface. This is equivalent to displacing the surface, computing the normal, and displacing again repeatedly. Here is an animation showing the effect.

`/* * nonlinear noise displacement shader */#include #include #define _octaves (params.INT)#define _freq (params.FLT)#define _amplitude (params.FLT)#define _bloom (params.FLT)#define _type (params.INT)AI_SHADER_NODE_EXPORT_METHODS(NonlinNzMethods);#define ENUM_SCALAR_TYPES { "perlin", "abs_perlin", "recursive", "abs_recursive", NULL };#define PERLIN 0#define ABS_PERLIN 1#define RECURSIVE 2#define ABS_RECURSIVE 3const char *types_enum[] = ENUM_SCALAR_TYPES;node_parameters{   AiParameterInt ("octaves" , 3);   AiParameterFlt ("freq" , 1);   AiParameterFlt ("amplitude", 1);   AiParameterFlt ("bloom" , 1);    AiParameterEnum("type" , PERLIN, types_enum);}float scalarfunc(AtPoint P, int type, int octaves){   float doubler = 1;   float NzAccum = 0;   switch (type)   {      case PERLIN:         return AiPerlin3(P);      case ABS_PERLIN:         return fabs(AiPerlin3(P));      case RECURSIVE:         for (int i = 0; i             NzAccum += AiPerlin3(P*doubler) / doubler;            doubler *= 2;         }         return NzAccum;      case ABS_RECURSIVE:         for (int i = 0; i             NzAccum += fabs(AiPerlin3(P*doubler)) / doubler;            doubler *= 2;         }         return NzAccum;   }   return AiPerlin3(P);}shader_evaluate{   const AtParamValue *params = AiNodeGetParams(node);   AtPoint Ploc, Uloc, Vloc; // noise sample location, and over in U and V locations   float Np, Nu, Nv; // noise at P, noise at location over in U and V   float Udelt, Vdelt; // delta in the noise over in U and V   float delta = .01; // distance delta for noise samples   AtVector U, V;   if (!AiV3IsZero(sg->dPdu) && !AiV3IsZero(sg->dPdv))   {      // tangents available, use them      U = sg->dPdu;      V = sg->dPdv;   }   else   {      // no tangents given, compute a pair      AiBuildLocalFramePolar(&U, &V, &sg->N);   }   if (_type > ABS_PERLIN)   {      // adjust delta to highest frequency in recursive noise      delta *= pow(.5, _octaves) * 2;   }   Ploc = sg->Po * _freq;   Uloc = Ploc + U * delta;   Vloc = Ploc + V * delta;   // noise sampled at P, and over in U and V   Np = scalarfunc(Ploc, _type, _octaves);   Nu = scalarfunc(Uloc, _type, _octaves);   Nv = scalarfunc(Vloc, _type, _octaves);   Udelt = (Nu - Np) * _bloom;   Vdelt = (Nv - Np) * _bloom;   AtPoint Pstepped = sg->P;    int steps = 10;   float stepscale = _amplitude / steps;   for (int i = 0; i    {      // stepdir is the cross product of the derivatives      AtVector stepdir = AiV3Cross(U,V);      // deflect the derivatives      U = AiV3Normalize(U + (stepdir * Udelt * stepscale));      V = AiV3Normalize(V + (stepdir * Vdelt * stepscale));      Pstepped += stepdir * Np * stepscale;   }   sg->out.VEC = Pstepped - sg->P;}node_initialize{}node_update{}node_finish{}node_loader{   if (i > 0) return FALSE;      node->methods = NonlinNzMethods;   node->output_type = AI_TYPE_VECTOR;   node->name = "nonlinear_noise";   node->node_type = AI_NODE_SHADER;   strcpy(node->version, AI_VERSION);   return TRUE;};`

And here is the .ass file that uses this shader:

`options{ AA_samples 3 xres 640 yres 480 background mysky GI_diffuse_depth 1 GI_diffuse_samples 3}plane{ name myplane point 0 -8 0 normal 0 1 0 shader groundshader}polymesh{ name mysph nsides 6 1 UINT 4 4 4 4 4 4  vidxs 24 1 UINT   0 4 5 1 1 5 6 2 2 6 7 3 3 7 4 0 3 0 1 2 4 7 6 5  vlist 8 1 b64POINT   AAB6wwAAAAAAAHrDAAB6QwAAAAAAAHrDAAB6QwAAAAAAAHpDAAB6wwAAAAAAAHpDAAB6wwAA+kMAAHrDAAB6QwAA+kMAAHrDAAB6QwAA+kMAAHpDAAB6wwAA+kMAAHpD smoothing on subdiv_type catclark subdiv_iterations 7 disp_map sphere_disp matrix   0.94693 0 0.321439 0  0 1 0 0  -0.321439 0 0.94693 0  0 0 0 1  shader sphere_surf}standard{ name sphere_surf Kd 0.15 Kd_color 0.8 0.8 1 Ks 0.1 Ks_color 0.8 0.8 1 specular_roughness 0.3 Ksss 0.5 Ksss_color 1 0.05 0.2 sss_radius 80 80 80}nonlinear_noise{ name sphere_disp type perlin freq 0.025 amplitude 80 bloom 1}lambert{ name groundshader Kd_color 0.4 0.4 0.4}persp_camera{ name mycamera fov 11  position 3677.0129 1039.1904 597.0592  look_at 0 250 0  up 0 1 0 }point_light{ name key position -6000 10000 6000  radius 400 color 1 0.7 0.2 intensity 2}sky{ name mysky color 0.7 0.8 0.9 intensity 0.9}`

Some examples of looks that can be obtained with this shader, along with the shader settings from the .ass file:

Lumpy nodules:

`type abs_perlin freq 0.025 amplitude 80` Fractal 'pomegranite':

`type recursive freq 0.025 amplitude 40 bloom 3` Fractal 'cauliflower':

`type abs_recursive freq 0.025 amplitude 40 bloom 3h` A 'brain coral' look:

`type abs_perlin freq 0.025 amplitude -40 bloom -4`  Posted By
Tags
• Arnold
• Rendering
• 1.0

Got questions? Visit the Autodesk forums.

## Latest Learning Tutorials

Maya 2019, Arnold 5.0

## Texturing Characters in Arnold

Maya 2017, Arnold 5.0, Maya 2019

## Interior Lighting in Arnold

3ds Max 2020, Arnold 5.1, 3ds Max 2019, +1 others

1 Comment