Geometric primitives
Relevant code:
src/core/primitives/Primitive.hpp
src/core/primitives/Primitive.cpp
src/core/primitives/Mesh.hpp
src/core/primitives/Mesh.cpp
src/core/primitives/Quad.hpp
src/core/primitives/Sphere.hpp
src/core/primitives/Spotlight.hpp
src/core/primitives/InfiniteSphere.hpp
src/core/primitives/InfiniteSphereCap.hpp
Currently, the renderer supports 6 different geometric primitives. The most general and the most useful primitive is the triangle mesh, which is what you would usually use to build your scene. There are a number of other analytic geometric shapes, mainly for use as light sources. Although triangle meshes can be used as light sources too, the corresponding analytic counterpart is usually more efficient and generates a better sampling distribution.
Any primitive can be turned into a light source by giving it non-zero emission. In fact, all primitives support textured emission, where the emission value is provided by a bitmap or procedural texture. For that reason, all primitives support generating inbound (path tracing) and outbound (light tracing) directions for light sampling and can compute the pdf of generating a given direction in the solid angle measure.
For textured emission, some primitives are also capable of mapping UV coordinates to points on the surface and computing the Jacobian of that mapping; this way, a point on the emission texture can be sampled and turned into a point on the surface. For primitives that don't support this (i.e. triangle meshes), the surface will just be sampled uniformly, even with textured emission. This is not optimal, but better than not sampling the emitter at all.
The complete list of supported primitives is given below:
- Triangle mesh: The classic. Supports smooth vertex normals, UV coordinates, light sampling and UV tangent space.
- Analytic sphere: Useful for light sources and refractive balls
- Analytic quadrilateral: Useful for light sources
- Analytic disk: Useful for light sources. Also allows to constrain the emission angle to act as a spot light
- Infinite sphere: An infinitely distant sphere. This is what you would use for environment maps by giving it an emissive texture
- Infinite spherical cap: An infinitely distant spherical cap. This is useful for finite directional emitters, e.g. the sun
Textured triangle mesh primitive
Textured sphere primitive
Textured quadrilateral primitive
Textured disk primitive
Infinite sphere primitive with textured emission
Infinite spherical cap primitive lighting a ground plane
BSDF overview
Relevant code:
src/core/bsdfs/Bsdf.hpp
src/core/bsdfs/Bsdf.cpp
The renderer supports various BSDFs to mimic different materials. BSDFs typically provide one or several parameters to control the underlying model, which can be set through the JSON file. Many BSDFs in the renderer also support textured parameters, meaning that the scalar or vector value in the JSON can be replaced by a path to an image or a procedural texture. Examples of textured BSDF parameters are given in the feature sections, but this list is by far not exhaustive. Just try it out and see for yourself!
To facilitate material testing and comparison, there is a standard material test scene that comes with the renderer, featuring a simple backdrop, an environment map and a "material test ball" in the style of Mitsuba and other renderers. The test scene is shown below for a textured lambertian BSDF:
The Tungsten material test scene
To decrease code duplication and improve consistency across BSDFs, there is a set of standard parameters that are supported by all BSDFs. These are as follows:
- Bump map: Adds small scale detail to the shading normals. See the bump map section for more info
- Alpha: Controls the opacity of the surface, which is useful for masking (in foliage, for example) or not completely opaque surfaces such as thin plastic foil. An example of binary masking for a lambert BSDF is shown below:
- Surface albedo: Controls the diffuse reflectance of the surface, often perceived as the "color" of a material. Note that this parameter is ignored for dielectrics to remain physical.
Textured transparency
Texture overview
Relevant code:
src/core/materials/Texture.hpp
src/core/materials/BitmapTexture.hpp
src/core/materials/CheckerTexture.hpp
src/core/materials/DiskTexture.hpp
src/core/materials/BladeTexture.hpp
For the renderer, I implemented a texture system that can combine constant values, bitmap textures and procedural textures in the same framework. Anywhere a texture parameter is valid in the JSON, a constant value (either scalar of vector) can be used instead. In a similar vein, changing a constant parameter to use a texture instead is as simple as using the path to the image file as the parameter. For procedural textures, a texture object can be instantiated in the parameter.
Since textures are used, among others, to specify emission and apertures, all textures in the renderer, including procedural textures, have to be perfectly samplable according to the texel luminance. When turning a texture into a samplable object, the caller can also give a hint as to the Jacobian of the UV mapping. This is used, for example, in environment maps, where the environment primitive will hint that it's using a spherical mapping, so that the bitmap texture can warp the sample distribution according to the sine of the inclination angle.
Below is a list of the implemented textures, including their sampling distributions:
Bitmap texture
Various LDR formats such as JPG, PNG, TGA, BMP, GIF as well as HDR textures in PFM format are supported.
HDR Texture
Sample distribution on HDR texture
Checker texture
Useful for testing. The number of squares in the U- and V- direction can be controlled individually and the on- and off- color of the squares can also be changed.
Disk texture
This texture is mostly intended for apertures. There are no parameters.
Blade texture
This texture is mostly intended for apertures. The number of blades as well as the rotation of the texture can be controlled.