3ds Max C++ API Reference
IPhysicalCamera_BitmapApertureSampler Class Reference

Utility class that provides efficient sampling for a bitmap aperture on the physical camera. More...

#include <IPhysicalCamera_BitmapApertureSampler.h>

Classes

class  IApertureBitmapAccess
 Interface used to provide generic access to the bitmap aperture. More...
 
struct  RGBValue
 A simple RGB color value. the Max SDK's Color class couldn't be used since we want to remain independent from the rest of the Max SDK. More...
 

Public Member Functions

 IPhysicalCamera_BitmapApertureSampler (IApertureBitmapAccess &aperture_bitmap_access, const float center_bias, const bool bitmap_affects_exposure)
 Constructs the sampler using the parameter values extracted from IPhysicalCamera and a callback interface used for fetching values from the aperture bitmap. More...
 
void sample_aperture (const float sample_u, const float sample_v, float &sampled_position_u, float &sampled_position_v, RGBValue &sample_weight) const
 Samples a location in the aperture bitmap. More...
 
Access to sampling acceleration structure

These provide access to the internal acceleration structures, useful if the client uses this class for building the structures but wishes to implement its own sampling code

Returns whether the sampling acceleration structure was built successfully.

May be false, for example, if the aperture bitmap is all black.

bool is_sampler_valid () const
 Returns the weights to be applied to the outgoing camera ray. More...
 
const std::vector< RGBValue > & get_pixel_weights () const
 Returns the weights to be applied to the outgoing camera ray. More...
 
const std::vector< float > & get_y_selection_cdf () const
 Returns the cumulative distribution function (CDF) used to select a row in the aperture bitmap. More...
 
const std::vector< float > & get_x_selection_cdf () const
 Returns the cumulative distribution function (CDF) used to select a column, within a previously selected row, in the aperture bitmap. More...
 
const int get_x_resolution () const
 Returns the resolution of the acceleration structure in X. More...
 
const int get_y_resolution () const
 Returns the resolution of the acceleration structure in Y. More...
 

Detailed Description

Utility class that provides efficient sampling for a bitmap aperture on the physical camera.

When a bitmap aperture is used, uniformly sampling the aperture will result in massively reduced performance or increased noise. This class implements a smarter, weight-based sampling method which will introduce minimal performance degradation from using a bitmap aperture. This class is intended to be used in one of three ways: For directly sampling the bitmap aperture For building the sampling acceleration structures, and then using them in the shading code (e.g. if the shader cannot reference C++ code) As a reference for implementing your own sampler

Remarks
WARNING: This class makes no reference whatsoever to the rest of the Max SDK. This enables it to be used in a renderer's shading code without pulling in the Max SDK.

Constructor & Destructor Documentation

◆ IPhysicalCamera_BitmapApertureSampler()

IPhysicalCamera_BitmapApertureSampler ( IApertureBitmapAccess aperture_bitmap_access,
const float  center_bias,
const bool  bitmap_affects_exposure 
)
inline

Constructs the sampler using the parameter values extracted from IPhysicalCamera and a callback interface used for fetching values from the aperture bitmap.

Parameters
aperture_bitmap_accessThe interface which provides access to contents of the aperture bitmap.
center_biasThe physical camera's aperture center bias, as returned by IPhysicalCamera::GetBokehCenterBias().
bitmap_affects_exposureWhether the aperture bitmap affects exposure, as returned by IPhysicalCamera::GetBokehTextureAffectExposure().
81  : m_sampler_valid(false),
82  m_x_resolution(0),
83  m_y_resolution(0)
84 {
85  m_x_resolution = aperture_bitmap_access.get_x_resolution();
86  m_y_resolution = aperture_bitmap_access.get_y_resolution();
87 
88  if((m_x_resolution > 0) && (m_y_resolution > 0))
89  {
90  const size_t num_pixels = size_t(m_x_resolution) * size_t(m_y_resolution);
91  m_pixel_weight.resize(num_pixels);
92  m_y_selection_cdf.resize(m_y_resolution);
93  m_x_selection_cdf.resize(num_pixels);
94 
95  double original_bitmap_weight = 0.0f; // use double as additions are terrible on floating-point precision
96 
97  // Accumulate the CDF for the entire bitmap
98  double cdf_entire_bitmap = 0.0; // use double as additions are terrible on floating-point precision
99  for(size_t y = 0, pixel_index = 0; y < m_y_resolution; ++y)
100  {
101  // Accumulate the CDF for the current row
102  double cdf_current_row = 0.0; // use double as additions are terrible on floating-point precision
103  for(unsigned int x = 0; x < m_x_resolution; ++x, ++pixel_index)
104  {
105  // Evaluate the pixel value
106  const RGBValue original_pixel_value = aperture_bitmap_access.get_pixel_value(x, static_cast<unsigned int>(y));
107  // Apply the center bias
108  const RGBValue pixel_value_with_bias = apply_center_bias(original_pixel_value, center_bias, x, static_cast<unsigned int>(y), m_x_resolution, m_y_resolution);
109 
110  // Accumulate CDF using pixel value that has the center bias applied (sampling takes this bias into account)
111  const float pixel_sampling_weight = get_sampling_weight(pixel_value_with_bias);
112  cdf_current_row += pixel_sampling_weight;
113  m_x_selection_cdf[pixel_index] = static_cast<float>(cdf_current_row);
114  m_pixel_weight[pixel_index] = pixel_value_with_bias;
115 
116  // The original pixel value, without center bias, is accumulated such that we may undo its effect if "affect exposure" is disabled
117  original_bitmap_weight += get_sampling_weight(original_pixel_value);
118  }
119 
120  // Accumulate the CDF for the entire row
121  cdf_entire_bitmap += cdf_current_row;
122  m_y_selection_cdf[y] = static_cast<float>(cdf_entire_bitmap);
123  }
124 
125  // Check whether the bitmap wasn't entirely black
126  if(cdf_entire_bitmap > 0.0f)
127  {
128  // Normalize the CDFs
129  for(size_t y = 0; y < m_y_resolution; ++y)
130  {
131  if(m_y_selection_cdf[y] > 0.0f)
132  {
133  const float row_total_cdf = m_x_selection_cdf[(y * m_x_resolution) + m_x_resolution - 1];
134  if(row_total_cdf > 0.0f)
135  {
136  for(size_t x = 0, pixel_index = y * m_x_resolution; x < m_x_resolution; ++x, ++pixel_index)
137  {
138  m_x_selection_cdf[pixel_index] /= row_total_cdf;
139  assert(m_x_selection_cdf[pixel_index] <= 1.0f);
140  }
141  }
142 
143  m_y_selection_cdf[y] /= m_y_selection_cdf[m_y_resolution - 1];
144  assert(m_y_selection_cdf[y] <= 1.0f);
145  }
146  }
147 
148  // Calculate the global weight to be applied to every pixel of the bitmap
149  double global_bitmap_weight = 1.0;
150  if(bitmap_affects_exposure)
151  {
152  // The bitmap's contents implicitly affect the the exposure since we multiply the ray result by the bitmap's pixel value.
153  // Here we scale the bitmap's contents such that a bitmap containing a white circle would render identically
154  // to a circular aperture.
155  global_bitmap_weight *= 1.0 / (M_PI * (0.5 * 0.5)); // one over the area of a disc with radius 0.5
156 
157  // If center bias is present, it has modified the texture but we don't want to let this affect exposure. We therefore
158  // counteract the effect.
159  global_bitmap_weight *= original_bitmap_weight / cdf_entire_bitmap;
160  }
161  else
162  {
163  // Because the bitmap's contents implicitly affect the exposure, here we counteract this such that the average
164  // pixel value gets normalized to 1.0.
165  const double bitmap_average_pixel_value = cdf_entire_bitmap / num_pixels;
166  global_bitmap_weight /= bitmap_average_pixel_value;
167  }
168 
169  // Calculate the final weight of every pixel in the bitmap
170  for(size_t y = 0, pixel_index = 0; y < m_y_resolution; ++y)
171  {
172  // Probability of sampling this y coordinate
173  const float previous_y_cdf = (y > 0) ? m_y_selection_cdf[y - 1] : 0.0f;
174  const float y_sampling_prob = m_y_selection_cdf[y] - previous_y_cdf;
175 
176  for(unsigned int x = 0; x < m_x_resolution; ++x, ++pixel_index)
177  {
178  // Apply the global weight (results from how we want exposure to be affected)
179  m_pixel_weight[pixel_index] *= static_cast<const float>(global_bitmap_weight);
180 
181  // Probability of sampling this x coordinate within the current row
182  const float previous_x_cdf = (x > 0) ? m_x_selection_cdf[pixel_index - 1] : 0.0f;
183  const float x_sampling_prob = m_x_selection_cdf[pixel_index] - previous_x_cdf;
184 
185  // Probability of sampling this pixel overall
186  const double pixel_sampling_prob = double(x_sampling_prob) * double(y_sampling_prob);
187 
188  // Compensate for the sampling probability of the pixel. Pixels should all have equal weight on the final result (photons
189  // have an equal likelihood of passing through any location on the aperture), but since we sample the aperture non-uniformly
190  // we need to compensate.
191  const double uniform_sampling_probability = (1.0 / num_pixels);
192  const double sample_compensation = (pixel_sampling_prob > 0.0f) ? (uniform_sampling_probability / pixel_sampling_prob) : 0.0;
193  m_pixel_weight[pixel_index] *= static_cast<const float>(sample_compensation);
194  }
195  }
196 
197  m_sampler_valid = true;
198  }
199  }
200 }
controller mat max min numsubs x z controller keys x z controller keys x
Definition: generics.inl:212
#define assert(expr)
Definition: assert1.h:81
MAXMEM_EXTERN_C UtilExport size_t(__cdecl *MAX_msize)(void *memblock)

Member Function Documentation

◆ sample_aperture()

void sample_aperture ( const float  sample_u,
const float  sample_v,
float &  sampled_position_u,
float &  sampled_position_v,
RGBValue sample_weight 
) const
inline

Samples a location in the aperture bitmap.

Parameters
sample_uThe first random sample value, in [0,1)
sample_vThe second random sample value, in [0,1)
[out]sampled_position_uReturns the the sampled position in U on the aperture in [-1,1]
[out]sampled_position_vReturns the the sampled position in V on the aperture in [-1,1]
[out]sample_weightReturns the weight of the sample that needs to be applied to the outgoing camera ray
241 {
242  if(m_sampler_valid)
243  {
244  // Sample a row
245  const float y_coord = sample_cdf_pixel(sample_v, m_y_selection_cdf.begin(), m_y_selection_cdf.end());
246  assert(y_coord < m_y_resolution);
247 
248  // Sample a x coordinate within the selected row
249  const std::vector<float>::const_iterator cdf_row_begin = m_x_selection_cdf.begin() + (size_t(y_coord) * m_x_resolution);
250  const float x_coord = sample_cdf_pixel(sample_u, cdf_row_begin, cdf_row_begin + m_x_resolution);
251  assert(x_coord < m_x_resolution);
252 
253  // Rescale sampled coordinate in [-1,1]
254  sampled_position_u = ((x_coord / m_x_resolution) * 2.0f) - 1.0f;
255  sampled_position_v = ((y_coord / m_y_resolution) * 2.0f) - 1.0f;
256  assert((sampled_position_u >= -1.0f) && (sampled_position_u <= 1.0f) && (sampled_position_v >= -1.0f) && (sampled_position_v <= 1.0f));
257 
258  // Evaluate the aperture bitmap weight
259  sample_weight = get_aperture_bitmap_weight(x_coord, y_coord);
260  assert((sample_weight.r > 0.0f) || (sample_weight.g > 0.0f) || (sample_weight.b > 0.0f));
261  }
262  else
263  {
264  // Sampler not valid: sample center point only
265  sampled_position_u = 0.0f;
266  sampled_position_v = 0.0f;
267  sample_weight = RGBValue(1.0f, 1.0f, 1.0f);
268  }
269 }

◆ is_sampler_valid()

bool is_sampler_valid ( ) const
inline

Returns the weights to be applied to the outgoing camera ray.

314 {
315  return m_sampler_valid;
316 }

◆ get_pixel_weights()

const std::vector< IPhysicalCamera_BitmapApertureSampler::RGBValue > & get_pixel_weights ( ) const
inline

Returns the weights to be applied to the outgoing camera ray.

319 {
320  return m_pixel_weight;
321 }

◆ get_y_selection_cdf()

const std::vector< float > & get_y_selection_cdf ( ) const
inline

Returns the cumulative distribution function (CDF) used to select a row in the aperture bitmap.

324 {
325  return m_y_selection_cdf;
326 }

◆ get_x_selection_cdf()

const std::vector< float > & get_x_selection_cdf ( ) const
inline

Returns the cumulative distribution function (CDF) used to select a column, within a previously selected row, in the aperture bitmap.

329 {
330  return m_x_selection_cdf;
331 }

◆ get_x_resolution()

const int get_x_resolution ( ) const
inline

Returns the resolution of the acceleration structure in X.

334 {
335  return m_x_resolution;
336 }

◆ get_y_resolution()

const int get_y_resolution ( ) const
inline

Returns the resolution of the acceleration structure in Y.

339 {
340  return m_y_resolution;
341 }