BBM Basics

Lets take the previous example again:

#include "bbm.h"
using namespace bbm;

int main(int argc, char** argv)
{
  BBM_IMPORT_CONFIG( floatRGB );

  return 0;
}

The header file bbm.h includes all relevant header files for BBM and sets up the BBM backbone. All BBM code resides in the bbm namespace, hence we import the namespace bbm. Some methods might clash with methods with the same name in other namespaces, and might still require explicitely adding bbm::.

bbm::config

The macro BBM_IMPORT_CONFIG( <config name> ) sets up some convenient type aliases based on the chosen bbm configuration. The number of available configurations depend on the backbone. For example, the native backbone currently only supports:

  • floatRGB: scalar non-differentiable/non-packet floating point types.

  • doubleRGB: scalar non-differentiable/non-packet double precision floating point types.

The goal of a configuration is to easily template classes (e.g., BSDFs) with common basic types such as Value and Spectrum. Other backbones support additional configurations (e.g., floatDiffRGB and floatPacketRGB), and future configurations are possible. It is also possible to create your own configuration. BBM relies on C++ concepts for setting specification requirements. A config is a structure that must meet the following requirements:

template<typename T>
concept config
#include <config.h>

config concept

Each config struct contains:

  • concepts::named

  • a Config typedef that points to itself

  • a Value typedef

  • a Spectrum typedef

  • static Spectrum wavelength() that returns a Spectrum-type with the wavelength in each channel in nm.

The macro BBM_IMPORT_CONFIG( <config name> ) will take the information from the config, and makes the following aliases active in the current scope:

  • Config: an alias to the imported config. Use this alias to pass to BBM classes instead of the actual name of the BBM config. This way, one only need to change the BBM_IMPORT_CONFIG call to change the configuration.

  • Value: same as Config::Value

  • Spectrum: same as Config::Spectrum

  • Scalar: underlying scalar type (typically float or double). This can be different from Value (e.g., in the case when value is a packet or differentiable type).

  • Mask: in its simplest form this is equivalent to bool. However, some configurations might require more complex types that somewhat mimics the behavior of a bool. However, similar to DrJIT and Enoki, when the underlying Value is a packet type, then Mask is equivalent to an array of bool. Hence, we cannot directly compare a Mask. Instead use methods such as bbm::none(Mask), bbm::any(Mask), bbm::all(Mask) to reduce the Mask to a bool, and bbm::select(Mask, A, B) to replace (Mask ? A : B).

  • Size_t: integer version of Value. If Value is a packet type, then Size_t will also be a packet.

  • BsdfFlag: Will be a packet if Value is a packet too.

    enum class bbm::bsdf_flag

    Reflectance Component Evaluation Flags.

    Values:

    enumerator None
    enumerator Diffuse
    enumerator Specular
    enumerator All
  • Constants: type dependent constants:

    template<typename T>
    struct constants

    Public Static Functions

    static inline constexpr T Epsilon(void)
    static inline constexpr T Pi(T scale = 1)
    static inline constexpr T InvPi(T scale = 1)
    static inline constexpr T Pi2(T scale = 1)
    static inline constexpr T InvSqrtPi(T scale = 1)
    static inline constexpr vec2d<T> Sphere(T scale = 1)
    static inline constexpr vec2d<T> Hemisphere(T scale = 1)
  • Vec2d and Vec3d: 2D and 3D vectors in which each element is of type Value.

  • Mat2d and Math3d: 2x2 and 3x3 matrices in which each element is of type Value

  • Complex: a complex type in which each component is of type Value.

  • BsdfSample: structure to hold a sampled direction and pdf:

    template<typename CONF>
    struct bsdfsample

    Structure to hold a sample’s direction and PDF.

    Public Members

    Vec3d direction

    Sampled direction.

    Value pdf

    Pdf of the sampled direction.

    BsdfFlag flag

    Type of sample.

  • Vec3dPair: a pair of in and out directions:

    template<typename CONF>
    struct vec3dpair

    Structure to hold a pair of directions.

    Public Members

    Vec3d in

    In direction.

    Vec3d out

    Out direction.

Note

BBM_IMPORT_CONFIG is recursive. Any type that itself has a bbm-config imported in its scope, can also serve a config.

struct Foo { BBM_IMPORT_CONFIG( floatRGB ); };
struct Bar { BBM_IMPORT_CONFIG( Foo ); };       // will use Foo::Config = floatRGB

An example of using BBM_IMPORT_CONFIG:

#include <iostream>
#include "util/typestring.h"

#include "bbm.h"
using namespace bbm;

template<typename CONF> requires concepts::config<CONF>
struct Foo
{
   BBM_IMPORT_CONFIG( CONF );
   Value val;
};

int main(int argc, char** argv)
{
  BBM_IMPORT_CONFIG( floatRGB );

  Vec2d v(1, 2);
  Foo<Config> foo;

  std::cout << v << std::endl;
  std::cout << toTypestring(v) << std::endl;
  std::cout << bbm::typestring<Specrtum> << std::endl;
}

The above example, defines a struct Foo that takes a config as template parameter CONF. Here we use concepts::config<CONF> to check that CONF is indeed a valid config. The concept concepts::has_config<T> can be used to check if a class has an imported configuration.

Note

All concepts in bbm are organized in the bbm::concepts namespace.

After BBM_IMPORT_CONFIG we can use any of the aliases. For example, in the main body we declare a variable Vec2d v, and a instance of Foo. Note that in the latter case we use the Config alias; if at any point we decide to change the configuration, we only need to change the BBM_IMPORT_CONFIG line.

Note

utils/typestring.h contains two useful tools for debugging. toTypestring is a macro that takes first extract the type of the argument, and then calls the const-expression bbm::typestring<...> to convert the type to a human-readable std::string_view.

Basis operations

All basis types support a number of operations implemented in the backbone: Math operations, Horizontal operations, Comparison operations, and Control operations. In addition, each type as a corresponding mask type that can be obtained with the bbm::mask_t<...> expression with corresponding Mask operations.

Math operations

template<typename T>
concept has_math_functions
#include <math.h>

Concept to check if a type has all bbm math functions.

  • fmod(a, b): modulo

  • lerp(a, b, t): linear interpolation

  • exp(a): \( e^a \)

  • log(a): natural logarithm

  • pow(a, b): \( a^b \)

  • sqrt(a): \( \sqrt{a} \)

  • cos(a), sin(a), tan(a): trig functions

  • acos(a), asin(a), atan(a): inverse trig functions

  • atan2(a, b) = atan(a/b)

  • cossin(a) : vec2d(cos(a), sin(a))

  • cosh(a), sinh(a), tanh(a); hyperbolic functions

  • acosh(a), asinh(a), atanh(a); inversehyperbolic functions

  • abs(a): absolute value

  • copysign(a, b) : copy the sign of b and the magnitude of a

  • sign(a) = copysign(1, a)

  • max(a,b), min(a,b)

  • ceil(a), floor(a)

  • round(a)

  • clamp(a, l, u): min(max(a, l), u)

  • safe_sqrt(a) : \( \sqrt{ max(a,0) } \)

  • safe_acos(a) : asin( clamp(a, -1, +1) )

  • safe_asin(a) : acos( clamp(a, -1, +1) )

  • eq, neq: equals and not equals; yields mask_t

  • isnan, isinfinite, isinf; yields mask_t

Horizontal operations

template<typename T>
concept horizontal
#include <horizontal.h>

A type T has horizontal functions if:

Regular horizontal operations:

  • hsum(T a): returns value_t(a[0] + a[1] + …)

  • hprod(T a): returns value_t(a[0] * a[1] * …)

  • hmax(T a): returns the maximum element value_t

  • hmin(T a): returns the minimum element value_t

  • dot(T a, T b): returns value_t( a[0]*b[0], a[1]*b[1], ….)

  • norm(T a): return sqrt(dot(a,a))

  • squared_norm(T a): returns dot(a,a)

  • normalize(T a): returns (a / norm(a))

Mask horizontal operations:

  • all(M a): (a[0] && a[1] && …)

  • any(M a): (a[0] || a[1] || …)

  • none(M a): !all(a)

  • count(M a): (a[0] + a[1] + ….)

Comparison operations

template<typename T>
concept ordered
#include <ordered.h>

Comparison operators:

Requires that the type has eq, neq, <, >, <=, and => operators that each return the same type. The return type does not need to be a boolean. == and != must return a boolean.

Control operations

template<typename T>
concept control
#include <control.h>

A type T has valid control methods if:

  • mask_t<T> is defined

  • value_t<T> is defined

  • remove_diff_t<T> is defined

  • index_t<T> is defined

  • cast<NewType>(oldType) cast oldType to newType

  • select(Mask, T, T) returns a type T based on the mask.

  • lookup<T>(container, index_t, index_mask_t) returns a type T; testing on std::vector as container

  • set(container, index_t, T, index_mask_t) set a value in a container taking; similar to lookup handles packet data

  • binary_search(container, predicate, index_mask_t) returns the index of the first element in container for which predicate is false.

Mask operations

template<typename M>
concept horizontal_mask
#include <horizontal.h>

A type M has horizontal masking functions if:

  • M is a recognized mask type

  • all(M) returns a bool(a[0] && a[1] && …)

  • any(M) returns a bool(a[0] || a[1] || …)

  • none(M) returns a bool(!any(M))

  • count(M) returns a size_t(hsum(M))

Non-scalar Operations

Vec2d, Vec3d, and Spectrum are multi-value types that support element-wise lookup with operator[].

Additional Vector Operations

Vectors have additional helper functions defined in the bbm::vec namespace:

namespace vec

Shortcuts to vector coordinates

template<typename T>
inline constexpr decltype(auto) x(bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) x(const bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) y(bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) y(const bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) z(bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) z(const bbm::vec3d<T> &v)
template<typename T>
inline constexpr decltype(auto) x(bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) x(const bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) y(bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) y(const bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) u(bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) u(const bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) v(bbm::vec2d<T> &v)
template<typename T>
inline constexpr decltype(auto) v(const bbm::vec2d<T> &v)

Reduce vec3d to vec2d

template<typename T>
inline constexpr const vec2d<T> xy(const vec3d<T> &v)
template<typename T>
inline constexpr const vec2d<T> xz(const vec3d<T> &v)
template<typename T>
inline constexpr const vec2d<T> yz(const vec3d<T> &v)

Increase the dimension of a vec

template<typename T, typename V>
inline constexpr const vec3d<T> expand(const vec2d<T> &v, V &&a)
template<typename T>
inline constexpr const vec3d<T> expand(const vec2d<T> &v)
template<typename T>
inline constexpr const vec2d<T> expand(T &&c)

In addition the following methods are available in the bbm namespace:

template<typename T>
inline vec2d<T> bbm::perp(const vec2d<T> &v)

Returns the lefthand (clockwise) perpendicular vector of a 2D vector.

Parameters:

v – Input vector

Returns:

(y, -x)

template<typename T>
inline vec2d<T> bbm::cperp(const vec2d<T> &v)

Returns the righthand (counterclockwise) perpendicular vector of a 2D vector.

Parameters:

v – Input vector

Returns:

(-y, x)

template<typename T>
inline vec3d<T> bbm::reflect(const vec3d<T> &v)

Reflects a 3D vector around Z=1.

Parameters:

v – Input vector to reflect

template<typename T>
inline vec3d<T> bbm::reflect(const vec3d<T> &v, const vec3d<T> &normal)

Reflects a 3D vector.

Parameters:
  • v – Input vector to reflect

  • normal – Normal to reflect around

template<typename T>
inline constexpr vec3d<T> bbm::cross(const vec3d<T> &a, const vec3d<T> &b)

Cross product of two 3D vectors.

Parameters:
  • a – = first vector

  • b – = second vector

template<typename T>
vec3d<T> bbm::halfway(const vec3d<T> &a, const vec3d<T> &b)

Halfway vector (3D)

Parameters:
  • a – = first vector

  • b – = second vector

template<typename T>
std::pair<vec3d<T>, vec3d<T>> bbm::convertToHalfwayDifference(const vec3d<T> &a, const vec3d<T> &b)

Convert fro Canonical to the Halfway-Difference parameterization.

Parameters:
  • a – = first vector

  • b – = second vector

template<typename T>
vec3d<T> bbm::difference(const vec3d<T> &a, const vec3d<T> &b)

Difference vector.

Parameters:
  • a – = first vector

  • b – = second vector

template<typename T>
std::pair<vec3d<T>, vec3d<T>> bbm::convertFromHalfwayDifference(const vec3d<T> &half, const vec3d<T> &diff)

Convert from Halfway-Difference to Canonical parameterization.

Parameters:
  • a – = first vector

  • b – = second vector

Spherical Coordinates

The following additional vector operations are included to support spherical coordinates:

namespace spherical

Theta access

template<typename T>
T &theta(vec2d<T> &v)
template<typename T>
const T &theta(const vec2d<T> &v)
template<typename T>
T theta(const vec3d<T> &v)

Phi access

template<typename T>
T &phi(vec2d<T> &v)
template<typename T>
const T &phi(const vec2d<T> &v)
template<typename T>
T phi(const vec3d<T> &v)

Conversion

template<typename T>
vec2d<T> convert(const vec3d<T> &v)
template<typename T>
vec3d<T> convert(const vec2d<T> &v)

Sine variants

template<typename T>
T sinTheta(const vec2d<T> &v)
template<typename T>
T sinTheta2(const vec2d<T> &v)
template<typename T>
T sinTheta2(const vec3d<T> &v)
template<typename T>
T sinTheta(const vec3d<T> &v)
template<typename T>
T sinPhi(const vec2d<T> &v)
template<typename T>
T sinPhi(const vec3d<T> &v)
template<typename T>
T sinPhi2(const vec2d<T> &v)
template<typename T>
T sinPhi2(const vec3d<T> &v)

Cosine variants

template<typename T>
T cosTheta(const vec2d<T> &v)
template<typename T>
T cosTheta(const vec3d<T> &v)
template<typename V>
bbm::value_t<V> cosTheta2(const V &v)
template<typename T>
T cosPhi(const vec2d<T> &v)
template<typename T>
T cosPhi(const vec3d<T> &v)
template<typename T>
T cosPhi2(const vec2d<T> &v)
template<typename T>
T cosPhi2(const vec3d<T> &v)

Joint Cos/Sin variants

template<typename T>
vec2d<T> cossinTheta(const vec2d<T> &v)
template<typename T>
vec2d<T> cossinTheta(const vec3d<T> &v)
template<typename T>
vec2d<T> cossinTheta2(const vec2d<T> &v)
template<typename T>
vec2d<T> cossinTheta2(const vec3d<T> &v)
template<typename T>
vec2d<T> cossinPhi(const vec2d<T> &v)
template<typename T>
vec2d<T> cossinPhi(const vec3d<T> &v)
template<typename T>
vec2d<T> cossinPhi2(const vec2d<T> &v)
template<typename T>
vec2d<T> cossinPhi2(const vec3d<T> &v)

Tangent variants

template<typename T>
T tanTheta(const vec2d<T> &v)
template<typename T>
T tanTheta(const vec3d<T> &v)
template<typename T>
T tanTheta2(const vec2d<T> &v)
template<typename T>
T tanTheta2(const vec3d<T> &v)
template<typename T>
T tanPhi(const vec2d<T> &v)
template<typename T>
T tanPhi(const vec3d<T> &v)
template<typename T>
T tanPhi2(const T &v)

Matrix

Matrices are implemented based on the vector implementations provided by the backbone. It support element lookup with operator(row, col) and supports additional transpose and identity methods.

Matrix Transformations

template<typename T>
mat3d<std::decay_t<T>> bbm::rotationX(T angle)

Rotation around the X-axis.

Parameters:

angle – = rotation angle

template<typename T>
mat3d<std::decay_t<T>> bbm::rotationX(const vec2d<T> &cossin)

Rotation around the X-axis.

Parameters:

angle – = rotation angle

Similar functions exists for the Y and Z axes.

Shading Frame

template<typename T>
mat3d<std::decay_t<T>> bbm::toGlobalShadingFrame(const vec3d<T> &normal)

Construct a local shading frame to global frame transformation given a normal direction.

Create a shading frame transformation from the local to the global shading frame determined by the surface normal. The tangent vector is randomly selected.

Parameters:

normal – = local shading frame normal direction

Returns:

shading frame transformation matrix from local to global frame.

template<typename T>
inline auto bbm::toLocalShadingFrame(const vec3d<T> &normal)

Construct a global to local shading frame transformation.