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 theBBM_IMPORT_CONFIGcall to change the configuration.Value: same asConfig::ValueSpectrum: same asConfig::SpectrumScalar: underlying scalar type (typicallyfloatordouble). This can be different fromValue(e.g., in the case when value is a packet or differentiable type).Mask: in its simplest form this is equivalent tobool. However, some configurations might require more complex types that somewhat mimics the behavior of abool. However, similar toDrJITandEnoki, when the underlyingValueis a packet type, thenMaskis equivalent to an array ofbool. Hence, we cannot directly compare aMask. Instead use methods such asbbm::none(Mask),bbm::any(Mask),bbm::all(Mask)to reduce theMaskto abool, andbbm::select(Mask, A, B)to replace(Mask ? A : B).Size_t: integer version ofValue. IfValueis a packet type, thenSize_twill also be a packet.BsdfFlag: Will be a packet ifValueis a packet too.Constants: type dependent constants:-
template<typename T>
struct constants Public Static Functions
-
template<typename T>
Vec2dandVec3d: 2D and 3D vectors in which each element is of typeValue.Mat2dandMath3d: 2x2 and 3x3 matrices in which each element is of typeValueComplex: a complex type in which each component is of typeValue.BsdfSample: structure to hold a sampled direction and pdf:-
template<typename CONF>
struct bsdfsample Structure to hold a sample’s direction and PDF.
-
template<typename CONF>
Vec3dPair: a pair of in and out directions:-
template<typename CONF>
struct vec3dpair Structure to hold a pair of directions.
-
template<typename CONF>
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
Reduce vec3d to vec2d
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
Spherical Coordinates
The following additional vector operations are included to support spherical coordinates:
-
namespace spherical
Theta access
Phi access
Conversion
Sine variants
Cosine variants
Joint Cos/Sin variants
Tangent variants
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.