MaterialsΒΆ
Materials define how surfaces interact with light. In RayON, every material implements two functions: emitted() (returns radiance emitted by the surface) and scatter() (returns a scattered ray direction and an attenuation factor).
Material overviewΒΆ
| Type | Enum | Scatters | Emits | Key parameter |
|---|---|---|---|---|
| Lambertian | LAMBERTIAN | β | β | albedo (RGB) |
| Metal | METAL | β | β | albedo, roughness [0β1] |
| Mirror | MIRROR | β | β | β |
| Rough Mirror | ROUGH_MIRROR | β | β | roughness [0β1], albedo (tint RGB) |
| Glass | GLASS | β | β | ior (index of refraction) |
| Dielectric | DIELECTRIC | β | β | refractive_index |
| Anisotropic Metal | ANISOTROPIC_METAL | β | β | roughness, anisotropy, eta/k (complex IOR) |
| Thin Film | THIN_FILM | β | β | film_thickness (nm), film_ior |
| Clear Coat | CLEAR_COAT | β | β | albedo, roughness, refractive_index |
| Area Light | LIGHT | β | β | emission (RGB) |
| SDF Material | SDF_MATERIAL | β | β | For ray-marched SDF objects |
| Show Normals | SHOW_NORMALS | β | β | debug: normal β RGB |
| Constant | CONSTANT | β | β | debug: solid colour |
Lambertian (diffuse)ΒΆ
A perfectly diffuse surface scatters light equally in all directions weighted by the cosine of the angle from the surface normal (Lambert's cosine law):
where \(\rho\) is the albedo β the fraction of light the surface reflects.
Scatter direction: a random point on a unit sphere offset along the surface normal (cosine-weighted hemisphere β see Sampling).
Mirror (perfect specular)ΒΆ
A perfect mirror reflects rays using the reflection formula:
where \(\mathbf{d}\) is the incoming direction and \(\hat{n}\) is the surface normal. Attenuation is always 1.0 (no energy loss).
Rough Mirror (microfacet)ΒΆ
Extends the perfect mirror with a roughness parameter that adds a random perturbation to the reflected direction:
where \(\xi\) is a random vector in the unit sphere. Rays that scatter back below the surface are absorbed (energy conservation).
A tint (RGB colour) modulates the reflection to produce metallic effects:
Preset tints:
| Metal | Tint RGB |
|---|---|
| Gold | (1.00, 0.85, 0.57) |
| Copper | (0.95, 0.50, 0.30) |
| Steel | (0.80, 0.85, 0.90) |
| Iron | (0.56, 0.57, 0.58) |
| Bronze | (0.80, 0.50, 0.20) |
materials:
- name: gold_sphere
type: rough_mirror
albedo: [1.0, 0.85, 0.57] # tint = gold
roughness: 0.03 # near-perfect reflection
roughness=0.05. The warm tint is visible in highlights.
Anisotropic Metal (GGX microfacet)ΒΆ
A physically-based conductor material using the GGX microfacet distribution with a full complex index of refraction (Ξ· + ik) for accurate spectral Fresnel. Unlike rough mirror, it models the anisotropic highlight patterns seen on brushed metal, vinyl records, and machined surfaces.
The anisotropy parameter [0β1] stretches the highlight tangentially (0 = isotropic sphere, 1 = line highlight). Named presets use measured IOR values:
| Preset | Ξ· (RGB) | k (RGB) |
|---|---|---|
gold | (0.18, 0.42, 1.37) | (3.42, 2.35, 1.77) |
silver | (0.05, 0.06, 0.05) | (4.18, 3.35, 2.58) |
copper | (0.27, 0.68, 1.22) | (3.60, 2.63, 2.29) |
aluminum | (1.35, 0.97, 0.53) | (7.47, 6.40, 5.28) |
materials:
- name: brushed_gold
type: anisotropic_metal
preset: gold # uses measured IOR values
roughness: 0.08
anisotropy: 0.7
- name: custom_metal
type: anisotropic_metal
roughness: 0.1
anisotropy: 0.3
eta: [0.18, 0.42, 1.37]
k: [3.42, 2.35, 1.77]
Anisotropic metal spheres. The stretched highlight pattern is controlled by the anisotropy parameter.
Thin Film (interference)ΒΆ
Simulates the iridescent colour shifts seen on soap bubbles, oil slicks, and butterfly wings. Thin-film interference arises when the optical path difference between reflections from the top and bottom of a thin layer produces wavelength-dependent constructive/destructive interference:
The film_thickness parameter controls which wavelengths are amplified β smaller values produce blue/violet; larger values shift toward red.
materials:
- name: soap_bubble
type: thin_film
film_thickness: 400 # nanometers β shifts highlight colour
film_ior: 1.33 # refractive index of the film (water/soap β 1.33)
Thin-film material at various film thicknesses. Varying film_thickness sweeps through the visible spectrum.
Clear CoatΒΆ
A two-layer material combining a smooth dielectric coat (Fresnel specular) over a diffuse base. Physically it models car paint, lacquered wood, and hard-plastic objects.
At each ray hit, a Fresnel-weighted coin flip decides:
- Coat layer (probability proportional to Fresnel reflectance) β sharp specular reflection.
- Base layer (remaining probability) β Lambertian diffuse scatter using
albedo.
materials:
- name: red_car_paint
type: clear_coat
albedo: [0.8, 0.05, 0.05] # diffuse base colour
roughness: 0.04 # coat roughness (0 = mirror coat)
refractive_index: 1.5 # coat IOR (1.5 = typical lacquer/plastic)
The Stanford dragon with a red clear-coat material. The highlight is the dielectric coat; the colour is the diffuse base beneath.
Glass (dielectric)ΒΆ
Glass both reflects and refracts light. The ratio of reflected to transmitted energy is governed by the Fresnel equations, approximated efficiently using Schlick's formula:
Refraction direction follows Snell's law:
When \(n_1 > n_2\) and \(\theta_1\) exceeds the critical angle, total internal reflection occurs and the surface acts as a perfect mirror.
Glass spheres refracting the background. The caustic ring under the sphere is not explicit β it emerges from the path tracing integral.
Area LightΒΆ
Light sources are geometry (rectangles or spheres) with the LIGHT material. The scatter() function returns false (no further bouncing) and emitted() returns the emission colour.
Soft shadows emerge naturally β a larger light source means more rays from the scene "see" the light directly, producing smooth penumbrae.
materials:
- name: warm_white_light
type: light
emission: [5.0, 4.5, 3.5] # HDR β values > 1 are physically valid
geometry:
- type: rectangle
material: warm_white_light
position: [-1.0, 3.0, -2.0]
u_vec: [2.5, 0.0, 0.0]
v_vec: [0.0, 0.0, 1.5]
Cornell box lit by a single rectangular area light on the ceiling. The coloured walls produce visible colour bleeding on the floor and white sphere.
Debug materialsΒΆ
Two materials are available for development and debugging:
ShowNormals β maps the surface normal to RGB: [ \text{colour} = 0.5(\hat{n} + \mathbf{1}) ] Red = +X axis, Green = +Y axis, Blue = +Z axis.
Constant β emits a fixed solid colour regardless of lighting. Useful for environment backgrounds.
All surfaces shown with the ShowNormals material. Note how the smooth normal interpolation on the imported mesh makes the face boundaries invisible.
The material dispatch systemΒΆ
The GPU renderer stores all material parameters in a flat POD union and selects the correct code path at runtime via a switch statement, since virtual dispatch is not available on the GPU.
CPU renderers archived
The CPU rendering backends (which used polymorphic C++ virtual dispatch for materials) have been moved to the legacy/cpu-renderer branch.
A Plain Old Data (POD) union is a C/C++ union whose members contain only simple data fields (no constructors, destructors, or virtual functions). All members of a union occupy the same memory location β only one member is valid at a time, selected by a separate tag field. Because a POD union has no hidden pointers and no vtable, it can be passed directly to a GPU kernel like any other value type. The GPU cannot allocate heap memory or call virtual methods, so every material type (Lambertian, Glass, AnisotropicMetalβ¦) maps to one named member of this union:
// MaterialParamsUnion β POD struct, GPU safe
// All members share the same storage; 'type' (below) tells us which one is live.
union MaterialParamsUnion {
LambertianParams lambertian; // albedo only
MirrorParams mirror; // (empty)
RoughMirrorParams rough_mirror; // albedo + roughness
GlassParams glass; // refractive_index
LightParams light; // emission RGB
AnisotropicParams anisotropic_metal; // roughness, anisotropy, eta, k
ThinFilmParams thin_film; // film_thickness, film_ior
ClearCoatParams clear_coat; // albedo, roughness, coat IOR
// ...
};
At runtime the MaterialType enum tag selects the correct branch:
__device__ void scatter_material(const Material& mat, ...) {
switch (mat.type) {
case MaterialType::LAMBERTIAN: scatter_lambertian(...); break;
case MaterialType::ROUGH_MIRROR: scatter_rough_mirror(...); break;
case MaterialType::GLASS: scatter_glass(...); break;
case MaterialType::ANISOTROPIC_METAL: scatter_anisotropic(...); break;
// ...
}
}
The compiler inlines each case completely β zero virtual-function overhead on the GPU. See Architecture β Scene System for details about how materials are transferred to the GPU.




