A material describes a surface: its color, how glossy it is, whether light passes through it, what light it gives off itself. Every drawn part names one shape and one material; the shape says where the surface is, the material how it answers light. A material is a value of type material with an identity: several parts may refer to one material, as several references point at one struct, and then one change to it (the dusk glow) reaches every part drawn in it. This page covers the material type and its fields, how parts refer to materials, the kinds of material the browser offers, the per-part tint, and the renderer settings that sit on materials and parts. Images painted onto materials are on Textures and surface patterns.

Words used on this page:

Emission
Light a surface gives off itself, whatever lights it, in nits (candela per square meter: a screen's or a lamp's surface brightness). Written glow.
Roughness
0 a mirror, 1 fully matte, in a physically based material.
Metalness
0 not metal, 1 metal: a metal's reflection takes its color, a non-metal's does not.
Tone mapping
The last step of drawing a picture, squeezing brightnesses from moonlight to sunlight into what a screen shows. A common choice is AgX, which pales very bright colors toward white.
Bloom
A soft halo spread round very bright points of the picture, which makes emission read as glowing.

How you write it

A material, held by a part

A material is declared under whatever part holds it, as a part holding the material, part name: constructor(...), and may have fields. The world's root holding a frameless part named materials is a convention of arrangement, not meaning: a material may equally be held by the cottage design or by one lantern. It lasts as long as its holder, and anyone refers to it by path.

part materials                                      // a part with no frame: a holder
  part lanternBlack: shiny(srgb(12, 13, 17), highlight: 0.9, shininess: 90)      // the stakes
  part lanternMetal: shiny(srgb(88, 62, 40), highlight: 0.22, shininess: 12)     // a bracket
  part lanternHead  : [shiny(srgb(88, 62, 40), highlight: 0.22, shininess: 12),
                       litByLayers([world, interior]),                           // frame, roof, plinth
                       glowing(color: glassColor, strength: glow)]
    field glow: nits = 0 nits                      // derived (below): not mutable
  part lanternGlass: [shiny(srgb(255, 205, 130), highlight: 0.45, shininess: 30),
                      translucent(covers: 0.12),                                 // the panes and lining
                      glowing(color: glassColor, strength: glow)]
    field glow: nits = 0 nits                      // derived (below): not mutable
  part lanternBulb  : [plain(srgb(255, 214, 122)),                               // the filament
                       glowing(color: lampColor, strength: glow)]
    field glow: nits = 0 nits                      // derived (below): not mutable
  part room         : [shaded(rooms, color: srgb(150, 120, 92), ambient: 0.25), // the rooms behind the windows
                       glowing(color: roomColor, strength: glow)]
    field glow: nits = 0 nits                      // derived (below): not mutable
  part windowGlass  : clearGlass(bends: 1.5, thickness: 6 mm,
                                 inside: leavesAfter(10 cm, (0.86, 0.93, 0.88)))
  part cottageBoards: [physical(color: white, roughness: 1),
                       paintedBy(surfacepatterns.PlankParams.siding(512, seed: 1928))]

A surface's color is written srgb(r, g, b), 0 to 255 (the screen's familiar numbers), turned into linear values for lighting; a glow's color is linear, color(r, g, b) (Values: colors).

How the lines are laid out. A material is a constructor, or a list of a base material (a constructor) and its modifiers, in brackets and separated by commas; a line ending in a comma goes on, and its continuation lines are indented further than the part word, aligned under the first item by custom. What the part holds (its field glow, any children) comes after the whole line, indented one step under the part word (Parts). The room material is one of the browser's own shaders, rooms: a warm beige room by day lit by a quarter of the ambient light, and at night the glow its glow field gives.

The rooms material goes on a part whose shape fills the window opening. The shader draws the room as seen through that shape, so the shape is a flat rectangle, a little behind the glass, with its texture coordinates running 0 to 1 across the window (the island puts each one 9 mm behind its pane). A room behind a window 0.82 m wide and 1.31 m tall:

fn windowRoom(w: meters, h: meters) shape {
  uvAt(face([(0, 0, 0), (w, 0, 0), (w, h, 0), (0, h, 0)]), p => (p.x / w, p.y / h))
}

part gableRoom: windowRoom(0.82 m, 1.31 m), material: materials.room, at: (-0.42 m, 3.8 m, -9 mm)

A glow: its color and its strength

glowing(color: c, strength: s) is a material modifier: the surface gives off light of color c at brightness s, in nits, whatever lights it. The glow is c with each of its channels multiplied by s: a color of (1, 0.3, 0.045) at a strength of 2800 gives off (2800, 840, 126) nits. strength may be a number of nits, or the name of a field of the material's own part, as glow is above: the glow then follows that field, and a derive on the field moves every surface drawn in the material. The color says which light; the field says how much. The lantern's three glowing materials share one pattern with different colors: the bulb glows in the lamp's own color, lampColor (linear (1, 0.397, 0.0953), about 2700 K), and the glass and the bronze head in a deeper amber, glassColor (1, 0.3, 0.045), so the tone map leaves the panes amber rather than peach. Both colors are fixed fields of the world, named once (The lanterns).

A part drawn in a material

part lanternHead
  part glass: panes(size),    material: materials.lanternGlass
  part bulb : filament(size), material: materials.lanternBulb

material: names a material by path (Parts: options). Inside a design a part's other names are relative (glass, self.parent.lantern[13]); a material held at the world's root is named from the root, so the same line works in every copy wherever it is placed. A part refers; it never describes a look of its own and names a shared one at once.

One value reaching every user

derive(materials.lanternBulb.glow,  lampLevel * bulbBrightness.at(eveningHour(hour(now(world)))))
derive(materials.lanternGlass.glow, materials.lanternBulb.glow * 0.09)
derive(materials.lanternHead.glow,  materials.lanternBulb.glow * 0.0015)
derive(materials.room.glow,         materials.lanternBulb.glow * 0.4)

The bulb material's glow is the world's lamp level times the brightness for the hour: about 2800 nits at dusk (17:28), falling to 1.3 nits from 20:00, because the picture's exposure rises about 5000-fold over the evening. The brightness is world data, a curve held in the world's field bulbBrightness: eight evening hours and the bulb's radiance at each, blended on a log scale, a morning hour read as the evening hour mirrored about noon by the world's function eveningHour (both written out on Lights: the world's lamp level). The glass glows at 0.09 of it, the bronze head at 0.0015, a faint warm lift, not a light, and a lit room behind a window at 0.4. Since every bulb is drawn in this one material, one formula lights fourteen lanterns.

Delivery is the engine's business. Content states what the glow is at every moment. When a changed value is sent to the renderer (only once it has moved by more than a hundredth, say), and any counts kept of what was sent, belong to the engine: content neither states nor reads them. A test that wants to see them uses the browser's test tooling (Outside the content: test hooks).

One part different: the tint

part pathLantern: lanternHead
  field gain: number = 1, within: 1 ..= nearGain, mutable: true   // brighter while someone is near
  derive(glass.tint, gain)                            // this lantern's parts only: a number, a gray
  derive(bulb.tint,  gain)

Every drawn part has a field tint, a color whose default is white, which a derive or a set may write. A tint multiplies one part's color and glow, and the colors of a painted material too (the image's texels times the tint); white means "as the material is". The conifers' age tint is the part's tint, a color whose components pass 1 (it runs from about (2.7, 3.0, 1.0) for a young tree to (0.78, 0.86, 0.92) for an old one), and a tree's foliage takes its part's tint. It may be written as a color, or as a number n, meaning the gray (n, n, n): a gain of 1.45 makes the glass and bulb 1.45 times as bright in every color, with their hue unchanged. The material is untouched and the other thirteen lanterns stay as they are. It is the one per-part change to a shared material: the material says what the surface is, the part may only scale it.

What it means

The material type

"Shared" is not a kind. The type is material; sharing is only several parts referring to one instance. An instance has identity and may have changing fields (the bulb's glow); it lives wherever it is held (the world's root, the cottage design, one lantern) and lasts as long as its holder. Sharing across kinds is common: lantern and wall lantern; rocks and edging stones; oak for doors, beams and hulls; one wetness darkening every surface in rain.

Sharing is meaning or mechanism

A shared material is sharing a change can observe: if a change to it would reach several users, it is meaning, and the renderer may never split it. Sharing that nothing can observe (twelve lanterns drawn as copies of one mesh, which nothing changes) is a resource choice, the browser's. The two forms of a glow differ in meaning, not only in cost:

A glow on the shared materialA glow field on each lantern
How many valuesonefourteen
What a change reachesevery part drawn in the material; nothing can light one aloneone lantern; lighting all is fourteen changes, or a rule on the design run per copy
What can differonly a tintanything
What must be kept in stepone value, or none if every browser works it out from the clockfourteen

The lanterns mean both at once: one shared glow for the schedule, and a per-lantern multiplier (the tint) for nearness. The content says which it means.

Order does not change meaning

A material is defined once, in its own place. Two definitions under one path are refused when the world is loaded. A part whose material has not yet arrived waits for it, or is drawn with a stand-in and corrected; it never defines it. Removing an object never removes a material its parts used, unless the object holds it.

Where a material's fields live

A material's field lives where the rules that read and write it run. The glow is a formula of the clock and the lamp level, so every browser could work it out for itself. Network cost never appears in content.

The kinds of material

The browser offers one material record, filled by constructors and changed by modifiers:

ConstructorWhat it describes
plain(color)a color, no highlight: a matte surface
shiny(color, highlight: fraction, shininess: number)a color plus a highlight of a strength (0 to 1) and tightness: the older Blinn-Phong model
physical(color: color, roughness: fraction | image = 1, metal: fraction = 0)the physically based model, which never reflects more light than it receives. The defaults are a fully matte non-metal. roughness may be a number or a one-channel image made by painted(valueAt: ...) (Textures: painting an image)
clearGlass(bends: number, thickness: meters, inside: leavesAfter(distance, color))glass that bends light (bends is its index of refraction), dimmed by its color inside; ray-traced where the graphics card can, else drawn by bending the picture already drawn
shaded(shader, ...)one of the browser's own shaders by name, with its own named values: terrain, water, brook, rooms (the rooms behind the windows: color:, ambient:), the view surface

A bare color is not a material. Wherever a material goes (material:, the list that makes one), write one: the plainest material of a color c is physical(color: c), fully matte and not metal.

Modifiers, each changing one setting, follow the constructor as the later items of a list, [constructor, modifier, ...]: matte, specular(amount: number) (the specular highlight as a share of the usual: matte is specular(amount: 0), and the island's conifer needles are specular(amount: 0.12)), backlit(share: number) (lit from behind when the sun is behind it: the maple leaves; a bird's wing is backlit(share: 0.25), the side away from the sun taking a quarter of the diffuse light the side facing it takes; the share is a factor that may exceed 1, since a thin leaf passes more than it receives in its own color: the maple's is backlit(share: 1.2) and the conifers' 0.9; written bare, backlit takes the browser's usual share), fluttering(amount: angle) (the most a leaf turns about its stem, each on its own period of 0.6 s to 1.8 s: the maples' fluttering(amount: 0.12 rad), about 7 degrees; it needs the foliage to carry its leaves' pivots, sway: true, Generators), translucent(covers: fraction) (see-through by blending: covers is how much of what lies behind it is hidden), dispersing, frosted, litByLayers (which lights reach it), cutOut(threshold: fraction) (holes where an image's alpha is below the threshold: the needle cards, cutOut(threshold: 0.5)), glowing(color:, strength:) (above), and paintedBy(image), which works on every kind (Textures). The ConeScript names on this page are provisional spellings; the vocabulary names for materials are not yet chosen, but each is spelled one way everywhere in this manual.

Two-sided. A shape's faces are one-sided: a face is seen only from the side it faces (Shape tools: primitives). A thin sheet seen from either side, such as a wing or a leaf, is drawn with the material option twoSided: true, a named option of the material's part, not an item of its list: part birdWing: [physical(color: color(0.075, 0.062, 0.05), roughness: 1), backlit(share: 0.25)], twoSided: true. The side turned away from the eye is shaded as if it faced it.

Underneath, the render package's material record (PbrMaterial) is far richer: base color, metallic, roughness, clearcoat, a thin iridescent film, wetness, textures for color, metal-roughness, normals and occlusion, emission, alpha mode and cut-off, transmission, flutter, specular scale, light layers, sheen, anisotropy, subsurface scattering, glass with index, thickness, absorption and dispersion, and builders for standard, chitin, glowing, translucent, cut-out, coated, iridescent, wet, soaked, velvet, brushed, skin, wax, glass, frosted, reflective and caustic-receiving surfaces. A world's vocabulary of materials would be drawn from this, not from Look.

The pbrmaterial package says what goes into a physically based material, as opposed to how it is lit: its per-texel output is a base color, roughness, metallic, occlusion, wear and grime, baked into a texture set (bakeTextureSet). It has no layered materials and no material graph; parameter fields evaluated in shaders (a film's thickness, a wet mask) and GPU bakes are not built.

Two models of gloss

"Shiny" and "physical" describe the same thing, a surface's gloss, in two models, and a world's numbers mean different things in each: in shiny(..., highlight: 0.22, ...) 0.22 is the strength of a Blinn-Phong highlight, not a roughness. A world written against one renderer's model fixes that renderer. Which model the vocabulary offers is open.

Light color and surface color

A light's color is the mix of wavelengths it gives off; a surface's color is its reflectance, the share of each color it sends back. What is seen is the light's color times the surface's: a shirt shows green only under light that contains green. An example lamp has four warm colors tuned by eye against its picture: the lamp's own, about 2700 K, linear (1, 0.397, 0.095), for the bulb's glow and interior light; a deeper amber (1, 0.3, 0.045) for the glass "so that the tone mapping leaves the interior amber and not peach"; the light thrown on the ground deeper still (1, 0.14, 0.01), because grass reflects mostly green; and a room's (1, 0.25, 0.028). The content there compensates for the grass and the tone map. Whether content states physical quantities (2700 K, a reflectance) or colors as they should look is open.

Renderer concerns on materials and parts

Several settings on materials and parts exist for the renderer, not for what the thing is:

Whether these may appear in content at all, as declarations, or must be stated as requirements ("the light inside reaches the ground"; "the frame is lit from within") that the browser meets as it can, is open.

How it connects

Open questions

  1. May a renderer concern appear in content? Shadow casting, light layers, shadow-only parts: as declarations; never, only requirements; or some derived (a see-through part casts no shadow) and the rest requirements. A proposal now writes layers and shadow switches as declarations (light layers, casts no shadow); shadow-only parts, and requirements in their place, remain open.
  2. Physical or perceived color? Physical quantities and the engine answers for tone mapping; the color as it should look; or both, chosen per value.
  3. Emission on the material or the part?
  4. One glass or two? The lanterns' see-through amber and the windows' refracting glass: two names, or one glass with qualities, the browser choosing the method and checking the shape.
  5. One material a part, or several over regions of a shape, split by the browser for drawing?
  6. Which model of gloss: only physically based, translating the rest, or several?
  7. Override beyond tint: may a part override a material's roughness or paint, and how does that differ from a material of its own?
  8. Local or world-wide: may a subtree (the labyrinth) hold materials only its parts name? (By "lifetime by holder", yes; how names resolve is open.)
  9. Who may write a formula for a material that reaches several objects, and where in the content it belongs.
  10. Changing a definition while running: the glow is a field; replacing the leaf paint for autumn changes the definition itself.
  11. Arrival order: whether definitions always travel first, or what a part looks like while it waits.
  12. A look from where you are: a color chosen from the facing, the height or the side seen, not from texture coordinates: a wing darker above and paler below, a body shading from belly to back by the way its surface faces, a ground chosen from its slope and the distance to the eye. A bird stands in four parts (body, beak, tail, wings) for one mesh. A design question (Terrain).

← Terrain | Contents | Textures and surface patterns →