Renderers#
Since 3.0.0 every plot carries a renderer switch:
plot = k3d.plot(renderer='advanced')
# or at any moment:
plot.renderer = 'advanced'
simple(default)The classic rasteriser: a fixed rig of four lights that follows the camera, and the cheapest of the three. Materials are physically based here too, so a surface answers to
roughnessandmetalnesswithout any environment map - whatadvancedadds is where the light comes from, not how the material responds to it.advancedImage-based lighting plus ambient occlusion. All light comes from an environment map, materials are physically based, and a GTAO pass grounds the geometry with contact shadows. Switching back and forth is a single assignment and never rebuilds the scene. Reckon on half again the cost of
simple: measured over the visual suite it draws a frame in 1.56x the time, more where a scene is limited by the pixels it fills and less where it is limited by geometry.cinematic(experimental)Progressive path tracing: light bounces between surfaces, so shadows, reflections and colour bleeding come out of the simulation rather than from a screen-space approximation. The image refines sample by sample and restarts whenever the camera or the scene changes. Requires WebGL2 with renderable float textures - see Cinematic rendering below.
The golden rule: the renderer changes the light, never what you asked to
draw. Unlit primitives - points with dot/flat shaders,
line(simple), line(thick), labels, texts, wireframes - are a deliberate
choice of no shading and look identical in simple and advanced.
cinematic keeps the shape of everything you asked for but not always the
implementation, and it has no concept of an unlit surface; the differences are
listed in Cinematic rendering.
The same roughness/metalness sweep under both renderers - dielectrics in the
front row, metals in the back, roughness growing to the right. First
simple:
and the identical scene under advanced (switch the environment in the
panel to see the light change):
Materials#
All lit objects use physically based materials with two knobs:
roughness0.0 = polished mirror-like highlight, 1.0 = fully matte. Default 0.4.
metalness0.0 = dielectric, 1.0 = metal (the surface reflects only its environment, tinted by its own colour). Default 0.0.
shininess was removed in 3.0.0. The equivalent conversion is
roughness = sqrt(2 / (shininess + 2)); passing shininess raises a loud
TraitError with that formula, and legacy .k3d snapshots are converted
automatically on load.
Both parameters live in the 0-1 range and are validated at assignment.
Volumetric objects (volume, mip) carry the same two knobs for the
specular highlight of their isodensity surface (default roughness=0.25) -
lower roughness makes noisy gradients sparkle like wet tissue, which may even
be desired. metalness tints and strengthens the highlight with the
transfer-function colour; it never darkens the body, because a volume has no
environment reflection to replace the lost diffuse light with.
Environments#
In advanced the environment map is the only light source. Every map is
energy-normalised, so the environment carries the shape of the light while
plot.lighting stays the exposure knob.
plot.environment = 'studio' # procedural preset
plot.environment = 'burnt_warehouse' # photographic catalog (Poly Haven, CC0)
plot.environment = my_hdr_array # any (H, W, 3) float32 equirect
plot.environment_rotation = np.pi / 3 # spin it around the scene's up axis
Procedural presets (neutral - the default, studio, outdoor) travel
as plain names and are generated deterministically on the CPU. The photographic
catalog ships with the package:
import k3d.environments
k3d.environments.available()
# ['autoshop_01', 'brown_photostudio_02', 'burnt_warehouse',
# 'moonless_golf', 'venice_sunset']
A photographic map with a filmic curve changes the mood entirely - the same
spheres again, under venice_sunset with AgX tone mapping:
Note
The photographic maps live in the Python package, so a kernel-less page
cannot resolve their names. An exported HTML snapshot therefore offers only
what it can regenerate: the procedural presets plus the map that was baked
into it at export time. A page may widen that list by including the
sideload script generated by k3d.environments.save_js(path) next to
standalone.js - this documentation does exactly that, which is why the
dropdown above carries the full catalog.
Since 3.0.0 a volume composes correctly with meshes that intersect it
when depth peeling is enabled (plot.depth_peels >= 3 - fewer layers make
the segmentation too coarse to be predictable). The ray march is split into
segments bounded by the peel layers, so geometry inside the volume occludes
and is occluded sample-accurately, in both renderers:
Volumetric data (volume, mip) and the points 3d impostors read the
same environment: diffuse light from the map’s spherical harmonics plus one
dominant directional light distilled from it, so a directional HDRI models
volumes consistently with every mesh in the scene.
Ambient occlusion#
advanced always includes a GTAO pass with spatial denoising. The result is
deterministic and screenshots are seam-free at any rendering_steps. Real
surfaces occlude; volumes and MIPs contribute the shell where their accumulated
opacity crosses one half, so dense structures cast and receive contact shadows
too. Two knobs (shown in the panel only when the advanced renderer is active):
plot.ao_radius = 0.02 # occlusion radius, fraction of the scene diagonal
plot.ao_strength = 3.0 # shadow-deepening exponent; 0 disables
The default radius (0.07) suits typical scenes; dense point clouds and closed interiors (the inside of a skull) usually want a smaller one.
Tone mapping#
plot.tone_mapping = 'agx' # or 'aces'; 'none' is the default
Filmic curves compress bright HDR highlights - useful with high-contrast photographic environments. All three renderers share the same curve, applied as the last step before the frame reaches the screen.
Cinematic: path tracing#
The third renderer traces the light instead of approximating it. Soft shadows, glossy reflections and colour bleeding come out of the simulation, the image refines sample by sample, and any change starts it over. The same material sweep as above, path traced (the counter in the corner tells you when it has settled):
It is experimental, it needs WebGL2 with renderable float textures, and it keeps the shape of everything you asked for without always keeping the implementation. Its parameters, the environments that light it, and the list of what changes object by object live on their own page: Cinematic rendering.