Choose fBm or refractive pools. Nine palettes use the internal warp fields for secondary pigments and iridescent color. Pool brush controls create a moving ripple; optics controls depth, refraction, dispersion, absorption and lighting. Refraction is a layered screen-space approximation.
Fractal selects noise basis, octave spacing and folds. Geometry stretches, twists or mirrors the domain. Warping controls the two displacement scales and directions. Structure adds terraces and marble bands. These shape controls also feed the refractive pool.
Suppresses the local ripple; 1 fully suppresses it.
auto_orbit
float
0.6
0 … 1
Amount of automatic ripple motion; zero uses the manual brush position.
optics
Name
Type
Default
Range
Doc
depth
float
1.2
0 … 4
Optical thickness of the pigment layers.
refraction
float
0.8
0 … 2
Screen-space bending of submerged pigment layers.
dispersion
float
0.3
0 … 1
Red/blue separation of refracted samples.
absorption
float
0.65
0 … 2
Depth-dependent colored attenuation.
relief
float
0.65
0 … 2
Surface-normal strength.
light_x
float
-0.45
-1 … 1
Horizontal light direction.
light_y
float
0.55
-1 … 1
Vertical light direction.
fractal
Name
Type
Default
Range
Doc
scale
float
3.2
0.5 … 10
octaves
int
6
1 … 8
roughness
float
0.5
0.15 … 0.8
warp
float
4
0 … 8
stages
int
2
0 … 2
seed
float
0
0 … 100
noise_basis
string
value
value | gradient | cells
Smooth value noise, directional gradient noise, or cellular distances.
fold
string
plain
plain | ridged | billow
Fold each octave into crests or rounded cloud lobes.
fold_mix
float
1
0 … 1
Strength of ridged or billow folding; plain ignores this.
lacunarity
float
2.03
1.1 … 3.5
Frequency multiplier between octaves.
octave_angle
float
36.869898
-180 … 180
Rotation between noise octaves in degrees.
motion
Name
Type
Default
Range
Doc
speed
float
0.22
-1 … 1
time_warp
float
0.65
0 … 2
evolution
float
1
0 … 3
Rate of shape evolution within the warp layers.
drift_x
float
0
-1 … 1
Horizontal domain drift, scaled by speed.
drift_y
float
0
-1 … 1
Vertical domain drift, scaled by speed.
color
Name
Type
Default
Range
Doc
palette
string
mineral
mineral | ember | ink | monochrome | opalescent | oxidized copper | bioluminescent | petroleum | orchid
veins
float
0.35
0 … 1
contrast
float
1.25
0.5 … 2.5
exposure
float
1.1
0.25 … 2.5
complexity
float
0.7
0 … 1
palette_shift
float
0
-1 … 1
color_spread
float
1.35
0.25 … 3
iridescence
float
0.25
0 … 1
saturation
float
1.05
0 … 2
geometry
Name
Type
Default
Range
Doc
rotation
float
0
-180 … 180
Rotate the entire domain in degrees.
stretch
float
0
-3 … 3
Directional stretch in powers of two; zero is isotropic.
twist
float
0
-4 … 4
Radius-dependent rotation for spirals and whorls.
pan_x
float
0
-10 … 10
Horizontal travel through the noise domain.
pan_y
float
0
-10 … 10
Vertical travel through the noise domain.
symmetry
int
0
0 … 12
Zero disables folding; 2 to 12 makes mirrored radial sectors.
warping
Name
Type
Default
Range
Doc
warp_scale
float
1
0.15 … 4
Spatial scale of the first displacement field.
second_scale
float
1
0.15 … 4
Spatial scale of the second displacement field.
second_gain
float
1
0 … 2
Strength of the final displacement relative to warp.
warp_angle
float
0
-180 … 180
Rotate displacement vectors to turn folds and streams.
cross_mix
float
0
0 … 1
Cross-couple the two components of the displacement.
structure
Name
Type
Default
Range
Doc
terraces
float
0
0 … 1
Blend the final density into stepped terraces.
terrace_count
float
6
2 … 24
Number of density terraces.
marbling
float
0
0 … 1
Blend the density into flowing marble bands.
band_frequency
float
12
1 … 40
Number of marble oscillations across the density range.
Source
The current source of this node, as it stands in the goofi repository at node-bundles/inception/TimeWarpFbm.wgsl.
node-bundles/inception/TimeWarpFbm.wgsl
/*goofi{"doc":"Nested fBm pigments and layered refractive pools.\nChoose fBm or refractive pools. Nine palettes use the internal warp fields for secondary pigments and iridescent color. Pool brush controls create a moving ripple; optics controls depth, refraction, dispersion, absorption and lighting. Refraction is a layered screen-space approximation.\nFractal selects noise basis, octave spacing and folds. Geometry stretches, twists or mirrors the domain. Warping controls the two displacement scales and directions. Structure adds terraces and marble bands. These shape controls also feed the refractive pool.","tags":["image","generator"],"params":[{"group":"interaction","name":"mode","kind":"str","default":"fbm","options":["fbm","refractive pools"]},{"group":"brush","name":"brush_x","kind":"float","default":0.5,"min":0,"max":1,"doc":"Horizontal brush position, normalized."},{"group":"brush","name":"brush_y","kind":"float","default":0.5,"min":0,"max":1,"doc":"Vertical brush position, normalized."},{"group":"brush","name":"inject","kind":"float","default":0.65,"min":0,"max":1,"doc":"Strength of the local ripple in refractive pools."},{"group":"brush","name":"erase","kind":"float","default":0,"min":0,"max":1,"doc":"Suppresses the local ripple; 1 fully suppresses it."},{"group":"brush","name":"auto_orbit","kind":"float","default":0.6,"min":0,"max":1,"doc":"Amount of automatic ripple motion; zero uses the manual brush position."},{"group":"optics","name":"depth","kind":"float","default":1.2,"min":0,"max":4,"doc":"Optical thickness of the pigment layers."},{"group":"optics","name":"refraction","kind":"float","default":0.8,"min":0,"max":2,"doc":"Screen-space bending of submerged pigment layers."},{"group":"optics","name":"dispersion","kind":"float","default":0.3,"min":0,"max":1,"doc":"Red/blue separation of refracted samples."},{"group":"optics","name":"absorption","kind":"float","default":0.65,"min":0,"max":2,"doc":"Depth-dependent colored attenuation."},{"group":"optics","name":"relief","kind":"float","default":0.65,"min":0,"max":2,"doc":"Surface-normal strength."},{"group":"optics","name":"light_x","kind":"float","default":-0.45,"min":-1,"max":1,"doc":"Horizontal light direction."},{"group":"optics","name":"light_y","kind":"float","default":0.55,"min":-1,"max":1,"doc":"Vertical light direction."},{"group":"fractal","name":"scale","kind":"float","default":3.2,"min":0.5,"max":10},{"group":"fractal","name":"octaves","kind":"int","default":6,"min":1,"max":8},{"group":"fractal","name":"roughness","kind":"float","default":0.5,"min":0.15,"max":0.8},{"group":"fractal","name":"warp","kind":"float","default":4,"min":0,"max":8},{"group":"fractal","name":"stages","kind":"int","default":2,"min":0,"max":2},{"group":"fractal","name":"seed","kind":"float","default":0,"min":0,"max":100},{"group":"motion","name":"speed","kind":"float","default":0.22,"min":-1,"max":1},{"group":"motion","name":"time_warp","kind":"float","default":0.65,"min":0,"max":2},{"group":"color","name":"palette","kind":"str","default":"mineral","options":["mineral","ember","ink","monochrome","opalescent","oxidized copper","bioluminescent","petroleum","orchid"]},{"group":"color","name":"veins","kind":"float","default":0.35,"min":0,"max":1},{"group":"color","name":"contrast","kind":"float","default":1.25,"min":0.5,"max":2.5},{"group":"color","name":"exposure","kind":"float","default":1.1,"min":0.25,"max":2.5},{"group":"color","name":"complexity","kind":"float","default":0.7,"min":0,"max":1},{"group":"color","name":"palette_shift","kind":"float","default":0,"min":-1,"max":1},{"group":"color","name":"color_spread","kind":"float","default":1.35,"min":0.25,"max":3},{"group":"color","name":"iridescence","kind":"float","default":0.25,"min":0,"max":1},{"group":"color","name":"saturation","kind":"float","default":1.05,"min":0,"max":2},{"group":"fractal","name":"noise_basis","kind":"str","default":"value","options":["value","gradient","cells"],"doc":"Smooth value noise, directional gradient noise, or cellular distances."},{"group":"fractal","name":"fold","kind":"str","default":"plain","options":["plain","ridged","billow"],"doc":"Fold each octave into crests or rounded cloud lobes."},{"group":"fractal","name":"fold_mix","kind":"float","default":1,"min":0,"max":1,"doc":"Strength of ridged or billow folding; plain ignores this."},{"group":"fractal","name":"lacunarity","kind":"float","default":2.03,"min":1.1,"max":3.5,"doc":"Frequency multiplier between octaves."},{"group":"fractal","name":"octave_angle","kind":"float","default":36.869898,"min":-180,"max":180,"doc":"Rotation between noise octaves in degrees."},{"group":"geometry","name":"rotation","kind":"float","default":0,"min":-180,"max":180,"doc":"Rotate the entire domain in degrees."},{"group":"geometry","name":"stretch","kind":"float","default":0,"min":-3,"max":3,"doc":"Directional stretch in powers of two; zero is isotropic."},{"group":"geometry","name":"twist","kind":"float","default":0,"min":-4,"max":4,"doc":"Radius-dependent rotation for spirals and whorls."},{"group":"geometry","name":"pan_x","kind":"float","default":0,"min":-10,"max":10,"doc":"Horizontal travel through the noise domain."},{"group":"geometry","name":"pan_y","kind":"float","default":0,"min":-10,"max":10,"doc":"Vertical travel through the noise domain."},{"group":"geometry","name":"symmetry","kind":"int","default":0,"min":0,"max":12,"doc":"Zero disables folding; 2 to 12 makes mirrored radial sectors."},{"group":"warping","name":"warp_scale","kind":"float","default":1,"min":0.15,"max":4,"doc":"Spatial scale of the first displacement field."},{"group":"warping","name":"second_scale","kind":"float","default":1,"min":0.15,"max":4,"doc":"Spatial scale of the second displacement field."},{"group":"warping","name":"second_gain","kind":"float","default":1,"min":0,"max":2,"doc":"Strength of the final displacement relative to warp."},{"group":"warping","name":"warp_angle","kind":"float","default":0,"min":-180,"max":180,"doc":"Rotate displacement vectors to turn folds and streams."},{"group":"warping","name":"cross_mix","kind":"float","default":0,"min":0,"max":1,"doc":"Cross-couple the two components of the displacement."},{"group":"motion","name":"evolution","kind":"float","default":1,"min":0,"max":3,"doc":"Rate of shape evolution within the warp layers."},{"group":"motion","name":"drift_x","kind":"float","default":0,"min":-1,"max":1,"doc":"Horizontal domain drift, scaled by speed."},{"group":"motion","name":"drift_y","kind":"float","default":0,"min":-1,"max":1,"doc":"Vertical domain drift, scaled by speed."},{"group":"structure","name":"terraces","kind":"float","default":0,"min":0,"max":1,"doc":"Blend the final density into stepped terraces."},{"group":"structure","name":"terrace_count","kind":"float","default":6,"min":2,"max":24,"doc":"Number of density terraces."},{"group":"structure","name":"marbling","kind":"float","default":0,"min":0,"max":1,"doc":"Blend the density into flowing marble bands."},{"group":"structure","name":"band_frequency","kind":"float","default":12,"min":1,"max":40,"doc":"Number of marble oscillations across the density range."}],"state":["pigments"]}*/// Integer hashing avoids sine-hash seams on GPU lattice boundaries.fnhash(cell:vec2i)->f32{letc=vec2u(cell+65536);varh=c.x*1597334673u^c.y*3812015801u^bitcast<u32>(p.seed);h=(h^(h>>16u))*2246822519u;h=(h^(h>>13u))*3266489917u;returnf32(h^(h>>16u))/4294967296.0;}fnnoise(v:vec2f)->f32{letcell=vec2i(floor(v));letf=fract(v);letw=f*f*f*(f*(f*6.0-15.0)+10.0);returnmix(mix(hash(cell),hash(cell+vec2i(1,0)),w.x),mix(hash(cell+vec2i(0,1)),hash(cell+vec2i(1,1)),w.x),w.y);}fnturn(v:vec2f,degrees:f32)->vec2f{letang=degrees*0.01745329252;letc=cos(ang);letsn=sin(ang);returnvec2f(c*v.x-sn*v.y,sn*v.x+c*v.y);}fngrad_dot(cell:vec2i,d:vec2f)->f32{letangle=hash(cell)*6.2831853;returndot(vec2f(cos(angle),sin(angle)),d);}fnbasis(v:vec2f)->f32{ifp.noise_basis==0u{returnnoise(v);}letcell=vec2i(floor(v));letf=fract(v);ifp.noise_basis==1u{letw=f*f*f*(f*(f*6.0-15.0)+10.0);letn=mix(mix(grad_dot(cell,f),grad_dot(cell+vec2i(1,0),f-vec2f(1,0)),w.x),mix(grad_dot(cell+vec2i(0,1),f-vec2f(0,1)),grad_dot(cell+vec2i(1,1),f-vec2f(1,1)),w.x),w.y);returnclamp(0.5+n*0.7,0.0,1.0);}varnearest=4.0;for(vary=-1;y<=1;y++){for(varx=-1;x<=1;x++){leto=vec2i(x,y);letpoint=vec2f(o)+vec2f(hash(cell+o),hash(cell+o+vec2i(127,311)));nearest=min(nearest,length(point-f));}}returnclamp(nearest,0.0,1.0);}fnfbm(v:vec2f)->f32{varat=v;varsum=0.0;varamp=1.0;varnorm=0.0;for(vari=0;i<clamp(p.octaves,1,8);i++){letn=basis(at);varfolded=n;ifp.fold==1u{folded=1.0-abs(2.0*n-1.0);}ifp.fold==2u{folded=abs(2.0*n-1.0);}sum+=amp*mix(n,folded,clamp(p.fold_mix,0.0,1.0));norm+=amp;// Rotate each octave so lattice axes do not stack.at=turn(at,p.octave_angle)*clamp(p.lacunarity,1.01,4.0)+vec2f(13.1,7.7);amp*=clamp(p.roughness,0.0,0.85);}returnsum/max(norm,0.001);}// Seven pigment stops, smoothly interpolated within each interval.fnpigment(x:f32)->vec3f{varc:array<vec3f,7>;switchp.palette{default:{c=array<vec3f,7>(vec3f(0.031373,0.082353,0.113725),vec3f(0.070588,0.247059,0.286275),vec3f(0.086275,0.521569,0.466667),vec3f(0.443137,0.607843,0.407843),vec3f(0.729412,0.458824,0.172549),vec3f(0.854902,0.752941,0.501961),vec3f(0.941176,0.960784,0.823529));}case1u:{c=array<vec3f,7>(vec3f(0.031373,0.011765,0.074510),vec3f(0.160784,0.043137,0.250980),vec3f(0.450980,0.047059,0.188235),vec3f(0.729412,0.141176,0.039216),vec3f(0.976471,0.407843,0.070588),vec3f(1.000000,0.768627,0.341176),vec3f(1.000000,0.941176,0.760784));}case2u:{c=array<vec3f,7>(vec3f(0.011765,0.027451,0.086275),vec3f(0.070588,0.141176,0.333333),vec3f(0.207843,0.235294,0.556863),vec3f(0.407843,0.239216,0.549020),vec3f(0.694118,0.427451,0.600000),vec3f(0.517647,0.745098,0.811765),vec3f(0.921569,0.960784,1.000000));}case3u:{c=array<vec3f,7>(vec3f(0.000000,0.000000,0.000000),vec3f(0.160784,0.160784,0.160784),vec3f(0.333333,0.333333,0.333333),vec3f(0.501961,0.501961,0.501961),vec3f(0.666667,0.666667,0.666667),vec3f(0.866667,0.866667,0.866667),vec3f(1.000000,1.000000,1.000000));}case4u:{c=array<vec3f,7>(vec3f(0.031373,0.043137,0.141176),vec3f(0.156863,0.278431,0.450980),vec3f(0.309804,0.666667,0.662745),vec3f(0.611765,0.474510,0.760784),vec3f(0.917647,0.607843,0.662745),vec3f(0.929412,0.811765,0.509804),vec3f(0.949020,1.000000,0.909804));}case5u:{c=array<vec3f,7>(vec3f(0.011765,0.062745,0.043137),vec3f(0.027451,0.294118,0.243137),vec3f(0.082353,0.572549,0.482353),vec3f(0.415686,0.647059,0.474510),vec3f(0.521569,0.250980,0.105882),vec3f(0.843137,0.517647,0.203922),vec3f(0.960784,0.866667,0.631373));}case6u:{c=array<vec3f,7>(vec3f(0.003922,0.011765,0.031373),vec3f(0.011765,0.058824,0.168627),vec3f(0.094118,0.133333,0.415686),vec3f(0.043137,0.443137,0.545098),vec3f(0.070588,0.823529,0.631373),vec3f(0.658824,0.925490,0.298039),vec3f(0.925490,1.000000,0.792157));}case7u:{c=array<vec3f,7>(vec3f(0.007843,0.007843,0.027451),vec3f(0.094118,0.035294,0.223529),vec3f(0.321569,0.125490,0.513725),vec3f(0.054902,0.509804,0.564706),vec3f(0.541176,0.686275,0.188235),vec3f(0.858824,0.384314,0.152941),vec3f(0.886275,0.694118,0.847059));}case8u:{c=array<vec3f,7>(vec3f(0.035294,0.007843,0.101961),vec3f(0.203922,0.047059,0.325490),vec3f(0.513725,0.078431,0.498039),vec3f(0.807843,0.223529,0.549020),vec3f(0.972549,0.537255,0.568627),vec3f(0.686275,0.713725,0.882353),vec3f(0.972549,0.905882,0.780392));}}letx6=clamp(x,0.0,1.0)*6.0;leti=min(u32(floor(x6)),5u);returnmix(c[i],c[i+1u],smoothstep(0.0,1.0,x6-f32(i)));}fnbase_pigments(uv:vec2f)->vec4f{letaspect=vec2f(resolution.x/max(resolution.y,1.0),1.0);vardomain=(uv-0.5)*aspect*max(p.scale,0.01);domain=turn(domain,p.rotation);letelongation=exp2(clamp(p.stretch,-4.0,4.0));domain*=vec2f(elongation,1.0/elongation);letradius=length(domain);ifp.symmetry>=2{letsector=6.2831853/f32(clamp(p.symmetry,2,12));letangle=atan2(domain.y,domain.x);letfolded=abs((angle+sector*0.5)-floor((angle+sector*0.5)/sector)*sector-sector*0.5);domain=radius*vec2f(cos(folded),sin(folded));}domain=turn(domain,p.twist*radius*35.0);letat=domain+vec2f(2.3,1.7)+vec2f(p.pan_x,p.pan_y)+time*p.speed*vec2f(p.drift_x,p.drift_y);lett=time*p.speed;// Bounded spatial phase distortion: time evolves smoothly without an// ever-growing spatial gradient. At speed zero this whole field is static.letclock_field=noise(at*0.55+vec2f(0.07*t,-0.04*t));lettau=(t+p.time_warp*2.0*sin(t*0.37+6.2831853*clock_field))*p.evolution;letfirst_at=at*max(p.warp_scale,0.01);letq=vec2f(fbm(first_at+vec2f(0.12*tau,0.07*tau)),fbm(first_at+vec2f(5.2,1.3)+vec2f(-0.09*tau,0.11*tau)));letqdir=turn(mix(q,q.yx,clamp(p.cross_mix,0.0,1.0)),p.warp_angle);letsecond_at=at*max(p.second_scale,0.01)+p.warp*qdir;letr=vec2f(fbm(second_at+vec2f(1.7,9.2)+vec2f(0.15*tau,-0.08*tau)),fbm(second_at+vec2f(8.3,2.8)+vec2f(-0.11*tau,0.13*tau)));letrdir=turn(mix(r,r.yx,clamp(p.cross_mix,0.0,1.0)),p.warp_angle);varf=fbm(at+p.warp*p.second_gain*rdir);ifp.stages==0{f=fbm(at+vec2f(0.1*tau,0.0));}ifp.stages==1{f=fbm(at+p.warp*qdir);}letcount=max(p.terrace_count,2.0);lettier=f*count;letterraced=(floor(tier)+smoothstep(0.35,0.65,fract(tier)))/count;f=mix(f,terraced,clamp(p.terraces,0.0,1.0));letbands=0.5+0.5*sin(f*max(p.band_frequency,1.0)*6.2831853);f=mix(f,bands,clamp(p.marbling,0.0,1.0));letdensity=clamp((f-0.5)*p.contrast*2.1+0.5,0.0,1.0);// q and r reveal secondary pigments and interfaces independently of density.letphase=p.palette_shift;letspread=max(p.color_spread,0.01);letbase=(density-0.5)*spread+0.5+phase;letinclusion=smoothstep(0.10,0.42,length(q-0.5));letinterface_mask=smoothstep(0.05,0.35,abs(r.x-r.y));letsecondary=pigment(0.5+(r.y-q.x)*spread*1.8+phase);varrgb=pigment(base);rgb=mix(rgb,secondary,inclusion*p.complexity*0.70);// Artistic spectral tint, not a physical refraction model.letspectral=0.5+0.5*cos(vec3f(0.0,2.1,4.2)+(r.x*2.2-q.y*1.4+f*1.7+phase)*6.2831853);rgb=mix(rgb,rgb*(0.5+spectral)*1.2,p.iridescence*interface_mask);letfilament=pow(clamp(1.0-abs(sin(f*42.0+r.x*7.0)),0.0,1.0),9.0);letvein_color=pigment(0.78+(q.y-r.x)*0.6+phase);rgb*=0.72+0.50*density;rgb+=p.veins*filament*vein_color*0.55;letluminance=dot(rgb,vec3f(0.2126,0.7152,0.0722));rgb=max(mix(vec3f(luminance),rgb,p.saturation),vec3f(0.0));ifp.palette==3u{rgb=vec3f(density);}rgb=clamp(rgb*p.exposure,vec3f(0.0),vec3f(1.0));returnvec4f(rgb,f);}// A movable ripple disturbs the refractive pool surface.fnbrush_center()->vec2f{lett=time*p.speed;returnclamp(vec2f(p.brush_x,p.brush_y)+p.auto_orbit*vec2f(0.23*sin(t*0.41),0.19*cos(t*0.33)),vec2f(0.0),vec2f(1.0));}fnsample_pigment(uv:vec2f)->vec4f{// Mirror edges to avoid stretched strips at large refractive displacements.letmirror=1.0-abs(fract(uv*0.5)*2.0-1.0);returntextureSampleLevel(pigments,samp,mirror,0.0);}fnnext_pigments(uv:vec2f)->vec4f{returnbase_pigments(uv);}fnpool_height(uv:vec2f)->f32{letaspect=vec2f(resolution.x/max(resolution.y,1.0),1.0);letd=length((uv-brush_center())*aspect);letripple=sin(d*95.0-time*p.speed*3.0)*exp(-d*8.0);returnsample_pigment(uv).a+0.08*p.inject*(1.0-p.erase)*ripple;}fnspectral_sample(uv:vec2f,offset:vec2f)->vec3f{letseparation=clamp(p.dispersion,0.0,1.0)*0.16;returnvec3f(sample_pigment(uv+offset*(1.0+separation)).r,sample_pigment(uv+offset).g,sample_pigment(uv+offset*(1.0-separation)).b);}fnshade(uv:vec2f)->vec4f{letbase=sample_pigment(uv);ifp.mode==0u{returnvec4f(base.rgb,1.0);}// Layered screen-space refraction; not ray-traced caustics.letpx=2.0/max(resolution,vec2f(1.0));leth=pool_height(uv);letgrad=vec2f(pool_height(uv+vec2f(px.x,0.0))-pool_height(uv-vec2f(px.x,0.0)),pool_height(uv+vec2f(0.0,px.y))-pool_height(uv-vec2f(0.0,px.y)))/px;letnormal=normalize(vec3f(-grad*0.10*p.relief,1.0));letdepth=max(p.depth,0.0);letoffset=normal.xy*0.055*p.refraction*depth;varrgb=vec3f(0.0);varweight=0.0;for(varlayer=0;layer<3;layer++){letz=(f32(layer)+1.0)/3.0;letw=1.0-z*0.35;letsubmerged=spectral_sample(uv,offset*z);lettransmission=exp(-vec3f(0.65,0.27,0.12)*p.absorption*depth*z*(0.35+h));rgb+=submerged*transmission*w;weight+=w;}rgb/=weight;letlight=normalize(vec3f(p.light_x,p.light_y,0.85));letdiffuse=0.65+0.35*max(dot(normal,light),0.0);letspec=pow(max(dot(normal,normalize(light+vec3f(0.0,0.0,1.0))),0.0),48.0);letfresnel=pow(1.0-max(normal.z,0.0),3.0);rgb=rgb*diffuse+vec3f(1.0,0.91,0.76)*spec*0.32+vec3f(0.25,0.40,0.60)*fresnel*0.25;returnvec4f(clamp(rgb,vec3f(0.0),vec3f(1.0)),1.0);}