Fibre is a WebGL application for visualizing and coding 3d vector fields and dynamical systems. A number of presets with well-known or interesting dynamical systems are provided as below (click to launch). New vector fields can be authored in the code editor, and shared via an HTML link with the embedded code.
In three (or more) dimensions, a non-linear continuous dynamical system may exhibit chaos, which is characterised by sensitive dependence of the solution trajectories to the initial conditions, and evolution governed by a so-called "strange attractor". Probably the most famous example is the Lorenz system, initially discovered as a simplified model of atmospheric convection:
whose solution curves (x(t), y(t), z(t)), starting from any initial point (x(0), y(0), z(0)), tend to the Lorenz strange attractor.
In general, a three-dimensional continuous dynamical system is a system of first order ordinary differential equations (ODE) of the form below, where the functions on the right hand side define a vector field giving the velocity vector of the dynamical system at each point in space (and, optionally, time):
In Fibre, the code editor on the left specifies this velocity vector field
via the GLSL function vec3 velocity(vec3 p, float t)
. Additionally, a color at each point in space must be specified via vec3 color(vec3 p, float t)
. The system then traces the solution curves of the ODE (using the Runge-Kutta method), starting from a grid of points covering the "initial conditions box" positioned in the viewport. These curves are rendered as colored tubes. (Note, the tubes are not rendered as geometry, they consist of many individual line segments, which are progressively rendered and blended over a number of iterations in order to converge to the final image).
- left-click mouse to rotate, right-click mouse to pan camera
- hover over the initial conditions box and left-click to drag it around, or hover over the box corners and left-click to resize the box dimensions. The box extents can also be edited directly in the
Integrator
rollout of the UI. - the first (green) ☰ button hides/shows the code editor, in which the velocity vector field and color field are defined as GLSL functions.
- editing the GLSL code immediately updates the shader -- if there is an error, this will be copied to the viewport, and rendering terminated
- click on values in the code editor to pop up a slider which scrubs the value
- click on colors (e.g.
rgb(255, 0, 0)
) in the code editor to pop up a color wheel - The second (blue) ☰ button generates a data URL which can be used to save and share the current vector field. Note that the URL itself contains the full state of the application, including the GLSL code, all the UI settings, and the camera and initial conditions box. So this link can be used to share the exact state of the application with anyone else (e.g. on Twitter).
- AWSD keys to fly
- C key to frame camera on the initial conditions box
- P key to capture a screenshot of the current render in a new browser window
- H key to hide/show the sidebar UI
- F11 key to enter/exit fullscreen mode
The GLSL code defining the vector field must have the following form:
// Mandatory function giving the vector field x, y, z components as a function of 3D spatial position.
// Optionally, these can also be a function of the supplied integration time t (generating a "non-autonomous" system)
vec3 velocity(vec3 p, float t)
{
// example code (Thomas attractor)
vec3 v;
float x = p.x;
float y = p.y;
float z = p.z;
const float b = 0.15; // (value is draggable in the code editor)
v.x = -b*x + sin(y);
v.y = -b*y + sin(z);
v.z = -b*z + sin(x);
return v;
}
// Mandatory function giving the diffuse color components associated with each point in space,
// and/or integration time.
vec3 color(vec3 p, float t)
{
// example code
const vec3 colLo = vec3(254,45,73) / 255.0;
const vec3 colHi = vec3(5,138,255) / 255.0;
float lerp = t/(float(abs(5.0))+t);
return (1.0-lerp)*colLo + lerp*colHi;
}
// Optional function (called if the word "teleport" appears anywhere in the code)
// which can be used to displace the current point in the integration trajectory an arbitrary amount,
// without drawing a line between the previous position and the updated position (thus introducing a broken line).
// The function should determine whether such a "teleportation" event is required,
// given the current integration point p and the previous point pprev,
// and if so displace p in-place and return true (otherwise, return false).
// This can be used to implement, for example, periodic boundary conditions, as shown below.
bool teleport(inout vec3 p, in vec3 pprev)
{
// example code
const float Xmin = 0.0; const float Xmax = 1.0; const float DX = Xmax - Xmin;
const float Ymin = 0.0; const float Ymax = 1.0; const float DY = Ymax - Ymin;
const float Zmin = 0.0; const float Zmax = 1.0; const float DZ = Zmax - Zmin;
const float eps = 1.0e-2 * min(min(DX, DY), DZ);
if (p.x < Xmin + eps) { p.x += DX - eps; return true; }
if (p.x > Xmax - eps) { p.x -= DX + eps; return true; }
if (p.y < Ymin + eps) { p.y += DY - eps; return true; }
if (p.y > Ymax - eps) { p.y -= DY + eps; return true; }
if (p.z < Zmin + eps) { p.z += DZ - eps; return true; }
if (p.z > Zmax - eps) { p.z -= DZ + eps; return true; }
return false;
}
- gridSpace: the spacing between start points within the initial conditions box, relative to the box maximum extents
- tubeWidth: the radius of the rendered solution tubes, relative to the box maximum extents
- integrationTime: the total time to integrate over
- maxTimeSteps: the number of timesteps into which the
integrationTime
is broken into - xmin: (etc.) initial conditions box bounds
- clipToBounds: renders only the portion of the solution curves which lies within the initial conditions box
- integrateForward: if selected, integrate over positive times i.e. the time interval [0,
integrationTime
], otherwise integrate over the time interval [-integrationTime
/2,integrationTime
/2] - enableBounds: if disabled, the initial conditions box is locked (allowing the camera to be moved within the box)
- showBounds: whether to show the initial conditions box bounds
- exposure: controls overall brightness of image
- gamma: controls gamma correction factor of image
- contrast: controls image contrast
- saturation: controls image saturation
- fov: change the camera field-of-view
- hairShader: use a simple hair shading model for the specular component (otherwise uses the Blinn-Phong model)
- specShine: controls size of tube specular highlight
- specColor: the color of the specular highlight (blended with the diffuse tube color defined in the code)
- depthTest: controls whether tubes are opaque or blended
- rayBatch: the number of ray segments to render in one iteration (higher means convergence in less iterations, but slower iterations)
- maxIterations: the max iteration count, beyond which the render terminates. Increase this to improve the final image quality.
- subtractiveColor: treat the colors as absorption coefficients
- bgColor: change color of background (probably use in conjunction with
subtractiveColor
) - light1_color: the color of the first light, used to modulate the diffuse reflectance from the tubes
- light2_color: the color of the second light, used to modulate the diffuse reflectance from the tubes
- light1_dir: the direction of the first light, used to modulate the diffuse reflectance from the tubes
- light2_dir: the direction of the second light, used to modulate the diffuse reflectance from the tubes
- dashes: enable animated dashes showing the direction of flow (must use in conjunction with
depthTest
) - dash_spacing: space between animated dashes, relative to
gridSpace
- dash_size: fractional size of dash, in [0, 1]
- dash_speed: rate of dash motion, in dashes per second
- RECORD: start/stop recording a GIF of the viewport interaction
- RECORD PERIOD: record a GIF of one dash period (for repeating GIFs of the animated dash motion)
- record_realtime: whether to record the real-time interaction to the GIF, or at the maximum frame-rate
- RENDER ANIM: start recording a GIF of a number of automatically rendered animated frames (the number specified via
anim_frames
). Each frame renders for the number of iterations specified viamaxIterations
. The camera can optionally (viaanim_enable_turntable
) be moved in a turntable motion through a specified number of degreesanim_turntable_degrees
during the animation. The uniform float variableanimFraction
is available in the shader, which equals the number of elapsed frames divided by the total number of frames. At the end of the animation, a GIF is rendered (which may take some time) and saved to the downloads folder with a standard name. - CANCEL ANIM: cancel the current animation recording
- anim_frames: how many animation frames to render in the generated GIF
- anim_enable_turntable: whether to move the camera in a turntable sweep motion during the animation
- anim_turntable_degrees: if turntable is enabled, how many degrees to rotate through