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;
}