Repository navigation
Ollin 0.15.0
Added
-
Segments, arcs, and ellipses as values.
LineSegment,Arc, andEllipsejoinCircleandRectangleas values a sketch holds: each knows its length or perimeter (the ellipse's exact, through the arithmetic-geometric mean, where the usual formula is a few parts in ten thousand off on a flat one), its area, its bounds, the point on it nearest to any other, and its outline, and each is drawn by the call that always drew it (drawLine(_:),drawArc(_:mode:),drawEllipse(_:), which turns a tilted ellipse for you). All of them, withCircle,Rectangle,Triangle, andContour, share thePlaneCurveprotocol, so any two can be asked where they cross:crossings(with:)returns the meeting points sorted along the shape you asked, from the textbook closed forms where one exists (a straight piece against a circle or an ellipse, two circles, arcs keeping only their sweep) and, for an ellipse against anything round, from the two outlines polished onto the true curves. A mixed list of them can be asked pair by pair.Circlegainsarea,perimeter,point(at:),nearestPoint(to:), anddistance(to:);Rectanglegainsarea,perimeter, andedges;Trianglegainsperimeterandedges. New example:Examples/Shapes/Crossings. Where curves cross -
A turtle you drive from code.
Turtleis a pen that stands on the canvas facing some way:forward,back,left, andright(in degrees) walk and turn it,penUpandpenDownbreak the drawing into runs,pushStateandpopStateremember where it stood and take it back there without drawing the way, which is how a branch is drawn, andmove(to:),home(), and a settableheadingplace it outright. Every run comes back as an ordinary openContour, or a closed one afterclose(), so the walk strokes, fills, and plots like any outline. The L-system turtle reads a string; this one takes calls. New example:Examples/Patterns/TurtleWalk. Turtle -
Anwar Jalal Shemza joins the recreations.
Examples/Recreations/AnwarJalalShemzais new, after the painter from Lahore who settled in Stafford and built his pictures, he said, from two simple forms, the square and the circle, taken from carpets, Urdu calligraphy, and the architecture of Mughal Lahore.Meemis the letter he painted again and again from 1964 to 1968, reduced to a disc for its head and a line for its tail: the canvas is ruled into lanes, a cluster is a few heads laid one touching the next, and every head drops a tail on the cluster's outer side, the tails one gap apart, each leaving its head as a quarter turn around it and falling to the bottom edge, the top head's turn hooking over its shoulder. The picture can be mirrored, and the look is either the oils' thin red lines over blue and green bands or the stained-glass window's wide green stems with white outlines. The letters are written right to left as the pen would write them.Rootsis his last series, 1977 to 1985: pieces of dyed cloth with frayed edges on a board, a flower on the top piece built by joining a half-disc and three discs, and on the pieces below its roots as a word in Urdu, set from a real face's own outlines in naskh, nastaliq, or Kufi, at a size taller than the cloth so it is cut where the cloth ends. Both export SVG that gives the rule back, and the words come back as the face's glyphs point for point. -
Lofting.
Mesh.loft(_:along:at:smoothed:scale:twist:capped:)anddrawLoftbuild one surface through several flat shapes placed along a 3D path, so a bottle runs from a square foot through a round belly to a narrow neck, a horn rounds from a star into a circle as it turns, and a boat hull is lofted as a sheet through open ribs. Each section lands exactly where it was asked for, corners and all, between the path's own points or on them; the outlines are matched round their lengths with each seam turned to the nearest start on the section before, so the skin does not twist on its own; a corner sharp on any section keeps its edge down the loft and fades where the other sections round it off; three or more sections blend as a smooth curve, or straight from rib to rib withsmoothed: false; and a loft of one shape is that shape's sweep, byte for byte. A sweep's and a loft's caps now meet the walls point for point (the cap's corners are the shape's own points rather than the triangulator's single-precision copies, and a run of points along one straight side no longer fills with slivers of no area), and both say whether they are closed, which the canvas depth of field reads. Lofting -
A shader as paint.
fill(shader)andstroke(shader)take aShaderanywhere they take a color, and itsshade(uv, info)runs at every pixel the shape covers, on every shape and every path: the analytic shapes, polygons andShapes, strokes, curves, and outline text.uvruns across the shape's own bounding box and turns with it under a transform;info.positionis the pixel in canvas points,info.distancethe signed distance to the shape's edge (negative inside, zero on a stroke's centerline), andinfo.alongthe fraction along a stroke. Coverage is applied after the shader returns, so the edge stays as sharp as a flat fill's, and a shader that returns one color draws what that color draws, byte for byte. A mesh underfill(shader)takes the shader as its surface texture, read at its uvs, so it reaches reflections, the tracer, and an export. Shapes painted by one shader draw as one run. A vector export writes a shader-painted shape in middle gray; a web page export stops and names the call.Paint.shader(_:)carries one as a value. New example:Examples/Shaders/ShaderPaint. A shader as paint -
Hidden-line solids.
featureEdges(creaseAngle:)draws a mesh the way a draughtsman draws a solid: the faces in the fill as always, and over them only the lines that describe the form, in the stroke atstrokeWeight. Those are the creases where two faces meet past the angle, the boundary where a surface ends, and the silhouette, worked out for every copy from where it is seen. A box gives its twelve edges and no diagonals, a sphere its outline, and near solids hide the edges of far ones. The weight is in canvas points, a hairline on every copy at any distance, or in world units understrokeWeight(_:in: .world). UnderdrawMesh(_:instances:)every copy's edges are one more GPU draw placed by the same matrices, so ten thousand boxes cost what their faces cost plus a few milliseconds of GPU time and no CPU time; a mesh's edges are found once and kept while it is drawn.noFeatureEdges()turns it off. New example:Examples/3D/Geometry/HiddenLines. Hidden-line solids -
Noise in three dimensions that loops.
noise(x, y, z, loop:), withsignedNoise,fbm,signedFbm,ridgedFbm, andturbulencein the same form, reads a volume that changes over a lap and comes back to exactly where it started, so a point cloud's drift or a cloud's density closes a seamless export. It reads a five-axis field, three for the point and two for the circle the lap tours, scaled to spread likenoise(x, y, z)so a sketch keeps its contrast when it moves to the looping form. Noise -
Live reload for a sketch that is a package. The live window compiles a sketch inside a SwiftPM target with every other
.swiftfile of that target, so helpers can live in files of their own and a save to any of them reloads the window, where before a helper moved into a second file stopped the live route withcannot find ... in scope.ollin MySketchruns a package's sketch by its folder, a bareollinruns the package you are in, and--target <name>picks one sketch of several. The compile readsPackage.swiftfor the target's language mode, defines, features, and resources, so a picture in a processedImages/folder loads through.modulein the live window as it does underswift run. A sketch that imports another target of its package, or another package, still needsswift run, and the error says so.OllinRun(the--installationhost) andOllinLiveCodingtake a package's folder too. Single-file sketches -
Hiroshi Kawano joins the recreations.
Examples/Recreations/HiroshiKawanois new, after the philosopher who taught himself to program in 1963 to test a theory of aesthetics, and published some of the first computer-made pictures anywhere in 1964.Designwalks a grid of black, red, blue, yellow, and white with a Markov chain of order 2, each cell chosen from what followed the last two in counts measured cell by cell off his Design 3-1 and Design 3-2; the printer types a code for every cell and the gouache goes on in the order the chain decided. Read in rows, the exported squares give back the counts they were walked with.ArtificialMondrianis the series of 1969: a chain sets where the lines run, a second decides whether each line runs on through a crossing, and the regions the black closes are colored at random, in three looks read from KD 55, KD 27, and KD 52. Every band, notch, and region exports as rectangles on the raster. -
Smoothing that keeps the points.
smoothed(neighbors:)on aContouror aShapeis a moving average: each point moves to the average of itself and that many neighbors on either side, and the outline comes back with one point for each point it had, in the same order. So a traced or resampled ring loses its facets and its jitter and stays in step with anything keyed to its points, a color per point or a partner on another ring, where corner cutting doubles the count on every pass. A bell weighs the window by default, so near neighbors count most and a curve shrinks less;weights: .boxis the plain average. A closed contour wraps around its seam, and an open one keeps its two ends exactly. Smoothing that keeps the points -
Motion blur drawn from subframes.
--subframes Ndraws each exported frame at N moments while the shutter is open and averages them in linear light, the accumulation buffer. It blurs anything a sketch draws, 2D and shaders included, at N times the render cost, and it is the reference the canvas motion blur is now measured against.--shutter Fsets the opening, and left out it is the sketch's ownmotionBlur(shutter:), else 0.5. Every moment is a draw:timesteps through the opening,deltaTimeis the time since the last draw, andframeCountcounts each moment, so motion counted in frames runs N times as fast. A picture the GPU carries from one draw to the next (anoClearpile, a feedback layer, an accumulator, a simulation field) is refused by name. In code it isOllinApp.exportSubframesandOllinApp.exportShutter. See Export. -
Volumes. A
Volumeis a box of density samples, built from a function of position, from a list, or from noise filled across every core (Volume.noise, with a looping form for a cloud that comes home each lap).drawVolume(_:size:medium:)draws it in a 3D scene as smoke, cloud, fire, or ink, and theMediumsays what it is made of:density(how thickly it blocks light, per world unit, likefog),color(the share of light it scatters),glowandglowIntensity(its own light), andanisotropy, with presets.smoke,.cloud,.fire,.ink, and.nebula. Each pixel's ray is marched through the box after the frame's solids are drawn and ends at the first one, so a solid inside a volume is veiled by the medium in front of it whatever order the calls came in. The medium scatters the scene's directional, point, and spot lights and its ambient and environment light, shades itself, and takes the shadows solids throw into it. Measured against the closed forms: transmittance by Beer's law, the glow's integral, and single scattering, each within half-float precision. A web page export namesdrawVolumeand stops. The3D/Effects/Volumesexample sends a column of smoke up past a hanging ball. See Volumes. -
Layer styles. Six filters give a layer an edge of its own, read from its alpha:
.outline(width:color:align:)outside, inside, or centered on the edge;.dropShadow(offset:radius:color:)and.innerShadow(offset:radius:color:), the layer's alpha blurred and moved, laid under it or held inside it;.outerGlow(radius:color:)and.innerGlow(radius:color:), which follow the measured distance from the edge rather than a blur, so a thin line keeps its glow; and.bevel(width:depth:profile:angle:elevation:highlight:shadow:), the band in from the edge raised into a rounded or chiseled slope and lit. They chain like any filter, and the chain's order is the order they stack in. A clear color, or a width or radius of 0, hands back the layer's own bytes. Measured against the shape they were drawn from, an outline is within 0.06 pixel of its width on either side, and a drop shadow is exactly the layer's Gaussian moved. All six cross to a web page, and so does.distanceField, which used to stop the export. TheEffects/LayerStylesexample stacks them on a word under a moving light. See Layer styles. -
Zdeněk Sýkora joins the recreations.
Examples/Recreations/ZdenekSykorais new, after the Czech painter who, with the mathematician Jaroslav Blažek, began placing the elements of his paintings by computer in 1964, and from 1973 painted lines whose every number came from chance.Structureis the rule the two of them published in Leonardo in 1970: twenty elements of half-discs in four groups by how much of them is black, a chart with a few elements placed and plus and minus signs, and a walk left to right and back that takes each cell's group from the cells around it and keeps the elements whose color or shape continues, as one of four rules asks. The machine types its codes on the left sheet, and the canvas is painted one template at a time. Replayed against the chart and table printed with the article, this reading gives back the printed group for 182 of the 196 computed cells, and all but 8 of the printed elements pass the rule.Lineswrites a score: a start in whole centimeters, a width, one to five of thirty paints mixed one to one the way pigments mix, and three to eight tangents, each joined to the next by an arc whose radius follows from their lengths and the angle between them, along the grid's eight directions, the clock's twelve, or every even degree. Every crossing goes over or under as a series of ones and twos decides. Both export SVG that gives the rule back: every element of the canvas and every sign of the chart, and every line's start, tangents, and width, with each settled crossing drawn again inside a clip of the place where the two lines overlap. -
Measuring color.
distance(to:)says how different two colors look, as a straight line in OKLab (the scale the palette check's tolerance already uses), or by the CIE's 2000 color-difference formula withmeasure: .ciede2000, which matches all 34 of the test pairs Sharma, Wu and Dalal published for checking implementations.CIELabbrings in the Lab numbers swatch books and color pickers give, against the D50 white they are measured under.Palette.nearest(to:)returns the swatch that looks most like a color andnearestIndex(to:)its position.Image.histogram(of:bins:in:)counts a picture's pixels by lightness or by one channel, andpercentile(_:)reads its working black and white. TheColor/Shadesexample uses all three. Measuring color -
Shades whose hue drifts.
Palette.shades(of:count:)andRamp.shades(of:)build a ramp of one color the way pixel artists and painters shade: the lights turn toward yellow and the shadows away from it, toward blue or purple, with the chroma eased at both ends. The base color is always one of the swatches, andhueDriftsets how far the hue turns, a negative drift cooling the lights instead. Shades -
The slope of a fitted field.
RadialBasisreports how its field changes:gradient(at:)for a field of numbers, the way uphill with a length that says how steep, andderivative(at:along:)for any field, a warp's stretch included. Both are exact and cost about one read of the value. For a function you wrote yourself,derivative(at:function:)andgradient(at:field:)measure the slope by central differences. TheMotion/ContourWalkersexample walks the contours of both kinds. Slopes
Changed
-
A caption draws over the finished frame.
drawCaptionnow lands in the overlay, so it stays sharp and white over a depth of field, a motion blur, a bloom, or a lens flare, and never joins an accumulating canvas; inside a layer or a batch recording, where there is no overlay, it draws in place as before. drawStatus & drawCaption -
A parameter read costs what a stored property costs. A
@Paramholding a number, an integer, or a switch now reads in under a nanosecond, down from about 40, so a loop may read one per vertex and the advice to copy parameters into locals first applies only to a color, a vector, a palette, or a text value, which still read through a lock. Parameters -
2D torque takes points, like force.
applyTorquenow takes a force in points times an arm in points, so the numbers that push a body also turn it; it took the engine's newton-meters before, so a sketch calling it spins about ten thousand times faster at the default scale and wants its torques divided byunitsPerMetersquared to keep its motion. Rigid bodies -
The band-pass voice filter is unity at its cutoff. Resonance now only narrows the band; the output used to grow with it (five times louder at resonance 0.9), so a band-pass voice at high resonance plays quieter than before. Voice.Filter
-
An export's soft shadows sample 64 points on the light. Under temporal anti-aliasing an export traces one set of points on the light across its sixteen passes, and the
.detailtier every export resolves to now takes 64 of them, four a pass, where the live tier's 16 showed a wide penumbra as sixteen steps;shadowSamples(_:)still passes an exact count through, and a window traces what it did. Soft shadows -
A camera rig's framing follows its arguments.
cameraShowcase,cameraControlandcameraMoveused to readtarget,radius,elevation,azimuthandfieldOfViewon their first call only, so a@Parampassed in tuned nothing in the window while an export framed with it. The arguments are now the shot's home. A changed one glides the shot there over about half a second, under a move or the showcase's auto-orbit, and undercameraControluntil the viewer takes the camera. After that it moves only whereresetCamera()and the idle return go. The same values every frame change nothing, so a sketch passing constants draws exactly what it did. Thefrom:forms take the authored camera's pose the same way. Showcase -
A traced shadow under an export's temporal anti-aliasing costs a sixteenth of what it did. The export draws the scene sixteen times at sub-pixel offsets, and a traced point or area shadow traced its whole ray budget in each of them, the same points on the light every time. Each pass now traces its own share of one set, so the frame traces the budget once: 1,000 spheres under a point light at 1080 by 1920 went from 25 seconds a frame to 2.3, with the same penumbra. A
shadowSamplescount is now how many points on the light the whole frame traces, so under the sixteen passes any count up to 16 costs one ray a pass. Exports of traced shadows under temporal AA change by a fraction of a level along their penumbrae. Soft shadows -
Even spacing on a surface is about three times faster.
surfacePoints(on:count:)with its default.blueNoisethinning now searches only the cells its candidates occupy, where it walked a grid of the whole bounding box, and keeps one queue entry per candidate. 400,000 points on a terrain took 13.4 s in a release build and take 4.7 s, a second of which is drawing the candidates. The debug build thatswift runmakes gains more: there the thinning of those points went from three minutes to 20 seconds. The points are the same for every mesh, count, and seed, so a scatter laid down before looks exactly as it did. Points on a surface -
MarkovChainis in the core. The chain the music helpers use to pick notes now lives inOllinitself, since it picks colors and line lengths as readily as notes, and a drawing sketch no longer needs the audio library for it. Every seed walks the same way it did. A file that imports onlyOllinAudioand uses the chain now needsimport Ollinbeside it. It also takes a table of counts directly:learn(_:after:times:)records that a context was followed by an element a number of times, so a table counted off a picture or written by hand becomes a chain with no sequence behind it. MarkovChain, a choice that remembers -
A color-vision simulation shows a dichromat colors they would see as themselves. At full severity
Color.simulated(_:)andFilter.colorVision(_:)now project each color onto the surface of colors a dichromat can tell apart, the two half-planes of Brettel, Viénot and Mollon (1997), so simulating a simulated color leaves it where it is. The table they used before was not a projection: a blue moved 0.064 on a second pass, a purple 0.072 under tritanopia. A severity below 1 now moves each color that share of the way in a straight line in linear light, so turning the severity up only ever moves a color toward where the dichromat sees it; the old tritan dial passed its own end on the way. Grays and white come back exactly. A result past what the display can show is still clamped channel by channel, which changes what that viewer sees least. Pictures move, the tritan column most, and the safe set's closest pair reads 0.078 where it read 0.076. Breaking:ColorVision.matrixis removed, since every kind now has two transforms rather than one;applied(toLinear:_:_:)still gives the model's result for a linear color. Seeing your colors as somebody else does
Fixed
-
An overlay no longer loses the scene under a sky backdrop. The overlay pass drew the environment's backdrop a second time, full-frame and with no depth, so a
withOverlayblock (or a caption) over a scene lit byenvironment(.sky(...))or an HDRI with its backdrop shown could cover every surface and keep only the sky and the block, where the camera's far plane let the backdrop through; the pass now starts from the finished frame and draws only the block. withOverlay -
The probe grid keeps its planes off a scene's faces. The global-illumination grid is now laid so a scene's bounding faces sit half a cell inside the outer probe planes, where a face on a plane put a ragged dark seam on a room's open end (a row of probes at the walls' front edges, flipping in and out of the visibility test); the seam is gone at the framework, and a room no longer has to run past the camera to avoid it. Global illumination
-
A traced shadow's softness no longer follows the camera. A traced point light's area is now sized by its distance to the scene's center, where it followed the distance to the camera's target, so the penumbra stayed put only while the camera aimed at one spot (192 levels between two aims along one line) and breathed through a camera move. Soft shadows
-
A light's reach holds in the bounce and in the trace. The probe field and the path tracer now read a lamp's
reach:as the raster does, so a lamp lights and bounces nothing past it anywhere, where the field used to fill a room the lamp could not reach and the tracer lit the floor past it. Reach -
A sun brighter than a half float arrives whole. An HDRI is now decoded at single precision and scaled into half range by one factor the bake divides back out, so a sun of 200,000 lights what its pixels say, where the decode used to clamp it at 65,504 and deliver a third of its light. Environment lighting
-
The blobby cross ends at its tips. Its distance formula reported a fraction of the true distance past each tip, so the render drew a faint spike from every tip to the quad's edge; the diamond through the four tips now floors it, and a blobbiness below 0.35 draws to its
radiuswhere the tip used to fall short. drawBlobbyCross -
A traced SVG outline keeps its sharp corners. The curved distance-field shapes trace their outline on a grid, which cut a sharp corner across by up to a cell's diagonal (2.4 px on the cut disk's corners, 1.7 on the horseshoe's arm ends); a cell whose two crossings disagree in normal now bends through the corner where the two sides meet. Vector: SVG
-
Text along a path fills the glyph the plain run fills. The per-glyph route (
drawText(_:along:),perGlyph:) flattened each glyph afresh at render size, so the same letter carried 4.5% more ink along a path than in a plain run; it now takes the cached outline the plain run uses, cleaned of self-overlaps, so the two agree to a level. Per-glyph drawText -
A sound export finds every instrument, on time, at the right level. Four leaks in the soundtrack an export writes, each measured in the survey below, are closed. An instrument held inside a struct, an array, an optional, a dictionary, or an object of the sketch's own is found and advanced like one held as a plain property, so the Spatial example's three chimes are back in its own file (the sketch's values are read through everything it holds, direct properties first, each object once, a list through its first element). A note asked for on a frame starts on that frame's first sample, where every note used to land one frame late (42 ms at 24 fps). A change of
hearingDistancewhile the export runs reaches the file at the frame that made it, where the file used to hold the range the export began with. Andunplace()plays the sound at the level it had before it was placed, in both ears, where it used to sit 6 dB down for the rest of the piece. The finder's rules are on the Synthesis page under Sound in an export. Sound in an export -
A struck body no longer clips, and
hardnessis the color of the strike rather than its loudness. The strike's push is divided by its own area, so a softer strike reaches fewer tones instead of pushing the same tones harder, and a strike is as loud at 48 kHz as at 44.1. Before, every struck preset but.woodsat on the renderer's ceiling for most of its attack (the bell for 56% of its first second) and the clip put tones in the bell that it has no partial at. The presets had been tuned under that clip, so they strike harder now (.belland.bar0.9,.drumand.plate0.85,.glass0.8) and each rings the partials it declares; the defaulthardnessof aModalBody,ModalBody(shape:), andStruckShape.bodymoves from 0.7 to 0.85 so a drawn shape's upper modes are heard an octave up; and the struckVoicepresets (.drum,.bar,.chime,.glass) carry gain 1, which puts them within 3 dB of.pluckat the same velocity. A sketch that balanced a struck instrument against the old clipped level will hear it a little quieter and a good deal cleaner; theSoundInAnExportandStruckShapesexamples raised their struck instruments' gain to keep their balance. A struck body -
Promises the reference no longer makes, after a survey of the sound, the color science, and the solvers. A plucked string's
decayis exact at low notes and shorter at high ones; a bowed string'sdecayis a loss in its loop, not a ring-out, since a bow at pressure 0 rests on the string; a blown tube needs a minimum breath before it speaks (0.6 for the clarinet, 0.4 for the reedy one, 0.7 for the hollow one), and the pressure sweep the page shows now starts there;Delay.timeruns up to 2 s; the limiter flattens a wave that crosses its ceiling, since it has no lookahead; the mixed soundtrack bends anything above 0.7, a single loud instrument included. A.wideor.extendedframe skips the dither on its way to an eight-bit file, the wide PNG is written at eight bits with a linear profile, and a gain map's one percent holds across a highlight and its field, not at its edge. A Lab color outside sRGB keeps its OKLab lightness and hue, and the system's generic CMYK profile proofs alike under perceptual, saturation, and relative. In the physics, a particle'sdragis taken once a step (45% of its speed a second at 60 Hz at the default) where a rigid body's is a loss per second and both reach a body or the walls when they are made; a body bounces off the walls or the ground with the larger of its own restitution and the world's; a spin is capped at a quarter turn a step; every 3D body loses 5% of its speed and spin each second; and a 3D frame a hair over 1/60 s takes two collision passes. Each gap is pinned by an invariant test that fails the day it closes, and filed in the design notes with the shape of its fix. -
A background on the canvas leaves the layers alone.
background(_:)outside a layer block used to drop everything the frame had recorded, the layers drawn before it included, so awithTargetlayer, awithFieldseed, or a feedback loop drawn before the canvas background came out empty with no error, and the reference told you to callbackgroundfirst. It now clears the canvas and keeps every layer's marks, so the order no longer matters: a frame that draws a layer, then the canvas, then the background is the same picture, pixel for pixel, as one that sets the background first. A frame that sets its background first is unchanged. withTarget -
Measured and written down where the vector outline and the render part. A test now checks, for all 34 distance-field shapes and point markers, that the outline the SVG export writes traces the shape the window draws. Three gaps stand as known issues with their numbers: the blobby cross draws a faint spike past each tip that its outline does not have, and the traced outlines of the cut disk and the horseshoe round their sharp corners off by about 2 px. A plain text run and the same text along a path or drawn glyph by glyph differ slightly at the curves (a 20 px O carries 4.5% more ink along a path); that is pinned the same way. Vector: SVG
-
Known gaps in the photoreal tier, measured and written down. Nineteen more physical invariants read the materials, the environment and the sky, the air, global illumination, the shadows, the path-traced export, the live temporal resolve,
LineSpray, and the lens flare's coatings against analytic values and a deep path trace. Five hold outright, and fourteen gaps stand as known issues with their numbers (inCAPABILITIES.mdand under Photorealistic 3D inDESIGN-NOTES.md). Among them: a rough white plastic loses about a tenth of its light at grazing under an environment, the procedural sky's sun casts almost no light of its own, and global illumination gathers no emission and ignores a lamp'sreach:, as does the path tracer. The 3D reference no longer promises that emitters light their neighbors under global illumination (they do so in a path-traced export) or that 2D in a 3D canvas is untouched by the live temporal resolve while the camera is still. -
A camera move starts at its start. A
cameraMoveorcameraShowcasemove advanced its clock before posing the camera, so its first frame showed it one step in and exported frame N was posed at (N+1)/fps whiletimeread N/fps. Two exports of one frame at different frame rates disagreed. The move now poses at its clock and then advances it, so frame N shows the move at N/fps. Every exported frame of a sketch with a camera move moves by one step. Camera moves -
A move resumed after the hands starts from the hands. Switching from
cameraControl()back to the samecameraMoveit left resumed the move from where it first started, so the camera jumped away from the shot just framed by hand. It now departs from the current pose, as switching to a different move always did. Camera control -
Exact black and exact white stay exact. The last pass adds up to a level of noise before the 8-bit encode so a smooth ramp does not band, and at the ends of the range that noise had nothing to do but harm: about one pixel in six of an exact black came out as 1, and one in eight of an exact white as 254. So a picture made only of black and white did not export as only black and white, and every export on a black ground fed a faint noise floor to the video encoder. The noise now reaches less far within a level of either end, never past it: exact black is 0 and exact white is 255 in every export and on the web page, gradients away from the ends dither as before, and values near an end average back to themselves more closely than they did. Color output
-
A motion streak keeps the light of what made it.
motionBlur()had kept a mover's inside at its full value while adding the smear around it, so a moving thing grew brighter: a 24-pixel box crossing at 20 pixels a frame gained 5% of its light at the default shutter and 25% at a full one, and a bloom read the excess. The gather now weighs each tap by whether a streak reaches the pixel and leaves the weights as they are, so their mean is the share of the shutter the streak spent there and the pixel's own color fills the rest. The box keeps its light within 0.2%, and region by region the streak stays within 2% of the box's value of the subframe export. On theMotionBlurexample the error around the spheres fell by two to three times, and the sharp ghost of a sphere inside its own streak is gone. A caption drawn over the 3D scene takes the motion of the surface under it, so draw it inwithOverlay { }. Motion blur -
A caustic gives back the light its shadow took, and no more. A clear glass ball's caustic had been adding half again the light its shadow removed, where real glass gives back less than it blocks. Two things made it bright: on a physically based floor the splat shaded its light at pi times what a direct light of the same strength gets, and it counted the angle the light arrives at twice. A half-float layer hid part of that, capping a focused spot at a power of two and shaving a few percent off everything else; the layer is single precision now. A caustic is now shaded exactly as the floor shades a direct light, on every shading model, and a clear pane gives back 92% of its shadow's light, a clear ball about 84% (the rest is what their surfaces reflect). Caustics are dimmer than before on a physically based floor;
caustics(intensity:)above 1 turns them up past physical. Light leaving glass now reflects at the angle it exits at, a live caustic no longer dims for its first frames while its photon plan settles, and a point or spot light's caustic carries the light the lamp lays at the end of each photon's path, so a lamp's ball gives back what its shadow took rather than a third of it, a mirror throws exactly the light of the lamp's mirror image, and a lamp'sreachthins its caustic as it thins its light. Caustics