Succulent Plant - Breakdown

Problem: To make a shader for a vase in Open Shading Language (OSL) that replicates the one seen on the image on the right (Reference A)

Answer:

Procedural Vase Shader in Houdini (OSL)

I developed a custom vase shader in Houdini using Open Shading Language (OSL), featuring two core functions for wood grain and resin effects.

  • Wood Grain Function: This function uses Perlin noise and sine waves to simulate natural wood grain, with random imperfections for added realism.

  • Resin Function: A recursive noise function generates a fluid, translucent resin texture, with adjustable parameters for scale, frequency, and intensity.

Alongside these key material functions, I included additional features for enhanced detail:

  • Burn Masking: Adds subtle burn marks to the wood for an aged and weathered appearance.

  • Customizable Colors and Controls: The shader offers flexible inputs for wood grain scaling, resin properties, and color blending.

By building these custom functions and controls, I created a shader that combines realism with a high degree of artistic flexibility.

OSL Shader Code:

Reference A

//Making a function for the wood grain

float WoodPattern(float scale2, float scale1, float frequency, int Variation, float patchamount)

{

//Normalizing (putting object in object space)

vector objP = transform("object",P);

//Making vector for the noise used, and a vector for the scaling of the noise

vector NoiseWood = objP;

vector NoiseWoodVec = vector(1,10,1);

//Vector options for wood rotation

vector p1 = vector (0,0,0);

vector p2 = vector (0,0,1);

vector p3 = vector (1,0,0);

//Rotating the Wood pattern

vector objProtate = rotate(objP,7.75,p1,p2);

//Multiplying noise by the scale

NoiseWood = noise("perlin",objProtate*NoiseWoodVec*scale1);

//Using differnet noises from the vector as tiles need an alternating pattern, so I should be able to call 2 different noise floats from the function

//To get wood pattern using a noise into a sine wave to get the wood like grain look

float stripeWood= 1-((0.5*sin(frequency*NoiseWood[Variation]))+0.5);

//Making noise for adding random value patches for the grain to give the grain a more natural look

float Woodpatchrandom = smoothstep(0.1,0.7,noise (objP*14));

//Making noise for adding random dots/marks across the pattern for a more natural look

float Woodpatchdots = smoothstep(0.3,1,noise ("perlin",objP*patchamount));

//Adding random values across the pattern

stripeWood = stripeWood * Woodpatchrandom;

//Adding random dots across the pattern

stripeWood = stripeWood + Woodpatchdots;

return stripeWood;

}

//Make function for resin

float resinweight(float ResinScale, float ResinFrequency ,float ValueAdd, float ValueMultiply)

{

vector Vector = P;

vector p = Vector * ResinScale;

int MaxIterations = 64;

for(int i=1; i < MaxIterations; i++)

{

vector newp = p;

newp[0]+=ResinFrequency/float(i)*sin(float(i)*p[1]+35/12.4+0.5*float(i))+22.9;

newp[1]+=ResinFrequency/float(i)*sin(float(i)*p[0]+35/12.4+0.5*float(i+8.93))-22.9;

p=newp;

}

float ColorF = ValueMultiply*(smoothstep(0.2,1,sin(35+p[1]+p[0]-M_PI/(4.0+sin(35)))*0.3+0.5)+ValueAdd);

return ColorF;

}

shader vase(

//inputs

//Base color inputs

color WoodColor = color(0.7,0.41,0.13),

color GrainColor = color(0.88,0.54,0.2),

color ResinColor = color(0.27,0.46,0.17),

//Wood Grain control inputs

float Wood_Grain_Scale =0.5[[float min= 0, float max=10]],

float Wood_Grain_Stretch =0.7[[float min= 0, float max=10]],

float MaskScale = 1[[float min= 0, float max=100]],

float rotatewoodMask = 1[[float min= 0, float max=100]],

float frequencyWood =16[[float min= 0, float max=100]],

float ScaleWoodMask =16[[float min= 0, float max=100]],

float debug1 =0.3[[float min= -1, float max=1]],

float debug2 =1[[float min= 0, float max=1]],

float debug3 =1[[float min= 0, float max=10]],

//vector uvw = P,

output color Base_Color = 0,

output color Roughness = 0,

output color Transmitioncolor = 0,

output float TransmitionMask =0,

)

{

vector objP = transform("object",P);

//Rotation Function

vector p1 = vector (0,0,0);

vector p2 = vector (0,0,1);

vector p3 = vector (0,1,0);

vector p4 = vector (1,0,0);

float rotates = 22.5;

float rotatewood = 37.5;

vector objProtate = rotate(objP,rotates,p1,p3);

vector objProtateWood = rotate(objP,rotatewoodMask,p1,p2);

float WoodPattern1 = WoodPattern(Wood_Grain_Stretch*3, Wood_Grain_Scale*5, frequencyWood, 0, 62);

float WoodPattern2 = WoodPattern(Wood_Grain_Stretch*3, Wood_Grain_Scale*5-0.5, frequencyWood, 0, 62);

//making a vector to subtract one wood color from to get a very slightly darker one

vector WoodColorSubtract = vector (0.1,0.05,0.03);

color BaseColorMix1 = mix (WoodColor,GrainColor,WoodPattern1);

color BaseColorMix2 =mix (WoodColor-WoodColorSubtract,GrainColor-WoodColorSubtract,WoodPattern2);

float WoodGrainMask1= smoothstep(0.3,0.5,noise(objProtateWood*ScaleWoodMask));

color BaseColorMixcombo = mix(BaseColorMix1,BaseColorMix2,WoodGrainMask1);

//Make mask for resin

vector noisescaleresin = vector (1,0.9,1);

//Masking in woodpatterns with different colors together

//Resin/WoodSeperationMask

float VaseMask2 = step(0.33,sin(5.45*noise (objProtate*MaskScale*noisescaleresin)));

//Roughness Mask

float VaseMask5 =0.9* (1-step(0.33,sin(5.45*noise (objProtate*MaskScale*noisescaleresin))));

//using smoothstep and pushing values further than 1 and -1 to get more of a gradient for the desired look

//Burn on Wood Mask

float VaseMask3 = smoothstep(-0.55,0.35,sin(5.45*noise (objProtate*MaskScale*noisescaleresin)));

//Burn on Resin Mask

float VaseMask4 = smoothstep(debug1,debug2,sin(5.45*noise (objProtate*MaskScale*noisescaleresin)));

color BaseColorMixcomboedge= mix(BaseColorMixcombo,BaseColorMixcombo/1.35,(VaseMask3*3));

color BaseColorMask = mix (BaseColorMixcomboedge,1,VaseMask2);

//Dividing Color to get the warm burn look

color ResinColorBurn = color(-0.05,0.55,0.96);

color ResinColorMask = mix (ResinColor/(debug3*ResinColorBurn),ResinColor,VaseMask4);

Base_Color = BaseColorMask;

Roughness = VaseMask5;

Transmitioncolor=ResinColorMask;

TransmitionMask= VaseMask2;

}