pysdic.compute_brdf_beckmann#

compute_brdf_beckmann(surface_points, surface_normals, light_positions, observer_positions, parameters, *, default=0.0)[source]#

Compute the Bidirectional Reflectance Distribution Function (BRDF) using Beckmann’s model for given parameters \((\rho_d, \rho_s, \text{rms})\) (ie. diffuse, specular, and RMS slope).

The BRDF describes how light is reflected at an opaque surface. Beckmann’s model accounts for both diffuse and specular reflection components. The equation for the BRDF in Beckmann’s model is given by:

\[\text{BRDF}(\theta_i, \theta_o) = \frac{\rho_d}{\pi} + \frac{\rho_s}{\pi m^2 \cos(\delta)^4} \exp\left(-\frac{\tan^2(\delta)}{m^2}\right)\]

where:

  • \(\theta_i = \arccos(\mathbf{I} \cdot \mathbf{N})\) is the angle between the input light direction and the surface normal.

  • \(\theta_o = \arccos(\mathbf{O} \cdot \mathbf{N})\) is the angle between the observer direction and the surface normal.

  • \(\delta = \arccos(\mathbf{H} \cdot \mathbf{N})\) is the angle between the surface normal and the half-vector (the bisector between the input and observer directions).

  • \(\rho_d\) is the diffuse reflection coefficient.

  • \(\rho_s\) is the specular reflection coefficient.

  • \(m\) is the root mean square (RMS) slope of the surface.

\[\delta = \arccos(\mathbf{H} \cdot \mathbf{N}) \quad \text{where} \quad \mathbf{H} = \frac{\mathbf{I} + \mathbf{O}}{||\mathbf{I} + \mathbf{O}||}\]

Note that \((A \cdot B)\) denotes the positive dot product between vectors \(A\) and \(B\) given by \(\max(0, A^T B)\). If the input light direction \(\mathbf{I}\) or the observer direction \(\mathbf{O}\) is below the surface (i.e., \(\theta_i > \frac{\pi}{2}\) or \(\theta_o > \frac{\pi}{2}\)), the BRDF is defined to be zero.

Note

The inputs arrays will be converted to numpy.float64 for computation. The output array will be also of type numpy.float64.

Parameters:
  • surface_points (ArrayLike) – Array of shape \((N_p, 3)\) or \((3,)\) representing the coordinates of \(N_p\) surface points in 3-dimensional space. If a 1D array is provided, it is treated as a single surface point \((1, 3)\).

  • surface_normals (ArrayLike) – Array of shape \((N_p, 3)\) or \((3,)\) representing the normal vectors at each surface point. Must have the same shape as surface_points.

  • light_positions (ArrayLike) – Array of shape \((N_l, 3)\) or \((3,)\) representing the position(s) of the light source(s). If a 1D array is provided, it is treated as a single light source \((1, 3)\).

  • observer_positions (ArrayLike) – Array of shape \((N_o, 3)\) or \((3,)\) representing the position(s) of the observer(s). If a 1D array is provided, it is treated as a single observer \((1, 3)\).

  • parameters (ArrayLike) – Array of shape \((N_{\text{models}}, 3)\) or \((3,)\) representing the BRDF parameters \((\rho_d, \rho_s, \text{rms})\) for each model. Diffuse coefficient \(\rho_d\) must be non-negative. Specular coefficient \(\rho_s\) must be non-negative. RMS slope \(\text{rms}\) must be positive. If a 1D array is provided, it is treated as a single set of parameters \((1, 3)\).

  • default (Real, optional) – Default value to use for invalid BRDF computations (e.g., when input or observer directions are below the surface). By default, 0.0 is used.

Returns:

brdf – Array of shape \((N_p, N_l, N_o, N_{\text{models}})\) representing the BRDF values at each \((N_p)\) surface point for each \((N_l)\) light source and each \((N_o)\) observer position for each \((N_{\text{models}})\) set of parameters.

Return type:

numpy.ndarray

Raises:
  • TypeError – If the inputs cannot be converted to floating-point numpy arrays.

  • ValueError – If input arrays do not have the correct dimensions or if coefficients are not valid numbers.

Examples

Basic usage with single point, light source, and observer:

 1import numpy
 2form pysdic import compute_brdf_beckmann
 3
 4surface_points = numpy.array([
 5    [0.0, 0.0, 0.0]
 6    [1.0, 0.0, 0.0]
 7])
 8
 9surface_normals = numpy.array([
10    [0.0, 0.0, 1.0]
11    [0.0, 0.0, 1.0]
12])
13
14light_positions = numpy.array([10.0, 0.0, 10.0])
15
16observer_positions = numpy.array([0.0, 0.0, 10.0])
17
18brdf_values = compute_brdf_beckmann(
19    surface_points,
20    surface_normals,
21    light_positions,
22    observer_positions,
23    parameters=numpy.array([0.5, 0.5, 0.2])
24)
25
26print(f"BRDF values (Shape: {brdf_values.shape}):")
27print(brdf_values)
BRDF values (Shape: (2, 1, 1, 1)):
[[[0.07957747]]
 [[0.07957747]]]