PBRT_V2 总结记录 <79> Distribution1D

Distribution1D 

struct Distribution1D {
    // Distribution1D Public Methods
    Distribution1D(const float *f, int n) {
        count = n;
        func = new float[n];
        memcpy(func, f, n*sizeof(float));
        cdf = new float[n+1];
        // Compute integral of step function at $x_i$
        cdf[0] = 0.;
        for (int i = 1; i < count+1; ++i)
            cdf[i] = cdf[i-1] + func[i-1] / n;

        // Transform step function integral into CDF
        funcInt = cdf[count];
        if (funcInt == 0.f) {
            for (int i = 1; i < n+1; ++i)
                cdf[i] = float(i) / float(n);
        }
        else {
            for (int i = 1; i < n+1; ++i)
                cdf[i] /= funcInt;
        }
    }
    ~Distribution1D() {
        delete[] func;
        delete[] cdf;
    }
    float SampleContinuous(float u, float *pdf, int *off = NULL) const {
        // Find surrounding CDF segments and _offset_
        float *ptr = std::upper_bound(cdf, cdf+count+1, u);
        int offset = max(0, int(ptr-cdf-1));
        if (off) *off = offset;
        Assert(offset < count);
        Assert(u >= cdf[offset] && u < cdf[offset+1]);

        // Compute offset along CDF segment
        float du = (u - cdf[offset]) / (cdf[offset+1] - cdf[offset]);
        Assert(!isnan(du));

        // Compute PDF for sampled offset
        if (pdf) *pdf = func[offset] / funcInt;

        // Return $x\in{}[0,1)$ corresponding to sample
        return (offset + du) / count;
    }
    int SampleDiscrete(float u, float *pdf) const {
        // Find surrounding CDF segments and _offset_
        float *ptr = std::upper_bound(cdf, cdf+count+1, u);
        int offset = max(0, int(ptr-cdf-1));
        Assert(offset < count);
        Assert(u >= cdf[offset] && u < cdf[offset+1]);
        if (pdf) *pdf = func[offset] / (funcInt * count);
        return offset;
    }
private:
    friend struct Distribution2D;
    // Distribution1D Private Data
    float *func, *cdf;
    float funcInt;
    int count;
};

作用:

(To work out how to sample from 1D piecewise-constant functions
(step functions),

Distribution1D 作用主要就是,用均匀的[0,1] 来采样 1D 分段常数函数的 采样点)

Distribution1D is a small utility class that represents a piecewise-constant 1D function’s
PDF and CDF and provides methods to perform this sampling efficiently.

 

1. 构造函数

    Distribution1D(const float *f, int n) {
        count = n;
        func = new float[n];
        memcpy(func, f, n*sizeof(float));
        cdf = new float[n+1];
        // Compute integral of step function at $x_i$
        cdf[0] = 0.;
        for (int i = 1; i < count+1; ++i)
            cdf[i] = cdf[i-1] + func[i-1] / n;

        // Transform step function integral into CDF
        funcInt = cdf[count];
        if (funcInt == 0.f) {
            for (int i = 1; i < n+1; ++i)
                cdf[i] = float(i) / float(n);
        }
        else {
            for (int i = 1; i < n+1; ++i)
                cdf[i] /= funcInt;
        }
    }

float *func, *cdf;
float funcInt;
int count;

作用:

(在构造函数里面,首先传入  分段常数函数 f,然后计算函数的CDF,之后再计算函数的积分)

The Distribution1D constructor takes n values of a piecewise-constant function f. It
makes its own copy of the function values, computes the function’s CDF, and also stores
the integral of the function, funcInt. Note that the constructor allocates n+1 floats for the
cdf array because if f (x) has N step values, then we need to store the value of the CDF
at each of the N + 1 values of xi . Storing the CDF value of 1 at the end of the array is
redundant but simplifies the sampling code later.

 

细节

a. 

        cdf[0] = 0.;
        for (int i = 1; i < count+1; ++i)
            cdf[i] = cdf[i-1] + func[i-1] / n;

        // Transform step function integral into CDF
        funcInt = cdf[count];

(上面的代码是计算 这个分段常数函数的积分,保存到 funcInt中,在计算积分的时候,思路就是计算 每一段的 面积 再累加起来,每一段的 面积就是 长 * 高 = 1/N * vi, 在这里的PDF其实可以理解为 一段面积占总面积的百分比,那么 CDF 就可以理解为,累加连续的面积 占 总面积的百分比)

Assume that the 1D function’s domain is split into N equal-sized pieces of size  Δ = 1/N.
These regions start and end at points xi = i Δ , where i ranges from 0 to N, inclusive.
Within each region, the value of the function f (x) is a constant (Figure 13.4(a)). The
value of f (x) is

(这里需要注意的是,Δ Vi 其实 Δ * Vi = 1/N * Vi)

 

Figure 13.4: (a) Probability density function for a piecewise-constant 1D function and (b) cumulative
distribution function defined by this PDF.

And so it is easy to construct the PDF p(x) for f (x) as f (x)/c.

 

b. 

for (int i = 1; i < n+1; ++i)
    cdf[i] /= funcInt;

作用:

(这里 把 cdf 的范围 限制在 [0,1]中)

Now that the value of the integral over all of [0, 1] is stored in cdf[n], this value can be
copied into funcInt and the CDF can be normalized by dividing through by it:

 

 

2. float SampleContinuous(float u, float *pdf, int *off = NULL) const

    float SampleContinuous(float u, float *pdf, int *off = NULL) const {
        // Find surrounding CDF segments and _offset_
        float *ptr = std::upper_bound(cdf, cdf+count+1, u);
        int offset = max(0, int(ptr-cdf-1));
        if (off) *off = offset;
        Assert(offset < count);
        Assert(u >= cdf[offset] && u < cdf[offset+1]);

        // Compute offset along CDF segment
        float du = (u - cdf[offset]) / (cdf[offset+1] - cdf[offset]);
        Assert(!isnan(du));

        // Compute PDF for sampled offset
        if (pdf) *pdf = func[offset] / funcInt;

        // Return $x\in{}[0,1)$ corresponding to sample
        return (offset + du) / count;
    }

作用:

(连续采样分段常数函数, 思路:其实这里就是为了把一个u, u是cdf 的结果, 范围是[0,1],匹配到分段常数函数的pdf对应的概率值,也就是像之前《The Inversion Method (2)》文中说的思路一致,  传入的 u 是 某一个 cdf 函数的结果,那么 一开始 就查找 这个 u 在cdf 数组中对应的索引是什么,之后 计算出索引 在整体数组个数的比例,就类似于 之前的 Inversion Method 的思路 )

The Distribution1D::SampleContinuous() method uses the given random sample u to
sample from its distribution. It returns the corresponding value x ∈ [0, 1) and the value
of the PDF p(x).

 

细节

a.

        float *ptr = std::upper_bound(cdf, cdf+count+1, u);
        int offset = max(0, int(ptr-cdf-1));
        if (off) *off = offset;

作用:

(寻找 u 在cdf 数组的 的offset 索引)

This method first finds the pair of CDF values that straddle(跨越) u, setting offset to the offset
to the first of the two of them. Because the cdf array is monotonically(单调) increasing (and is
thus sorted), we can use a binary search function provided by the C++ standard library:
lower_bound() takes a pointer to the start of the array and a pointer one position past
the end of the array as well as the value to search for. Explicitly storing the value 1 at
the end of the array makes it possible to just use lower_bound() directly and not include
additional logic to handle the end-case. In the rare (but valid) case of u == 0, then the
max() test below ensures that the value −1 isn’t incorrectly computed for offset.

 

b. 

float du = (u - cdf[offset]) / (cdf[offset+1] - cdf[offset]);

作用:

Given the pair of CDF values, we can compute x. First, we determine how far u is between
cdf[offset] and cdf[offset+1], du, where du is zero if u == cdf[offset] and goes up to
one if u == cdf[offset+1]. Because the CDF is piecewise linear, the sample value x is the
same offset between xi and xi+1

 

c. 

if (pdf) *pdf = func[offset] / funcInt;

作用:

The PDF for this sample p(x) is easily computed since we have the function’s integral in
funcInt.

 

3. int SampleDiscrete(float u, float *pdf) const

    int SampleDiscrete(float u, float *pdf) const {
        // Find surrounding CDF segments and _offset_
        float *ptr = std::upper_bound(cdf, cdf+count+1, u);
        int offset = max(0, int(ptr-cdf-1));
        Assert(offset < count);
        Assert(u >= cdf[offset] && u < cdf[offset+1]);
        if (pdf) *pdf = func[offset] / (funcInt * count);
        return offset;
    }

作用:

(离散采样分段常数函数)

In a small overloading of semantics(语义), Distribution1D can also be used for discrete 1D
probability distributions where there are some number of buckets(桶) n, each with some
weight, and we’d like to sample among the buckets with probability proportional to their
relative weights. This functionality is used, for example, by some of the Integrators
in Chapter 15 that compute a discrete distribution for the light sources in the scene
with weights given by the lights’ powers. Sampling from the discrete distribution just
requires figuring out which pair of CDF values the sample value lies between; the PDF is
computed as the discrete probability of sampling the corresponding bucket.

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值