connortbot
connortbot

Reputation: 3

Why is my LayeredBSDF implementation absorbing light?

In my renderer, I already implemented a cook torrance dielectric and an oren nayar diffuse, and used that as my top and bottom layer respectively (to try and make a glossy diffuse, with glass on the top).

// Structure courtesy of 14.3.2, pbrt
    BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override {
        HitInfo rec_manip = rec;
        BSDFSample absorbed; absorbed.scatter = false;
        // Sample BSDF at entrance interface to get initial direction w
        bool on_top = rec_manip.front_face;
        vec3 outward_normal = rec_manip.front_face ? rec_manip.normal : -rec_manip.normal;

        BSDFSample bs = on_top ? top->sample(r_in, rec_manip, scattered) : bottom->sample(r_in, rec_manip, scattered);
        if (!bs.scatter) { return absorbed; }
        if (dot(rec_manip.normal, bs.scatter_direction) > 0) { return bs; }
        vec3 w = bs.scatter_direction;

        color f = bs.bsdf_value * fabs(dot(rec_manip.normal, (bs.scatter_direction)));
        float pdf = bs.pdf_value;

        for (int depth = 0; depth < termination; depth++) {
            // Follow random walk through layers to sample layered BSDF
            // Possibly terminate layered BSDF sampling with Russian Roulette
            float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value;
            if (depth > 3 && rrBeta < 0.25) {
                float q = fmax(0, 1-rrBeta);
                if (random_double() < q) { return absorbed; } // absorb light
                // otherwise, account pdf for possibility of termination
                pdf *= 1 - q;
            }

            // Initialize new surface
            std::shared_ptr<material> layer = on_top ? bottom : top;

            // Sample layer BSDF for determine new path direction
            ray r_new = ray(r_in.origin() - w, w, 0.0);
            BSDFSample bs = layer->sample(r_new, rec_manip, scattered);
            if (!bs.scatter) { return absorbed; }
            f = f * bs.bsdf_value;
            pdf = pdf * bs.pdf_value;
            w = bs.scatter_direction;

            // Return sample if path has left the layers
            if (bs.type == BSDF_TYPE::TRANSMISSION) {
                BSDF_TYPE flag = dot(outward_normal, w) ? BSDF_TYPE::SPECULAR : BSDF_TYPE::TRANSMISSION;
                BSDFSample out_sample;
                out_sample.scatter = true;
                out_sample.scatter_direction = w;
                out_sample.bsdf_value = f;
                out_sample.pdf_value = pdf;
                out_sample.type = flag;
                return out_sample;
            }
            
            f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction)));

            // Flip
            on_top = !on_top;
            rec_manip.front_face = !rec_manip.front_face;
            rec_manip.normal = -rec_manip.normal;
        }
        return absorbed;
   }

Which is resulting in an absurd amount of absorption of light. I'm aware that the way layered BSDFs are usually simulated typically darkens with a loss of energy...but probably not to this extent?

For context, the setting of the scatter flag to false just makes the current trace return, effectively returning a blank (or black) sample.

Upvotes: 0

Views: 23

Answers (0)

Related Questions