// Overview
Alchemist's Corner is systems, not just models. The heart of the pack is the liquid shader: one shared material becomes any number of glowing potions, and the surface behaves like a surface - it stays horizontal when the bottle tips, and rides along when someone picks it up.
The key decision: the fill plane is rebuilt from the object matrix every frame rather than pushed by a script. A bottle can be lifted, parented to a character's hand, thrown or animated - the potion goes with it at zero per-frame cost on the C# side.
// Video
// How it works
The fill level (0..1) maps into the mesh's own object-space Y range, and the shader rebuilds the cutting plane from the object matrix every frame. The Fill Space switch decides how that plane is oriented: World keeps the surface horizontal no matter how the bottle tilts, Local welds it to the mesh - for corked vials and physics props that tumble.
The liquid mesh renders double sided, and its back faces are only ever visible through the hole the plane cuts. The shader shades them as a flat top surface: the view ray is walked back up to the plane, and the result gets the plane's normal, the surface colour and foam. That's what stops the „I look into the bottle and see straight through it" effect.
A transform's position is quantised to frames, so differentiating it twice gives a spike train rather than an acceleration - dragging an object produces frames with no movement next to frames with a big jump. Clamping that makes every motion saturate the same way, and a slow glide comes out identical to a flick.
So the simulator does four things: it filters velocity and acceleration with frame-rate independent time constants, uses soft saturation instead of clamping, derives tilt from acceleration and agitation mostly from jerk (how suddenly the motion changes), and integrates the spring semi-implicitly with sub-steps so stiff settings don't explode when the frame rate drops.
The result is measurable - same 60 fps rig, distance and duration varied:
| Motion | Peak tilt | Peak agitation | Agitation 3 s later |
|---|---|---|---|
| slow glide (0.5 m over 1.5 s) | 0.04 | 0.09 | 0.03 |
| medium (0.5 m over 0.5 s) | 0.18 | 0.27 | 0.10 |
| quick dart (0.3 m over 0.12 s) | 0.47 | 0.64 | 0.24 |
| violent yank (0.5 m over 0.08 s) | 0.71 | 0.89 | 0.33 |
// Four classes of motion produce four distinct readings instead of one saturated maximum.
The foam is a band placed along Y, independent of everything else: you set separately how far below the surface its top edge sits, how tall it is, and how softly both edges fade. A foam texture erodes the band's edge where it is dark - renormalised, so the core keeps full opacity and only the fringe breaks up.
Three crossing sines are added to the cut plane itself, so the line where the liquid meets the glass undulates instead of being ruler-straight, and the top surface tips its normal along the wave and gathers foam on the crests.
Four texture slots (liquid body, foam, bubbles, emission mask) are optional and keyword-gated - an empty slot costs nothing and the shader stays fully procedural. The default mapping is an object-space projection, so it works on any bottle with no UV unwrap and doesn't swim when the object moves.
Glass (Stylized) - two stylized specular bands, outer and inner fresnel rim, a grime mask. No GrabPass, so it's safe for VR and mobile. Cauldron (Bubbling Brew) shares property names with the liquid shader, so the same component recolours both a potion and a brew.
CCAlc Potion Filler writes per-instance values through a MaterialPropertyBlock - every bottle is its own potion despite one shared material. It ships a potion palette, a right-click randomizer, and live tilt / agitation / speed meters in the inspector. CCAlc Vapor Emitter configures a ParticleSystem from a preset (steam, smoke, magic haze), and a starter-prefab generator drops the whole showcase into the scene in one click - without entering Play mode.
// Gallery



// Design decisions
- The shader reads the transform, the script doesn't push the level - which is what makes the liquid work on animated, hand-held and thrown objects with no user code at all. With slosh off, the per-frame cost is zero.
- Soft saturation instead of clamping - without it the whole range of motion collapses to one value and the effect stops carrying information about what happened to the bottle.
- A spring integrated with sub-steps - stiff settings can blow up when the frame rate drops; semi-implicit integration with sub-steps keeps the simulation stable.
- One material, many potions - per-instance values through a
MaterialPropertyBlock, so no collection of material copies grows in the customer's project. - The trap is caught and handled in the UI - a liquid mesh marked Batching Static loses its transform reference and the fill lands at the wrong height. The inspector detects it, warns, and fixes it in one click instead of leaving the user with a puzzle.