wahn / rs_pbrt

Rust crate to implement a counterpart to the PBRT book's (3rd edition) C++ code. See also https://www.rs-pbrt.org/about ...
https://www.rs-pbrt.org
Other
811 stars 59 forks source link

Implement FourierMaterial #61

Closed wahn closed 6 years ago

wahn commented 6 years ago

The test scene ~/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van/vw-van.pbrt uses a fourier material:

$ rg fourier /home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van
/home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van/vw-van.pbrt
55:    "string type" "fourier" "string bsdffile" "bsdfs/ceramic.bsdf"

On the C++ side we have this class:

// FourierMaterial Declarations
class FourierMaterial : public Material {
  public:
    // FourierMaterial Public Methods
    FourierMaterial(const std::string &filename,
                    const std::shared_ptr<Texture<Float>> &bump);
    void ComputeScatteringFunctions(SurfaceInteraction *si, MemoryArena &arena,
                                    TransportMode mode,
                                    bool allowMultipleLobes) const;

  private:
    // FourierMaterial Private Data
    FourierBSDFTable *bsdfTable;
    std::shared_ptr<Texture<Float>> bumpMap;
    static std::map<std::string, std::unique_ptr<FourierBSDFTable>> loadedBSDFs;
};

We have to implement the Rust counterpart ...

wahn commented 6 years ago

Commit ea3f0c7133103a4b921d183b914027f3e96200a4 starts to work on this issue and provides a skeleton for FourierMaterial and FourierBSDFTable.

wahn commented 6 years ago

We have to implement the Rust counterpart to FourierBSDFTable::Read(...) next ...

wahn commented 6 years ago

Let's check the header first (see commit 88b0971a9857718576b41fa7fa50a1f6473b46c2):

$ cd /home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van
$ ~/git/self_hosted/Rust/pbrt/target/release/examples/rs_pbrt -i vw-van.pbrt
...
thread 'main' panicked at 'WORK: Header found', src/core/reflection.rs:60:17
note: Run with `RUST_BACKTRACE=1` for a backtrace.
wahn commented 6 years ago

Since commit ddd1b6cc7ecc75ac2893bd31d6d6dbe4924f62b2 we use the byteorder crate (to read 6 little endian i32 values):

$ cd /home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van
$ ~/git/self_hosted/Rust/pbrt/target/release/examples/rs_pbrt -i vw-van.pbrt
...
WORK: flags = 1
WORK: n_mu = 852
WORK: n_coeffs = 24360150
WORK: m_max = 1599
WORK: n_channels = 3
WORK: n_bases = 1
reading "bsdfs/ceramic.bsdf" returns false
...

With gdb and the C++ version you can confirm those values:

(gdb) p flags
$7 = 1
(gdb) p bsdfTable->nMu
$8 = 852
(gdb) p nCoeffs
$9 = 24360150
(gdb) p bsdfTable->mMax
$10 = 1599
(gdb) p bsdfTable->nChannels
$11 = 3
(gdb) p nBases
$12 = 1
wahn commented 6 years ago

Since commit 363ee05cd35a367aa9eb29cd626096ea99a6c827 we can read the entire *.bsdf file:

$ cd /home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van
$ ~/git/self_hosted/Rust/pbrt/target/release/examples/rs_pbrt -i vw-van.pbrt
...
WORK: flags = 1
WORK: n_mu = 852
WORK: n_coeffs = 24360150
WORK: m_max = 1599
WORK: n_channels = 3
WORK: n_bases = 1
WORK: eta = 1.0
WORK: 852 f32 values read for mu
WORK: 725904 f32 values read for cdf
WORK: 1451808 f32 values read for offset_and_length
WORK: 24360150 f32 values read for a
reading "bsdfs/ceramic.bsdf" returns true
...

But the values stored in FourierBSDFTable are not used yet.

wahn commented 6 years ago

Since commit b33f0f86db4c6b2f06e5e670e64a7dbe87fa881d we store the FourierBSDFTable within the FourierMaterial, but we need a FourierBSDF now:

> rg -tcpp "class FourierBSDF"
core/reflection.h
500:class FourierBSDF : public BxDF {

In C++ the class looks like this:

class FourierBSDF : public BxDF {
  public:
    // FourierBSDF Public Methods
    Spectrum f(const Vector3f &wo, const Vector3f &wi) const;
    FourierBSDF(const FourierBSDFTable &bsdfTable, TransportMode mode)
        : BxDF(BxDFType(BSDF_REFLECTION | BSDF_TRANSMISSION | BSDF_GLOSSY)),
          bsdfTable(bsdfTable),
          mode(mode) {}
    Spectrum Sample_f(const Vector3f &wo, Vector3f *wi, const Point2f &u,
                      Float *pdf, BxDFType *sampledType) const;
    Float Pdf(const Vector3f &wo, const Vector3f &wi) const;
    std::string ToString() const;

  private:
    // FourierBSDF Private Data
    const FourierBSDFTable &bsdfTable;
    const TransportMode mode;
};
wahn commented 6 years ago

Commit ed6ecc598c1394e9d2600c046dfaf7e8a73d9fcc leaves us a couple of functions to implement:

I started to work on FourierBSDF::f(...), needed FourierBSDFTable::get_weights_and_offset(...), and the next task to do is to implement catmull_rom_weights(...):

> rg -tcpp "bool CatmullRomWeights\(" -A 1
core/interpolation.h
48:bool CatmullRomWeights(int size, const Float *nodes, Float x, int *offset,
49-                       Float *weights);

core/interpolation.cpp
61:bool CatmullRomWeights(int size, const Float *nodes, Float x, int *offset,
62-                       Float *weights) {

The C++ counterpart looks like this:

bool CatmullRomWeights(int size, const Float *nodes, Float x, int *offset,
                       Float *weights) {
    // Return _false_ if _x_ is out of bounds
    if (!(x >= nodes[0] && x <= nodes[size - 1])) return false;

    // Search for the interval _idx_ containing _x_
    int idx = FindInterval(size, [&](int i) { return nodes[i] <= x; });
    *offset = idx - 1;
    Float x0 = nodes[idx], x1 = nodes[idx + 1];

    // Compute the $t$ parameter and powers
    Float t = (x - x0) / (x1 - x0), t2 = t * t, t3 = t2 * t;

    // Compute initial node weights $w_1$ and $w_2$
    weights[1] = 2 * t3 - 3 * t2 + 1;
    weights[2] = -2 * t3 + 3 * t2;

    // Compute first node weight $w_0$
    if (idx > 0) {
        Float w0 = (t3 - 2 * t2 + t) * (x1 - x0) / (x1 - nodes[idx - 1]);
        weights[0] = -w0;
        weights[2] += w0;
    } else {
        Float w0 = t3 - 2 * t2 + t;
        weights[0] = 0;
        weights[1] -= w0;
        weights[2] += w0;
    }

    // Compute last node weight $w_3$
    if (idx + 2 < size) {
        Float w3 = (t3 - t2) * (x1 - x0) / (nodes[idx + 2] - x0);
        weights[1] -= w3;
        weights[3] = w3;
    } else {
        Float w3 = t3 - t2;
        weights[1] -= w3;
        weights[2] += w3;
        weights[3] = 0;
    }
    return true;
}
wahn commented 6 years ago

Since commit 4c8b7599de1f8aada30cbb55cb3c5a0bb5c2728f FourierBSDF::f(...) is finished, but there are still two functions left to do:

  1. FourierBSDF::sample_f(...)
  2. FourierBSDF::pdf(...)
wahn commented 6 years ago

In theory all the Bxdf functions are fully implemented for FourierBSDF (since commit fe8752a03435ddad6393904b7f993b506a838499), but we have to debug why the scene panics:

$ pwd
/home/jan/git/gitlab/rs-pbrt-test-scenes/pbrt/pbrt_vw_van
$ ~/git/self_hosted/Rust/pbrt/target/release/examples/rs_pbrt -i vw-van.pbrt
pbrt version 0.4.2 [Detected 4 cores]
...
thread '<unnamed>' panicked at 'assertion failed: !ret.has_nans()', src/core/spectrum.rs:1622:9
wahn commented 6 years ago

Closing the ticket after commit e7ab8e5036971f0a9bd7da2c8d2f7723d76459e3 removes the source of the panic.