A CSG object is made of other objects. union, merge, intersection and difference combine them using the question every object can answer: is this point inside? The result is an object like any other: it takes modifiers, can be declared, transformed, textured, clipped and used as a member of another CSG object. This page lists the syntax, how each operation finds the surface, which objects have an inside, how the members get their textures, and how a CSG object is bounded and split. The shapes themselves are on Finite objects, Infinite and implicit objects and Meshes. The modifiers are on Object modifiers.
Syntax
union { object ... [modifiers] }
merge { object ... [modifiers] }
intersection { object ... [modifiers] }
difference { object ... [modifiers] }
| Word | Result | Surface that remains |
|---|---|---|
union | all the space inside any member | all the surfaces of all the members. Surfaces inside other members stay |
merge | the same as a union | only the surfaces that are not inside another member, so there are no inner walls |
intersection | the space inside every member | for each member, the part of its surface that is inside all the others |
difference | the space inside the first member and outside all the others | the part of the first member outside the rest, and the parts of the others that are inside the first |
composite | an old name for union | the same as a union (a warning appears only in a scene that declares a version before 1.5) |
The members are any objects, in any number, in any order: shapes, other CSG objects, object { ... } wrappers, light sources. They are read until the first word that is not an object; then the modifiers follow. As with every shape, the first thing in the block may be the name of a declared object, which is copied. A CSG with fewer than two members is accepted without a message (the warning only exists for versions before 1.5).
These words start an object, and so can be a member of a CSG object (or the body of clipped_by, bounded_by and looks_like): box, sphere, cylinder, cone, torus, disc, lemon, ovus, superellipsoid, lathe, prism, sor, text, polygon, triangle, smooth_triangle, bicubic_patch, blob, sphere_sweep, julia_fractal, plane, poly, cubic, quartic, polynomial, quadric, isosurface, parametric, height_field, mesh, mesh2, union, merge, intersection, difference, composite, light_group, light_source, object, and the name of a declared object.
How the surface is found
To trace a ray, a CSG object asks each member for all the points where the ray crosses its surface and keeps some of them.
- A union keeps every crossing of every member.
- A merge keeps a crossing of one member only if the point is not inside any other member. This costs one inside test per other member.
- An intersection keeps a crossing of one member only if the point is inside all the other members.
- A difference is an intersection. The parser inverts every member but the first, then builds the intersection. The inverted members are "everything outside the object", so their surfaces are kept where they lie inside the first member, and the first member's surface is kept where it lies outside the others.
The inside test of a member is the question the shape itself answers (see the table below). A ray that goes on inside an object, for example in refraction or in media, relies on the crossings coming in pairs, one entering and one leaving. A shape that is not closed breaks that, and so gives wrong pictures in CSG.
A point counts as inside a CSG object by the same rules, applied to the members. A member that has no inside never passes the test of an intersection or a difference. The parser warns Patch objects not allowed in intersection. when a patch is a member of an intersection or a difference.
inverse
The modifier inverse turns the inside and the outside of an object round. On a CSG object the parser rewrites the object with the rules of De Morgan: an inverted union (or merge) becomes an intersection of the inverted members; an inverted intersection or difference becomes a merge of the inverted members. That is how difference works internally. inverse on an object that has no inside (a patch) gives the warning Cannot invert a patch object.
The inside of each object
| Object | Inside is | Can be used in CSG? |
|---|---|---|
box, sphere, torus, superellipsoid, ovus, julia_fractal, sphere_sweep | the volume they enclose | yes |
cylinder, cone, lemon, prism, sor | the volume they enclose, if closed. The default is closed | yes, but not with open: without the caps the parity of the crossings is wrong |
lathe | the volume the curve encloses when turned (the curve must be closed by the user) | yes, if the curve is closed |
text | the extruded letters (the text is a union of glyph solids) | yes |
blob | where the field is above the threshold | yes |
poly, cubic, quartic, polynomial, quadric | where the polynomial is negative (or zero for the quadric) | yes |
plane | the half-space behind the plane, opposite the normal | yes |
disc | the half-space behind the plane of the disc (the radii are not used) | yes, but it is a half-space, not a flat solid |
isosurface | inside the container, where the function is below the threshold (above with polarity) | yes, with max_trace or all_intersections so that all crossings are found; not with open |
height_field | below the surface, inside the unit square | yes |
mesh, mesh2 | none by default. With inside_vector: a point is inside if a ray from it along that direction crosses an odd number of triangles | only with inside_vector, and the mesh must be closed |
polygon | nothing: the inside test is always false | in a union only |
triangle, smooth_triangle, bicubic_patch, parametric, light_source | nothing: they are patches | in a union only |
A CSG object that has any patch among its members is a patch itself: it takes over the patch property of its members, so it has no inside either. The same holds one level up. A union of patches is fine (it is a way to group triangles). An intersection or a difference with a patch member has an inside test that is always false for that member, so the result is unreliable.
In a difference, the patch behaves as expected only as the first member: solids can be subtracted from a patch with the expected result. Subtracting a patch from a solid, or inverting a patch, can give surprising results.
Textures and flags of the members
After the whole scene is read, the parser walks each CSG object and gives its members what they do not already have. The order is: first the object's own texture, then the parent's.
| Member has | Rule |
|---|---|
no texture | it uses the texture of the CSG object (the same texture, not a copy). The uv_mapping flag comes with it |
a texture | it keeps its own. The texture of the CSG object does not mix with it and does not go on top |
no interior_texture | it uses the interior texture of the CSG object |
no interior | it uses the interior of the CSG object |
| no texture at all, in the CSG and in itself | it gets the default texture that is in force when the object is placed in the scene (#default { ... }) |
no texture, inside an intersection or difference with cutaway_textures | it does not get the default texture; see below |
A transformation of a CSG object is applied to every member and to every texture the CSG and its members have at that moment. So a texture that is written after a translate or a rotate in the same block is not moved by it. The same holds for the object modifiers: write the transformations last if the texture should move with the object.
These flags and settings of a CSG object are also copied to the members that do not set them: no_shadow, no_image, no_reflection, no_radiosity, cutaway_textures, the photons flags (target with its density, pass_through, reflection, refraction, collect) and the radiosity { importance } value. hollow and double_illuminate are different: when they are read in a union, merge, intersection or difference, the parser at once sets or clears the flag on every member (hollow) or on the whole tree below (double_illuminate). A union in which every member has no_shadow gets no_shadow itself.
cutaway_textures
Normally the face made by a cut takes the texture of the object that cut: in a difference, the face of the subtracted object. If that object has no texture, it gets the default texture. With cutaway_textures in an intersection or difference, members that have no texture are not given the default texture. For a cut face, POV-Ray runs the inside tests of the members: where the point lies inside an object, that object's texture is used, evaluated at that point. Where the objects overlap and are CSG objects with different textures, the textures are averaged. The keyword is an error in anything but an intersection or a difference (cutaway_textures can only be used with intersection and difference.).
Bounding, clipping and splitting
| Item | What it does |
|---|---|
| Bounding box | The parser computes the box of a CSG object from its members. For a union or merge it is the box that encloses the boxes of all members. For an intersection it is the common part of the boxes of the members that are not inverted (height fields are skipped; planes and quadrics are used last, to be limited by the others). So the box of a difference is that of its first member. |
bounded_by { object ... } | The CSG object is only tested for a ray that hits the bounding objects. Bounding objects may not have a texture and may not be patches (Illegal texture or patch in clip, bound, object or potential pattern.). If the box of the bounding objects is smaller than the box of the CSG object, it is used as the box of the CSG object. |
clipped_by { object ... } | Cuts away the parts of the surface that are outside the clipping objects. A point must be inside all of them to stay, so several objects mean an intersection. A clip object can itself be a CSG object, for example a difference (below). The same restrictions as for bounding objects apply. A clip does not close the cut: only the surface is removed, nothing is added. |
| Removal of bounds | A bounding object on a shape that is cheap to test (anything but a CSG object, a poly, cubic, quartic, polynomial, text, or a quadric whose box was not computed from a clip) is removed after parsing with the warning Unnecessary bounding object removed., if the INI option Remove_Bounds is on (default on). A bound that is also used as the clip is kept. |
split_union n | Controls whether a top-level union is split into its members. The value is a number or on/off; the default is on (the parser's flag is nonzero). It is accepted only on a union or merge (anything else is an error, split_union found in non-union object.). See below. |
Splitting. After the parse a union that is not inside another CSG object, has no clipped_by and is not a merge is split: its members are linked to the scene one by one and the union disappears. The point is speed: the members are then bounded individually. The union is kept whole if split_union off (or 0) was written, and if it has a bounded_by and either one of its members is infinite or the INI option Split_Unions is off (its default). A bounded union that is split gives the warning Bounded CSG union split.; one that is kept although it could have been split gives CSG union unnecessarily bounded. The textures and flags of the union are passed on to its members before the split, so splitting does not change the picture.
Intersections, differences and merges are never split. A CSG object that is itself a member of another CSG object is not split either.
The object wrapper
object { object [modifiers] }
An object block holds one object and then modifiers. The inner object can be a shape written in place or the name of a declared object. The result is a copy that is changed by the modifiers; the declared original is not touched. This is the way to place the same declared object many times.
- Transformations are added after the ones the declared object already has.
pigment,normalandfinishreplace that part of the plain texture the object already has. Atextureblock is laid on top of the existing texture as a new layer, so the object needs a transparent texture for the lower layer to show. Layering over a texture that is a pattern (atexture_maportiles) is the errorCannot layer over a patterned texture.- The object can be a CSG object; the modifiers are applied to the CSG object as a whole.
- Written without the
objectword, the name of a declared object can follow the opening brace of any shape:cylinder { Peg translate x }. The declared object is copied whatever its type was and the shape's own arguments are not read.
Light sources in CSG
A light_source can be a member of a CSG object, and the light then moves, rotates and scales with the object. A light has no surface of its own. Its optional looks_like { object } is a visible object at the position of the light; the parser moves it to the light's position, gives it the no-shadow flag, and gives the light the no-shadow flag as well. For the inside tests of an intersection or a merge a light without looks_like is skipped, so it does not change the result. A CSG object that holds only lights is marked as a union of lights, and the light sources in it are processed as global lights. In a light_group they are local to the group. See Lights.
Examples
// a die: a cube rounded by a sphere, with two pits cut out
difference {
intersection {
box { -1, 1 }
sphere { 0, 1.35 }
}
sphere { <0, 0, -1.4>, 0.3 }
sphere { <-1.4, 0, 0>, 0.3 }
pigment { rgb <1, 1, 0.9> }
translate -x*3
}
// merge removes the surface inside: a see-through shape shows no inner shell
merge {
sphere { <-0.4, 0, 0>, 0.8 }
sphere { <0.4, 0, 0>, 0.8 }
pigment { rgbf <0.6, 0.8, 1, 0.7> }
interior { ior 1.3 }
}
// a union texture applies to the members that have none
union {
cylinder { <0, -1, 0>, <0, 1, 0>, 0.3 pigment { rgb <1, 0, 0> } }
cylinder { <-1, 0, 0>, <1, 0, 0>, 0.3 }
pigment { rgb <0, 0, 1> }
translate x*3
}
#declare Peg = cylinder { <0, 0, 0>, <0, 1, 0>, 0.2 pigment { rgb <1, 0.5, 0> } }
// the object wrapper: reuse a declared object with extra modifiers
object { Peg translate -x*2 }
object { Peg scale 2 pigment { rgb <0, 1, 0> } }
// clipped_by with a CSG object as the clip
sphere {
<2, 0.5, 0>, 1
pigment { rgb 1 }
clipped_by {
difference {
box { <1, -1, -2>, <3, 2, 2> }
sphere { <2, 0.5, -1>, 0.6 }
}
}
}
// a light with looks_like, inside a union
union {
light_source {
<0, 3, 0>, rgb 1
looks_like { sphere { 0, 0.2 pigment { rgb 1 } finish { emission 1 } } }
}
box { <-0.5, 2, -0.5>, <0.5, 2.2, 0.5> pigment { rgb 0.4 } }
}
// inverse on a union gives an intersection of the inverted members
intersection {
box { <-3, -3, -0.5>, <-1, -1, 0.5> }
union {
sphere { <-2, -2, 0>, 0.8 }
sphere { <-1.2, -2, 0>, 0.8 }
inverse
}
pigment { rgb <1, 0, 1> }
}
// cutaway_textures: the cut faces take the texture of the object that was cut
difference {
sphere { 0, 1 pigment { rgb <1, 0.5, 0> } interior_texture { pigment { rgb <0, 0.5, 1> } } }
box { <-0.5, -0.5, -2>, <0.5, 0.5, 0> pigment { rgb <0, 1, 0> } }
cutaway_textures
}
// hollow and split_union
union {
sphere { <3, 0, 0>, 0.5 }
sphere { <4, 0, 0>, 0.5 }
split_union off
hollow
pigment { rgb 1 }
}
// a solid mesh in a difference needs an inside_vector
difference {
box { <-4, -1, -1>, <-2, 1, 1> pigment { rgb 1 } }
mesh {
triangle { <-3, -1.5, -1.5>, <-2.2, -1.5, -1.5>, <-3, 0.5, -1.5> }
triangle { <-3, -1.5, -1.5>, <-2.2, -1.5, -1.5>, <-3, -1.5, 0.5> }
triangle { <-3, -1.5, -1.5>, <-3, 0.5, -1.5>, <-3, -1.5, 0.5> }
triangle { <-2.2, -1.5, -1.5>, <-3, 0.5, -1.5>, <-3, -1.5, 0.5> }
inside_vector <0, 0, 1>
}
}
Not described
Known, not yet covered
- Reference: the exact way
cutaway_texturesdecides between textures at the shading step (the average over overlapping objects is taken from the manual) and the way bounding slabs use the box of a CSG object (theBoundingandBounding_ThresholdINI options) are not described.