Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/laurent-lu/TMbLM-Rendering
21 October 2023, 05:38:44 UTC
  • Code
  • Branches (1)
  • Releases (0)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/main
    No releases to show
  • 522c671
  • /
  • plugins
  • /
  • tm.h
Raw File Download
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge Iframe embedding
swh:1:cnt:d86ea70000b77887856b6a527178d3aa18d4f5b9
origin badgedirectory badge Iframe embedding
swh:1:dir:15c9b846643926e0283b9923373032f2f8d50600
origin badgerevision badge
swh:1:rev:26975e6f47f8c0b63f37e5e3f9c90d051ac25209
origin badgesnapshot badge
swh:1:snp:a276e74c1cc68176a285facfb42032a61eefde1b
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: 26975e6f47f8c0b63f37e5e3f9c90d051ac25209 authored by Joël Randrianandrasana on 20 September 2021, 14:27:13 UTC
* README: Windows 10 build instructions: Added note related to Mitsuba's GUI issue
Tip revision: 26975e6
tm.h
#ifndef _TM_
#define _TM_

#include <mitsuba/core/fresolver.h>
#include <mitsuba/render/bsdf.h>
#include <mitsuba/hw/basicshader.h>

#include "ior.h"
#include "microfacet.h"

#define TM_LAYER_COUNT_MAX 5
#define TM_EXP_ARG_MAX 45.f

#define CLAMP(v, vmin, vmax) ((std::max)((std::min)((v), (vmax)), (vmin)))

MTS_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////
// XML parsing and generic LUT class
//
// Code adapted from the supplemental code provided with "Efficient Rendering of
// Layered Materials using an Atomic Decomposition with Statistical Operators"
// Laurent Belcour SIGGRAPH 2018 paper
//
////////////////////////////////////////////////////////////////////////////////

void parseLayers(const Properties &props,
                 int& nb_layers,
                 std::vector<ref<const Texture>>& tex_etas,
                 std::vector<ref<const Texture>>& tex_kappas,
                 std::vector<ref<const Texture>>& tex_alphas,
                 bool opaque_material) {

    nb_layers = props.getInteger("nb_layers", 0);

    float extEta = lookupIOR(props, "extEta", "air");

    tex_etas.push_back(new ConstantSpectrumTexture(Spectrum(extEta)));
    tex_kappas.push_back(new ConstantSpectrumTexture(Spectrum(0.f)));
    tex_alphas.push_back(new ConstantSpectrumTexture(Spectrum(0.f)));

    for(int k=0; k<nb_layers; ++k) {

        std::string index = std::to_string(k);
        std::string name;

        name = std::string("eta_") + index;
        Spectrum eta = props.getSpectrum(name, Spectrum(extEta));

        name = std::string("kappa_") + index;
        Spectrum kappa = props.getSpectrum(name, Spectrum(0.f));

        // Check structure integrity
        if (!kappa.isZero()) {
            if (opaque_material) {
                if (k < (nb_layers - 1))
                    throw std::logic_error("Opaque material: Conductive layer allowed only as substrate");
            }
            else
                throw std::logic_error("Transparent material: Conductive layer not allowed");
        }
        else if (opaque_material && k == (nb_layers - 1))
            throw std::logic_error("Opaque material: Conductive substrate expected");

        name = std::string("alpha_") + index;
        float alpha = props.getFloat(name, 0.f);

        tex_etas.push_back(new ConstantSpectrumTexture(eta));
        tex_kappas.push_back(new ConstantSpectrumTexture(kappa));
        tex_alphas.push_back(new ConstantFloatTexture(alpha));

    }

    SLog(EInfo, "");
    SLog(EInfo, "Adding Exterior IOR");
    SLog(EInfo, " + n = %s", tex_etas[0]->getAverage().toString().c_str());
    SLog(EInfo, " + k = %s", tex_kappas[0]->getAverage().toString().c_str());
    SLog(EInfo, " + a = %f", tex_alphas[0]->getAverage().average());
    SLog(EInfo, "");

    for(int k=0; k<nb_layers; ++k) {

        SLog(EInfo, "Adding layer %d", k);
        SLog(EInfo, " + n = %s", tex_etas[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + k = %s", tex_kappas[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + a = %f", tex_alphas[k+1]->getAverage().average());
        SLog(EInfo, "");
    }
}
void parseLayers(const Properties &props,
                 int& nb_layers,
                 std::vector<ref<const Texture>>& tex_etas,
                 std::vector<ref<const Texture>>& tex_kappas,
                 std::vector<ref<const Texture>>& tex_alphas,
                 std::vector<ref<const Texture>>& tex_depths,
                 std::vector<ref<const Texture>>& tex_sigmas_s,
                 std::vector<ref<const Texture>>& tex_sigmas_k,
                 std::vector<ref<const Texture>>& tex_gs) {

    nb_layers = props.getInteger("nb_layers", 0);

    tex_etas.push_back(new ConstantSpectrumTexture(Spectrum(lookupIOR(props, "extEta", "air"))));
    tex_kappas.push_back(new ConstantSpectrumTexture(Spectrum(0.0)));
    tex_alphas.push_back(new ConstantFloatTexture(0.0));

    tex_depths.push_back(new ConstantFloatTexture(0.0));
    tex_sigmas_s.push_back(new ConstantSpectrumTexture(Spectrum(0.0)));
    tex_sigmas_k.push_back(new ConstantSpectrumTexture(Spectrum(0.0)));
    tex_gs.push_back(new ConstantFloatTexture(0.0));

    for(int k=0; k<nb_layers; ++k) {

        std::string index = std::to_string(k);
        std::string name;

        name = std::string("eta_") + index;
        Spectrum eta = props.getSpectrum(name, Spectrum(0.f));
        if (eta == Spectrum(0.f))
            eta = tex_etas.back()->getAverage();

        name = std::string("kappa_") + index;
        Spectrum kappa = props.getSpectrum(name, Spectrum(0.f));

        // Check structure integrity
        if (!kappa.isZero()) {
            if (k < (nb_layers - 1))
                throw std::logic_error("TM6: Conductive layer allowed only as substrate");
        }
        else if (k == (nb_layers - 1)) {
            throw std::logic_error("TM6: Conductive substrate expected");
        }

        name = std::string("alpha_") + index;
        float alpha = props.getFloat(name, 0.f);

        name = std::string("depth_") + index;
        float depth = props.getFloat(name, 0.f);

        name = std::string("sigmas_") + index;
        Spectrum sigma_s = props.getSpectrum(name, Spectrum(0.f));

        // Force non null absorption values
        name = std::string("sigmak_") + index;
        Spectrum sigma_k = props.getSpectrum(name, Spectrum(0.f));

        name = std::string("g_") + index;
        float g = props.getFloat(name, 1.f);

        tex_etas.push_back(new ConstantSpectrumTexture(eta));
        tex_kappas.push_back(new ConstantSpectrumTexture(kappa));
        tex_alphas.push_back(new ConstantFloatTexture(alpha));

        tex_depths.push_back(new ConstantFloatTexture(depth));
        tex_sigmas_s.push_back(new ConstantSpectrumTexture(sigma_s));
        tex_sigmas_k.push_back(new ConstantSpectrumTexture(sigma_k));
        tex_gs.push_back(new ConstantFloatTexture(g));
    }

    SLog(EInfo, "");
    SLog(EInfo, "Adding Exterior IOR");
    SLog(EInfo, " + n = %s", tex_etas[0]->getAverage().toString().c_str());
    SLog(EInfo, " + k = %s", tex_kappas[0]->getAverage().toString().c_str());
    SLog(EInfo, " + a = %f", tex_alphas[0]->getAverage().average());
    SLog(EInfo, "");

    for(int k=0; k<nb_layers; ++k) {

        SLog(EInfo, "Adding layer %d", k);

        SLog(EInfo, " + n = %s", tex_etas[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + k = %s", tex_kappas[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + a = %f", tex_alphas[k+1]->getAverage().average());
        SLog(EInfo, "");
        SLog(EInfo, " + d  = %f", tex_depths[k+1]->getAverage().average());
        SLog(EInfo, " + ss = %s", tex_sigmas_s[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + sk = %s", tex_sigmas_k[k+1]->getAverage().toString().c_str());
        SLog(EInfo, " + g  = %f", tex_gs[k+1]->getAverage().average());
        SLog(EInfo, "");
    }
}

struct lut_range {

	float min;
	float max;

	lut_range(float _min = std::numeric_limits<float>::quiet_NaN(),
			  float _max = std::numeric_limits<float>::quiet_NaN()) {

		min = _min;
		max = _max;
	}

	bool operator!=(const lut_range& other) const {

		return min != other.min || max != other.max;
	}
};

template<int LutDimension, typename LutDataType>
class lut {
private:

    // Data size
    int _size[LutDimension];
    // Data range
    lut_range _range[LutDimension];
    // Data
    std::vector<LutDataType> _data;

public:

    lut() { }
    lut(const std::string& filename) {

        std::ifstream in(filename, std::ios_base::in | std::ios_base::binary);

        // Warn if not loaded
        if (in.bad() || in.fail())
            fprintf(stderr, "Unable to load %dD LUT data file: %s\n", LutDimension, filename.c_str());
        else
            fprintf(stdout, "Loading %dD LUT data file: %s\n", LutDimension, filename.c_str());

        this->load(in);
    }
    lut(const int size[], const lut_range range[]) {

        for (int i = 0; i < LutDimension; ++i) {

            _size[i] = size[i];
            _range[i] = range[i];
        }

        _data.assign(linear_size(), LutDataType());
    }

    const int* size() const { return _size; }
    constexpr int dimensions() const { return LutDimension; }
    const lut_range* range() const { return _range; }

    LutDataType* data() { return _data.data(); }
    const LutDataType* data() const { return _data.data(); }

    std::string size_string() const {

        std::ostringstream oss;

        for (int i = 0; i < LutDimension; ++i) {
            oss << _size[i];
            if (i < (LutDimension - 1))
                oss << "x";
        }

        return oss.str();
    }
    std::string range_string() const {

        std::ostringstream oss;
        for (int i = 0; i < LutDimension; ++i) {

            oss << "[" << _range[i].min << "," << _range[i].max << "]";
            if (i < (LutDimension - 1))
                oss << "x";
        }
        return oss.str();
    }

    int linear_size() const {

        int lsize = 1;
        for (int i = 0; i < LutDimension; ++i)
            lsize *= _size[i];
        return lsize;
    }
    int linear_index_from_grid_index(const int grid_index[]) const {

        int index = grid_index[LutDimension - 1];
        for (int i = LutDimension - 2; i >= 0; --i)
            index = index * _size[i] + grid_index[i];
        return index;
    }

    void load(std::ifstream& in) {

        // Read size
        for (int i = 0; i < LutDimension; ++i)
            in.read((char*)&_size[i], sizeof(int));

        fprintf(stdout, "Loading %dD LUT of dimensions %s\n", LutDimension, size_string().c_str());

        // Read data range (min / max)
        for (int i = 0; i < LutDimension; ++i) {

            in.read((char*)&_range[i].min, sizeof(float));
            in.read((char*)&_range[i].max, sizeof(float));
        }

        fprintf(stdout, "Loading %dD LUT data range: %s\n", LutDimension, range_string().c_str());

        // Read data
        _data.assign(linear_size(), LutDataType());
        in.read((char*)_data.data(), linear_size() * sizeof(LutDataType));
    }

    template<typename... T>
    inline LutDataType range_get(T... vrange_coords) const {

        float	x[LutDimension] = { vrange_coords... };
        int		i[LutDimension];

        for (int k = 0; k < LutDimension; ++k) {

            // Integer index (ensure the index stays in the limits)
            i[k] = (int)floor((_size[k] - 1) * (x[k] - _range[k].min) / (_range[k].max - _range[k].min));
            i[k] = CLAMP(i[k], 0, _size[k] - 1);
        }

        return _data[linear_index_from_grid_index(i)];
    }
    template<typename... T>
    inline LutDataType range_get_interpolate(T... vrange_coords) const {

        float	x[LutDimension] = { vrange_coords... };
        float	a[LutDimension];
        int		i[LutDimension];
        float	alphas[LutDimension];

        for (int k = 0; k < LutDimension; ++k) {

            // Floating point index
            a[k] = (_size[k] - 1) * (x[k] - _range[k].min) / (_range[k].max - _range[k].min);

            // Integer index
            i[k] = (int)floor(a[k]);

            // Ensure the indexes stays in the limits
            i[k] = CLAMP(i[k], 0, _size[k] - 1);

            // Clamp the interpolation weights
            alphas[k] = CLAMP(a[k] - (float)i[k], 0.f, 1.f);
        }

        int index = linear_index_from_grid_index(i);

        // Lookup result
        LutDataType v = LutDataType();

        // For every possible combinaison of index shift per dimension,
        // fetch the value in memory and do linear interpolation.
        // We fetch using shift of 0 and 1.
        //
        //     v(i+di, j+di, k+dk, l+dl),  where dk in [0,1]
        //
        const unsigned int D = (unsigned int)pow(2, LutDimension);
        for (unsigned int d = 0; d < D; ++d) {

            float alpha = 1.0; // Global alpha
            int   cid_s = 0;   // Id shift

            // Evaluate the weight of the sample d which correspond to
            // one for the shifted configuration:
            // The weight is the product of the weights per dimension.
            for (int k = (LutDimension - 1); k >= 0; --k) {

                bool  bitset = ((1 << k) & d);
                float calpha = (bitset) ? alphas[k] : 1.f - alphas[k];

                // Correct the shift to none if we go out of the grid
                if (i[k] + 1 >= _size[k])
                    bitset = false;

                alpha *= calpha;
                cid_s = cid_s * _size[k] + ((bitset) ? 1 : 0);
            }

            v += alpha * _data[index + cid_s];
        }

        return v;
    }
};

typedef lut<2, float> lut2;
typedef lut<3, float> lut3;
typedef lut<4, float> lut4;

////////////////////////////////////////////////////////////////////////////////
// Common routines
////////////////////////////////////////////////////////////////////////////////

static inline Vector3 reflectZ(const Vector3& v) {

    return Vector3(-v.x, -v.y, v.z);
}
static inline Vector3 refractZ(const Vector3& v, Float eta) {

    return refract(v, Vector3(0.f, 0.f,std::copysign(1.f, v.z)), eta);
}

static inline Spectrum min(const Spectrum& s, Float v) {

    Spectrum r;
    for (int i = 0; i < Spectrum::dim; ++i)
        r[i] = std::min(s[i], v);
    return r;
}
static inline Spectrum max(const Spectrum& s, Float v) {

    Spectrum r;
    for (int i = 0; i < Spectrum::dim; ++i)
        r[i] = std::max(s[i], v);
    return r;
}
static inline Spectrum exp(const Spectrum& s) {

    Spectrum r;
    for (int i = 0; i < Spectrum::dim; ++i)
        r[i] = std::exp(s[i]);
    return r;
}
static inline Spectrum sinh(const Spectrum& s) {

    Spectrum r;
    for (int i = 0; i < Spectrum::dim; ++i)
        r[i] = std::sinh(s[i]);
    return r;
}
static inline Spectrum safe_div(const Spectrum& n, const Spectrum& d) {

    Spectrum r;
    for (int i = 0; i < Spectrum::dim; ++i)
        r[i] = std::abs(d[i]) > 1e-7f ? n[i] / d[i] : 0.f;
    return r;
}

////////////////////////////////////////////////////////////////////////////////
// Henyey-Greenstein
////////////////////////////////////////////////////////////////////////////////

struct hg {

    // Asymmetry parameter
    Float g;
    // Mean
    Vector3 mean;
    // Norm
    Spectrum norm;

    hg(Float _g = 0.f, const Vector3& _mean = Vector3(0.f), const Spectrum& _norm = Spectrum(0.f))
        : g(_g), mean(_mean), norm(_norm) { }
};

static inline Float hg_from_ggx(Float a) {
    return CLAMP(-0.085f + (1.f + 0.085f)/(1.f + std::pow(std::min(CLAMP(a, 0.f, 1.f), 1.f)/0.5f, 1.3f)), 0.f, 1.f);
}
static inline Float hg_to_ggx(Float g) {
    return CLAMP(0.5f * std::pow((1.f + 0.085f)/(std::max(CLAMP(g, -1.f, 1.f), 0.2f) + 0.085f) - 1.f, 1.f / 1.3f), 1e-4f, 1.f);
}
static inline Float hg_refract(Float g, Float eta) {

  return std::min(std::sqrt(std::max(1.f - (1.f - g * g) * std::pow(eta, 0.75f), 0.f)), 1.f);
}
static inline Float hg_lh_norm(Float g) {

    const bool g_neg = g < 0.f;
    g = std::abs(g);
    const Float n = CLAMP(0.5039f - 0.8254f * g + 0.3226f * g * g, 0.f, 0.5f);
    return g_neg ? 1.f - n : n;
}

////////////////////////////////////////////////////////////////////////////////
// Albedos
////////////////////////////////////////////////////////////////////////////////

static inline void albedo(const Float cti, const Float alpha,
                          const Spectrum& etas_ij, const Spectrum& kappas_ij,
                          Spectrum& r_ij,
                          const lut4& FGD) {

    for (int i = 0; i < SPECTRUM_SAMPLES; ++i)
        r_ij[i] = FGD.range_get_interpolate(cti, alpha, etas_ij[i], kappas_ij[i]);
}
static inline void albedos(const Float cti, const Float alpha, const Float eta_ij,
                           Spectrum& r_ij, Spectrum& t_ij, Spectrum& r_ji, Spectrum& t_ji,
                           const lut4& FGD) {

    // No IoR change
    if (std::abs(eta_ij - 1.f) < 1e-3f) {

        r_ij = r_ji = Spectrum(0.f);
        t_ij = t_ji = Spectrum(1.f);
        return;
    }

    r_ij = r_ji = Spectrum(FGD.range_get_interpolate(cti, alpha, eta_ij));
    t_ij = t_ji = Spectrum(1.f) - r_ij;
}

////////////////////////////////////////////////////////////////////////////////
// Common
////////////////////////////////////////////////////////////////////////////////

enum EComponentType : unsigned char {

    EUndefinedComponent,
    ENoComponent,
    EDielectricInterface,
    EConductorInterface,
    EHomogeneousMedium
};

////////////////////////////////////////////////////////////////////////////////
// 2-flux transfer calculus
////////////////////////////////////////////////////////////////////////////////

struct component_factors_2 {

    EComponentType type;

    hg r;     // Downward reflection
    hg t;     // Downward transmission
    hg rp;    // Upward reflection
    hg tp;    // Upward transmission

    component_factors_2(EComponentType type_ = EUndefinedComponent): type(type_) { }
};

template<typename Type>
struct transfer_matrix_2 {

    Type _m11, _m12;
    Type _m21, _m22;

    transfer_matrix_2():    _m11(Type(0.f)), _m12(Type(0.f)),
                            _m21(Type(0.f)), _m22(Type(0.f)) { }

    Type r() const { return _m21 / _m11; }
    Type t() const { return Type(1.f) / _m11; }
    
    Type r(const Type& r_cond) const {
        
        return (_m21 + _m22 * r_cond) / (_m11 + _m12 * r_cond);
    }

    inline transfer_matrix_2 operator*(const transfer_matrix_2& tm) const {

        transfer_matrix_2 res;

        res._m11 = _m11 * tm._m11 + _m12 * tm._m21;
        res._m12 = _m11 * tm._m12 + _m12 * tm._m22;
        
        res._m21 = _m21 * tm._m11 + _m22 * tm._m21;
        res._m22 = _m21 * tm._m12 + _m22 * tm._m22;

        return res;
    }
    inline transfer_matrix_2& operator*=(const transfer_matrix_2& tm) {

        *this = *this * tm;
        return *this;
    }

    static inline transfer_matrix_2 identity() {

        transfer_matrix_2 id;

        id._m11 = id._m22 = Type(1.f);

        return id;
    }
};

typedef transfer_matrix_2<Float>    transfer_matrix_21;
typedef transfer_matrix_2<Spectrum> transfer_matrix_2s;

static inline transfer_matrix_2s energy_matrix(const component_factors_2& ops) {

    transfer_matrix_2s etm;
    
    // Matrix factor
    const Spectrum t_inv = Spectrum(1.f) / ops.t.norm;

    // Matrix entries
    etm._m11 = t_inv;
    etm._m12 = -ops.rp.norm * t_inv;

    etm._m21 = ops.r.norm * t_inv;
    etm._m22 = (-ops.r.norm * ops.rp.norm + ops.t.norm * ops.tp.norm) * t_inv;

    return etm;
}
static inline transfer_matrix_21 asymmetry_matrix(const component_factors_2& ops) {

    transfer_matrix_21 gtm;

    // Weighted asymmetry parameters
    const Float r_g = ops.r.g * ops.r.norm.average();
    const Float t_g = ops.t.g * ops.t.norm.average();
    const Float rp_g = ops.rp.g * ops.rp.norm.average();
    const Float tp_g = ops.tp.g * ops.tp.norm.average();
    
    // Matrix factor
    const Float t_inv = 1.f / t_g;

    // Matrix entries
    gtm._m11 = t_inv;
    gtm._m12 = -rp_g * t_inv;

    gtm._m21 = r_g * t_inv;
    gtm._m22 = (-r_g * rp_g + t_g * tp_g) * t_inv;

    return gtm;
}

static inline void dielectric_transfer_factors(const Vector3& wl,
                                               const Float eta_ij,
                                               const Float alpha,
                                               const lut4& FGD,
                                               component_factors_2& ops) {

    Float eta_ji;
    Float s_t_ij, s_t_ji;

    // No IoR change
    if (std::abs(eta_ij - 1.f) < 1e-5f) {

        ops.type = ENoComponent;
        ops.t.mean = -wl;

        return;
    }

    // Component type
    ops.type = EDielectricInterface;

    // Inverse IoR
    eta_ji = 1.f / eta_ij;

    // Downward reflection
    ops.r.g = hg_from_ggx(alpha);
    ops.r.mean = reflectZ(wl);

    // Upward reflection
    ops.rp.g = ops.r.g;
    ops.rp.mean = refractZ(wl, eta_ij);

    // Fake rough transmissions scaling factors
    s_t_ij = std::abs((eta_ji * ops.r.mean.z + ops.rp.mean.z) / ops.rp.mean.z);
    s_t_ji = std::abs((eta_ij * ops.rp.mean.z + ops.r.mean.z) / ops.r.mean.z);

    // Downward transmission (fake reflection)
    ops.t.g = hg_from_ggx(0.5f * s_t_ij * alpha);
    ops.t.mean = ops.rp.mean;

    // Upward transmission (fake reflection)
    ops.tp.g = hg_from_ggx(0.5f * s_t_ji * alpha);
    ops.tp.mean = ops.r.mean;

    // Albedos
    albedos(std::abs(wl.z), alpha, eta_ij, ops.r.norm, ops.t.norm, ops.rp.norm, ops.tp.norm, FGD);
}
static inline void conductor_transfer_factors(const Vector3& wl,
                                              const Spectrum& etas,
                                              const Spectrum& kappas,
                                              const Float alpha,
                                              const lut4& FGD,
                                              component_factors_2& ops) {

    // Component type
    ops.type = EConductorInterface;

    // Downward reflection
    ops.r.g = hg_from_ggx(alpha);
    ops.r.mean = reflectZ(wl);

    // Albedo
    albedo(std::abs(wl.z), alpha, etas, kappas, ops.r.norm, FGD);
}

////////////////////////////////////////////////////////////////////////////////
// 6-flux transfer calculus
////////////////////////////////////////////////////////////////////////////////

struct component_factors_6 {

    EComponentType type;

    union {

        // Interface transfer factors
        struct {

            hg r;       // Primary downward reflection
            hg t;       // Primary downward transmission
            hg rp;      // Primary upward reflection
            hg tp;      // Primary upward transmission

            hg r_ff;    // Secondary downward reflection
            hg t_ff;    // Secondary downward transmission
            hg rp_ff;   // Secondary upward reflection
            hg tp_ff;   // Secondary upward transmission
        } i;
        // Homogeneous participating medium transfer factors
        struct {

            hg t;     // Primary flux transmission
            hg r_fb;  // Secondary flux backward reflection
            hg t_ff;  // Secondary flux forward transmission
        } m;
    };

    component_factors_6(EComponentType type_ = EUndefinedComponent): type(type_) { }
};

enum ETransferFactorRequest : unsigned char {

    ENone                           = 0x00,
    EPrimaryReflectance             = 0x01,
    ESecondaryForwardReflectance    = 0x02,
    ESecondaryBackwardReflectance   = 0x04
};

template<typename Type>
struct transfer_matrix_6 {

    EComponentType type;

    Type m11, m12;
    Type m21, m22;
    Type m31, m32, m33, m34, m35, m36;
    Type m41, m42, m43, m44, m45, m46;
    Type m51, m52;
    Type m61, m62;

    transfer_matrix_6()
     : type(EUndefinedComponent),
       m11(Type(0.f)), m12(Type(0.f)),
       m21(Type(0.f)), m22(Type(0.f)),
       m31(Type(0.f)), m32(Type(0.f)), m33(Type(0.f)), m34(Type(0.f)), m35(Type(0.f)), m36(Type(0.f)),
       m41(Type(0.f)), m42(Type(0.f)), m43(Type(0.f)), m44(Type(0.f)), m45(Type(0.f)), m46(Type(0.f)),
       m51(Type(0.f)), m52(Type(0.f)),
       m61(Type(0.f)), m62(Type(0.f)) { }

    static inline transfer_matrix_6 identity() {

        transfer_matrix_6 id;

        id.type = ENoComponent;
        id.m11 = id.m22 = id.m33 = id.m44 = Type(1.f);

        return id;
    }

    inline transfer_matrix_6 operator*(const transfer_matrix_6& tm) const {

        transfer_matrix_6 res;

        // Identity
        if (type == ENoComponent)
            return tm;

        if (tm.type == ENoComponent) {

            return *this;
        }
        else if (tm.type == EDielectricInterface) {

            res.m11 = m11*tm.m11 + m12*tm.m21;
            res.m12 = m11*tm.m12 + m12*tm.m22;

            res.m21 = m21*tm.m11 + m22*tm.m21;
            res.m22 = m21*tm.m12 + m22*tm.m22;

            res.m31 = m31*tm.m11 + m32*tm.m21;
            res.m32 = m31*tm.m12 + m32*tm.m22;
            res.m33 = m33*tm.m33 + m34*tm.m43;
            res.m34 = m33*tm.m34 + m34*tm.m44;
            res.m35 = m35*tm.m33 + m36*tm.m43;
            res.m36 = m35*tm.m34 + m36*tm.m44;

            res.m41 = m41*tm.m11 + m42*tm.m21;
            res.m42 = m41*tm.m12 + m42*tm.m22;
            res.m43 = m43*tm.m33 + m44*tm.m43;
            res.m44 = m43*tm.m34 + m44*tm.m44;
            res.m45 = m45*tm.m33 + m46*tm.m43;
            res.m46 = m45*tm.m34 + m46*tm.m44;

            res.m51 = m51*tm.m11 + m52*tm.m21;
            res.m52 = m51*tm.m12 + m52*tm.m22;

            res.m61 = m61*tm.m11 + m62*tm.m21;
            res.m62 = m61*tm.m12 + m62*tm.m22;
        }
        else if (tm.type == EHomogeneousMedium) {

            res.m11 = m11*tm.m11;
            res.m12 = m12*tm.m22;

            res.m21 = m21*tm.m11;
            res.m22 = m22*tm.m22;

            res.m31 = m31*tm.m11 + m33*tm.m31 - m36*tm.m36;
            res.m32 = m32*tm.m22 + m34*tm.m42 + m35*tm.m36;
            res.m33 = m33*tm.m33 - m36*tm.m36;
            res.m34 = m34*tm.m44 + m35*tm.m36;
            res.m35 = -m34*tm.m36 + m35*tm.m33;
            res.m36 = m33*tm.m36 + m36*tm.m44;

            res.m41 = m41*tm.m11 + m43*tm.m31 - m46*tm.m36;
            res.m42 = m42*tm.m22 + m44*tm.m42 + m45*tm.m36;
            res.m43 = m43*tm.m33 - m46*tm.m36;
            res.m44 = m44*tm.m44 + m45*tm.m36;
            res.m45 = -m44*tm.m36 + m45*tm.m33;
            res.m46 = m43*tm.m36 + m46*tm.m44;

            res.m51 = -m34*tm.m36 + m35*tm.m31 + m51*tm.m11;
            res.m52 = m33*tm.m36 + m36*tm.m42 + m52*tm.m22;

            res.m61 = -m44*tm.m36 + m45*tm.m31 + m61*tm.m11;
            res.m62 = m43*tm.m36 + m46*tm.m42 + m62*tm.m22;
        }
        else {
            throw std::logic_error("Operation not supported");
        }

        return res;
    }
    inline transfer_matrix_6& operator*=(const transfer_matrix_6& tm) {

        *this = *this * tm;
        return *this;
    }

    inline void factors(Type& r, Type& r_f, Type& r_b, unsigned char req) const {

        const Type x0 = Type(1.f)/m11;

        if (req & ESecondaryBackwardReflectance) {

            const Type x1 = -m31*m33 + m35*m51;
            const Type x2 = m31*m35 - m33*m51;
            const Type x3 = x0/(m33*m33 - m35*m35);
            const Type x4 = m43*x3;
            const Type x5 = m45*x3;

            r_b = m61*x0 + x1*x5 + x4*x2;
            if (req & ESecondaryForwardReflectance)
                r_f = m41*x0 + x1*x4 + x2*x5;
        }

        if (req & EPrimaryReflectance)
            r = m21*x0;
    }
    inline void factors(Type& r, Type& r_f, Type& r_b, const Type& r_cond, unsigned char req) const {

        const Type x0 = Type(1.f)/(m11 + m12*r_cond);

        if (req & ESecondaryBackwardReflectance) {

            const Type x1 = m45 + m46*r_cond;
            const Type x2 = m31 + m32*r_cond;
            const Type x3 = m35 + m36*r_cond;
            const Type x4 = m33 + m34*r_cond;
            const Type x5 = m51 + m52*r_cond;
            const Type x6 = x0/(-x3*x3 + x4*x4);
            const Type x7 = x6*(x2*x3 - x4*x5);
            const Type x8 = m43 + m44*r_cond;
            const Type x9 = x6*(x2*x4 - x3*x5);

            r_b = x0*(m61 + m62*r_cond) - x1*x9 + x7*x8;
            if (req & ESecondaryForwardReflectance)
                r_f = x0*(m41 + m42*r_cond) + x1*x7 - x8*x9;
        }

        if (req & EPrimaryReflectance)
            r = x0*(m21 + m22*r_cond);
    }
};

typedef transfer_matrix_6<Spectrum> transfer_matrix_6s;

static inline transfer_matrix_6s energy_matrix(const component_factors_6& ops) {

    transfer_matrix_6s etm;

    etm.type = ops.type;

    if (ops.type == EDielectricInterface) {

        etm.m11 = Spectrum(1.f) / ops.i.t.norm;
        etm.m12 = -ops.i.rp.norm * etm.m11;
        etm.m21 = ops.i.r.norm * etm.m11;
        etm.m22 = -ops.i.r.norm * ops.i.rp.norm * etm.m11 + ops.i.tp.norm;

        etm.m33 = Spectrum(1.f) / ops.i.t_ff.norm;
        etm.m34 = -ops.i.rp_ff.norm * etm.m33;
        etm.m43 = ops.i.r_ff.norm * etm.m33;
        etm.m44 = -ops.i.r_ff.norm * ops.i.rp_ff.norm * etm.m33 + ops.i.tp_ff.norm;
    }
    else if (ops.type == EHomogeneousMedium) {

        etm.m11 = Spectrum(1.f) / ops.m.t.norm;
        etm.m22 = ops.m.t.norm;
        etm.m33 = Spectrum(1.f) / ops.m.t_ff.norm;
        etm.m44 = -(ops.m.r_fb.norm * ops.m.r_fb.norm) * etm.m33 + ops.m.t_ff.norm;
        etm.m52 = etm.m36 = -ops.m.r_fb.norm * etm.m33;
        etm.m31 = etm.m33 - etm.m11;
        etm.m42 = etm.m44 - ops.m.t.norm;
        etm.m61 = etm.m45 = -etm.m36;
    }
    else {
        throw std::logic_error("Operation not supported");
    }

    return etm;
}
static inline transfer_matrix_6s asymmetry_matrix(const component_factors_6& ops) {

    transfer_matrix_6s gtm;

    gtm.type = ops.type;

    if (ops.type == EDielectricInterface) {

        const Spectrum r_g     = ops.i.r.norm  * ops.i.r.g;
        const Spectrum t_g     = ops.i.t.norm  * ops.i.t.g;
        const Spectrum rp_g    = ops.i.rp.norm * ops.i.rp.g;
        const Spectrum tp_g    = ops.i.tp.norm * ops.i.tp.g;

        const Spectrum r_ff_g   = ops.i.r_ff.norm  * ops.i.r_ff.g;
        const Spectrum t_ff_g   = ops.i.t_ff.norm  * ops.i.t_ff.g;
        const Spectrum rp_ff_g  = ops.i.rp_ff.norm * ops.i.rp_ff.g;
        const Spectrum tp_ff_g  = ops.i.tp_ff.norm * ops.i.tp_ff.g;

        gtm.m11 = Spectrum(1.f) / t_g;
        gtm.m12 = -rp_g * gtm.m11;
        gtm.m21 = r_g * gtm.m11;
        gtm.m22 = -r_g * rp_g * gtm.m11 + tp_g;

        gtm.m33 = Spectrum(1.f) / t_ff_g;
        gtm.m34 = -rp_ff_g * gtm.m33;
        gtm.m43 = r_ff_g * gtm.m33;
        gtm.m44 = -r_ff_g * rp_ff_g * gtm.m33 + tp_ff_g;
    }
    else if (ops.type == EHomogeneousMedium) {

        const Spectrum m_t_g       = ops.m.t.norm    * ops.m.t.g;
        const Spectrum m_r_fb_g    = ops.m.r_fb.norm * ops.m.r_fb.g;
        const Spectrum m_t_ff_g    = ops.m.t_ff.norm * ops.m.t_ff.g;

        gtm.m11 = Spectrum(1.f) / m_t_g;
        gtm.m22 = m_t_g;
        gtm.m33 = Spectrum(1.f) / m_t_ff_g;
        gtm.m44 = -(m_r_fb_g * m_r_fb_g) * gtm.m33 + m_t_ff_g;
        gtm.m52 = gtm.m36 = -m_r_fb_g * gtm.m33;
        gtm.m31 = gtm.m33 - gtm.m11;
        gtm.m42 = gtm.m44 - m_t_g;
        gtm.m61 = gtm.m45 = -gtm.m36;
    }
    else {
        throw std::logic_error("Operation not supported");
    }

    return gtm;
}

static inline void dielectric_transfer_factors(const Vector3& wl,
                                               const Float eta_ij,
                                               const Float alpha,
                                               const lut4& FGD,
                                               component_factors_6& ops) {

	Float eta_ji;
    Float s_t_ij, s_t_ji;

    // No IoR change
    if (std::abs(eta_ij - 1.f) < 1e-5f) {

        ops.type = ENoComponent;
        ops.i.t.mean = -wl;

        return;
    }

    // Component type
    ops.type = EDielectricInterface;

    // Inverse IoR
    eta_ji = 1.f / eta_ij;

    // Downward reflection
    ops.i.r.g = hg_from_ggx(alpha);
    ops.i.r.mean = reflectZ(wl);

    // Upward reflection
    ops.i.rp.g = ops.i.r.g;
    ops.i.rp.mean = refractZ(wl, eta_ij);

    // Fake rough transmissions scaling factors
    s_t_ij = std::abs((eta_ji * ops.i.r.mean.z + ops.i.rp.mean.z) / ops.i.rp.mean.z);
    s_t_ji = std::abs((eta_ij * ops.i.rp.mean.z + ops.i.r.mean.z) / ops.i.r.mean.z);

    // Downward transmission (fake reflection)
    ops.i.t.g = hg_from_ggx(0.5f * s_t_ij * alpha);
    ops.i.t.mean = ops.i.rp.mean;

    // Upward transmission (fake reflection)
    ops.i.tp.g = hg_from_ggx(0.5f * s_t_ji * alpha);
    ops.i.tp.mean = ops.i.r.mean;

    // Albedos
    albedos(std::abs(wl.z), alpha, eta_ij, ops.i.r.norm, ops.i.t.norm, ops.i.rp.norm, ops.i.tp.norm, FGD);

    ops.i.r_ff     = ops.i.r;
    ops.i.t_ff     = ops.i.t;
    ops.i.rp_ff    = ops.i.rp;
    ops.i.tp_ff    = ops.i.tp;
}
static inline void conductor_transfer_factors(const Vector3& wl,
                                              const Spectrum& etas,
                                              const Spectrum& kappas,
                                              const Float alpha,
                                              const lut4& FGD,
                                              component_factors_6& ops) {

    // Component type
    ops.type = EConductorInterface;

    // Downward reflection
    ops.i.r.g = hg_from_ggx(alpha);
    ops.i.r.mean = reflectZ(wl);

    // Albedo
    albedo(std::abs(wl.z), alpha, etas, kappas, ops.i.r.norm, FGD);

    ops.i.r_ff = ops.i.r;
}
static inline void medium_transfer_factors(const Vector3& wl,
                                           const Float depth,
                                           const Spectrum& sigma_s,
                                           const Spectrum& sigma_k,
                                           const Float g,
                                           component_factors_6& ops) {

    // Component type
    ops.type = EHomogeneousMedium;

    // Empty medium
    if (depth == 0.f) {

        ops.type = ENoComponent;
        ops.i.t.mean = -wl;

        return;
    }

    // Incident depth
    Float tau = depth / wl.z;

    // Interaction coefficients
    const Spectrum sigma_sb     = sigma_s * hg_lh_norm(g);
    const Spectrum sigma_sf     = sigma_s - sigma_sb;
    const Spectrum sigma_ext    = sigma_s + sigma_k;

    // Energies
    {
        const Spectrum alpha    = sigma_ext - sigma_sf;
        const Spectrum beta     = sigma_sb;
        const Spectrum gamma    = (alpha * alpha - beta * beta).sqrt();

        // Clamp depth to avoid numerical issues with transcendent functions
        tau = std::min(tau, TM_EXP_ARG_MAX / gamma.max());
        tau = std::min(tau, TM_EXP_ARG_MAX / sigma_ext.max());

        const Spectrum S = sinh(gamma * tau);
        const Spectrum C = (Spectrum(1.f) + S * S).sqrt();

        const Spectrum x0 = exp(-sigma_ext * tau);
        const Spectrum x1 = Spectrum(1.f) / (C * gamma + S * alpha);

        ops.m.t.norm     = x0;
        ops.m.r_fb.norm  = S * beta * x1;
        ops.m.t_ff.norm  = gamma * x1;
    }
    // Asymetry parameters
    {
        const Spectrum alpha   = sigma_ext - sigma_sf * g;
        const Spectrum beta    = sigma_sb * -g;
        const Spectrum gamma   = (alpha * alpha - beta * beta).sqrt();

        const Spectrum S = sinh(gamma * tau);
        const Spectrum C = (Spectrum(1.f) + S * S).sqrt();

        const Spectrum x0 = Spectrum(1.f) / (C * gamma + S * alpha);

        ops.m.t.g    = 1.f;
        ops.m.r_fb.g = safe_div(S * beta * x0, ops.m.r_fb.norm).average();
        ops.m.t_ff.g = safe_div(gamma * x0, ops.m.t_ff.norm).average();
    }
}

MTS_NAMESPACE_END

#endif

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top