miBoolean mi_sample( double *sample, int *instance, miState *state, const miUshort dimension, const miUint *n)
This is mental ray's primary generic sampling function. It can be used to sample arbitrary functions for evaluating integrals by deterministic sample points, which converge faster than their random counterparts and thus reduce rendering time. The mi_sample function is a high-level easy-to-use interface hiding all mathematical details of advanced quasi-Monte Carlo integration from the shader writer. sample is an array with dimension members that the new sample point will be stored into. Sample results are uniformly distributed in the range [0,1)^{dim}. instance is the current sample number; it must point to an integer that is initialized to 0 before the first loop iteration. If there is no loop, a null pointer may be passed. state is the current shader state. dimension is the dimension of the sample, such as 3 for XYZ vectors. n must point to an integer that specifies the number of samples to take. If only a single sample is taken, n may be a null pointer. The body of the mi_sample loop may not write to *sample or *n or vary dimension between any subsequent calls in the same loop because mi_sample computes the next sample incrementally from the previous.
mi_sample has three main applications:
Note that the returned sample point sample must be used as one point. This means that several one dimensional samples must not be assembled to yield a multidimensional sample, and that a multidimensional sample must not be used in order to simulate consecutive events. If, for example, 3D sampling vectors are computed with mi_sample, a single loop with dimension 3 must be used, rather than a higher dimension to obtain other sampling values at the same time, or fewer to compute the X, Y, and Z direction separately. Samples from mi_sample must be used consecutively. The sequence should not be terminated prematurely, nor should samples be dropped, since rejection sampling will result in biased approximations.
A call to mi_sample in single sample mode looks like:
void a_single_sample(..., miState *state, ...) { double sample[2]; mi_sample(sample, 0, state, 2, 0); /* use sample[0] and sample[1] */ }
The sampling dimension in this example is 2. Since only a single sample is required, no sample counting is indicated by passing a null pointer as *instance counter and to the required number of samples n. After the call to mi_sample, the array x holds a two-dimensional sample point of [0,1)^{2}. The function call always returns miFALSE in single sample mode.
In the adaptive sampling mode, the number of samples to be taken is not known in advance, and the code must decide when to break from the loop (which it may not in the other two modes, where mi_sample returns miFALSE). mi_sample is then used the following way:
void adaptive_sampling(..., miState *state, ...) { double sample[5]; int sample_number = 0; while (mi_sample(sample, &sample_number, state, 5, 0)) { /* use sample[0], ..., sample[4] */ if (enough samples taken) break; } }
Adaptive sampling is indicated to mi_sample by passing a null pointer as the number of samples n. The while loop is then controlled by the mi_sample call, which always returns miTRUE in adaptive sampling mode. The current sample number starting with 1 for the first sample is kept in sample_number. This variable must be initialized to zero in order to force initialization in mi_sample. Note that in deterministic sampling, adaptive sampling (here, the break condition) cannot be controlled by the calculation of variance as in Monte Carlo context. This will result in biased approximations. Adaptive sampling here could be based on contrast calculations, for example. sample holds a sample point in the five-dimensional unit cube [0,1)^{5}.
In the classical case, where the number of samples is known in advance, the deterministic sampling controlled by mi_sample very much resembles a Monte Carlo quadrature:
void finite_sampling(..., miState *state, ...) { const miUint samples = 16; double sample[2]; int sample_number = 0; while (mi_sample(sample, &sample_number, state, 2, &samples)) { /* use sample[0], sample[1] */ } }
Here, all arguments of mi_sample are used. Unlike the
previous adaptive sampling case, the number of samples is passed
explicitly to mi_sample.
Again the integer variable sample_number is initialized to
zero and passed by reference to instance. *state is
the current state of the shader.
mi_sample returns miTRUE
for samples iterations of the loop, until finally miFALSE is
returned to terminate the loop. The loop should not be terminated
with a break or return statement, because then the
internal state of state does not guarantee convergence of rendering
any longer (that is, state→qmc_instance
and
state→qmc_component
are wrong until the shader terminates).
In the loop, *instance provides the current sample number, starting
at 1, until samples is reached. The loop controls a two-dimensional
integration, where sample is an array of doubles with two
elements. The vector sample is the sample point in the two-dimensional
unit cube [0,1)^{2}. The finite sampling mode exposes a faster
convergence than the adaptive sampling mode.
Exceptional convergence can be achieved if the number of samples is
dimension−1 | ||
samples = | ∏ | p_{i} ^{ki} |
i = 1 |
where p_{i} are the prime numbers, i.e., p_{1} = 2, p_{2} = 3, etc., and k_{i} is a (positive) natural number. In two dimensions, i.e., dimension = 2, samples should be a power of 2. In the special case of dimension = 1, any number of samples can be used in order to achieve optimal convergence.
Note that if mi_sample controls the loop, it may take fewer samples than expected. For example, consider a sun, modeled as a disk area light source, with sampling set to request 8 samples. Assuming the sun is setting and only half of it is visible over the horizon, sampling the lower half cannot contribute any light. In this case, a light sampling function such as mi_sample_light (which is a specialization of mi_sample) may return miTRUE four times, and then abort the loop by returning miFALSE for the fifth call. The shader should work for such early aborts, even if the very first call returns miFALSE. The shader should also not assume any valid result parameters, such as dot_nl, for a call to mi_sample that has returned miFALSE.
Copyright © 1986, 2013 NVIDIA Corporation