Succulent Plant Commercial
A short product spot whose hero object is a wood and resin vase. Rather than texture it, I wrote the surface procedurally in Open Shading Language inside Houdini, matching a photographic reference.
- Role
- Shader author, look development
- Tool
- Houdini, Open Shading Language
- Context
- SCAD, VSFX 319
- Year
- 2024
1 problems, 1 answers.
Write an OSL shader for the vase that matches the reference photo.
ApproachWood grain function. Perlin noise fed into a sine wave gives the grain; a second noise adds random patches and dots so no two tiles read the same.
Resin function. A recursive noise builds the fluid, translucent resin, with controls for scale, frequency and intensity.
Burn masking. Subtle burn marks darken the wood near the resin for an aged, weathered edge.
Controls. Grain scale, resin properties and color blending are all exposed, so the shader stays art-directable rather than a one-off.
The shader, as written.
Open Shading Language, authored in Houdini. Wood grain, resin and burn masks are separate functions so each can be tuned on its own.
//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;
}