#[SOLVED] Monte-Carlo Path-Tracer output too bright/saturated

145 messages · Page 1 of 1 (latest)

ashen trench
#

remove the objects and see if the values in the sky are what you expect in the resulting image

#

MCPT is not a thing it's just called path tracing

hollow grove
ashen trench
#

inspect the RGB values to see if they are what you expect

#

It's very hard to debug stuff if you don't test assumptions in your code

hollow grove
#

They look fine to me. They are within [0,255] as expected and growing towards 255 as we move downwards in the picture, which makes sense as we're transitioning to pure white.

ashen trench
#

check the values are exactly what you'd expect not that they are brighter then expected

hollow grove
#

They are what I expect. like I said.

ashen trench
#

If I have values that are too bright within the range of 0-1 say because I did .sqrt() or something then you'll still get 0 to 255 transition

#

manually inspect pixel values (not at the ends) and do the calculation manually

hollow grove
#

I am not sure what you are trying to get at. The way I am handling colors is identical to that of the handling in the book. The book uses a linear color space during the path-tracing. When eventually saving the image, gamma-2 correction is done followed by a clamping to [0,1], and lastly a transformation to [0, 255].

The above image void of any objects essentially boils down to evaluating the below for each pixel.

static const glm::dvec3 SKY_COLOR{ 0.5, 0.7, 1.0 };  // same color as in the book

const double alpha = 0.5 * (glm::normalize(ray.direction).y + 1.0);  // same alpha calculation
const glm::dvec3 skyColor = alpha * SKY_COLOR + (1.0 - alpha) * glm::dvec3{1.0};  // same linear-interpolation

return skyColor;

Nothing interesting is going on here and looking at color values at different stages of the program seemingly yield exactly what you'd expect.

ashen trench
#

So have you checked that if you have a pixel value at uv=(some value that isn't at a boundary) it is the correct value by opening it in GIMP or something?

hollow grove
#

I have printed out some color-related stuff at various points in the program, as well as inspected the individual pixel values in the resulting image using an appropriate tool.

ashen trench
#

so yes?

hollow grove
#

Yes.

ashen trench
#

Ok good we've at least narrowed it down a little bit

#

Set the sky to a solid value of (1, 1, 1) and the ground to (0.5, 0.5, 0.5) is the ground the colour you expect (remove all objects above it)

#

the ground should be exactly 0.5, 0.5, 0.5 before any gamma correction

hollow grove
#

I've already tried several such sanity tests, the one you suggested included. Yes, it does return vec3(0.5).

ashen trench
#

and if you try it with the same ground colour?

hollow grove
#

What do you mean?

ashen trench
#

If you try it with the same ground colour as the reference it should return a similar colour to the reference image

hollow grove
#

The ground color in the reference is vec3(0.5) too.

ashen trench
#

*with the sky

hollow grove
#

You mean to render the ground (with same color as reference) with sky included (as evaluated in reference)?

ashen trench
#

and no objects. I would stick with a constant colour that is close to what it is near the top

#

It should match the colour pretty closely

#

both yours and the reference should be ~gamma(SKY_COLOUR*Vec3(0.5)) no?

#

based on the images you sent one of them is clearly not

hollow grove
#

You will get the same color as in my image, the one that is much too bright/saturated.

#

I believe I've tried exactly that.

#

That's what is confusing me so much

#

I don't get how it becomes that way

hollow grove
#

The evaluation of the sky color is identical and the ground color is identical

ashen trench
#

is it gamma(SKY_COLOUR*Vec3(0.5))?

hollow grove
#

The gamma correction is the same

#

The clamping and transformation as well

ashen trench
#

that's not what I'm asking

hollow grove
#

How it produces the blueish ground color is not making sense

#

(your latest suggested test)

ashen trench
#

Is the pixel value the same as if y ou calculated gamma(SKY_COLOUR*Vec3(0.5)) manually?

hollow grove
#

I do not know what SKY_COLOR will evaluate to as it is the result of a linear interpolation that is dependent on the y-component of a given ray.

ashen trench
#

there is clearly a big enough difference between yours and the reference it should be clear if it's off

#

It should be around [0.474, 0.561, 0.671] roughly

#

did a colour picker on the reference image and it's around there in a lot of locations

#

which is what you'd expect

hollow grove
#

Yes, it is clearly off as we can see in the images. The gamma-corrected values as well as the final values in [0, 255] are much too high.

ashen trench
hollow grove
#

I just tested it? The results are much too high, e.g. (gamma-corrected) pixel values such as 0.774202 0.87157 1

ashen trench
#

ok then now you have something to work off

hollow grove
#

All of this was known prior to this hence why my hypothesis lies in TraceRay(...) incorrectly accumulating light.

#

The updating of throughput (which depends on e.g. BRDF, cosine term as well as the PDF with respect to a Lambertian material) is my main suspect.

#

As it is what is being multiplied by the sky color.

ashen trench
#

you should not have a cosine term

#

the sampling in RTOW is proportional to a cosine distribution so it cancels out

hollow grove
#

Correct, this is done in my case too.

#

BRDF * cosine / PDF is being evaluated where PDF = cosine / PI, so the cosine cancels out.

ashen trench
#

I'm aware

hollow grove
#

With a pure white sky color, the pre gamma-corrected value is vec(0.5), as expected.

ashen trench
ashen trench
#

just gamma correcting 0.9*sky gets you [0.671, 0.794, 0.949] which is smaller then the value you have

hollow grove
#

Hmm

ashen trench
#

for the sake of testing I would remove the gamma correction and then just do a solid SKY_COLOUR with 0.5, 0.5 ,0.5 and see if you get [0.250, 0.350, 0.500]

#

If that works try the same with 0.9 * SKY_COLOUR which should get [0.225, 0.315, 0.450]

hollow grove
#

What do you mean with "solid SKY_COLOUR"? Do you want it to remain as evaluated in the book, or do you want to set it to e.g. pure white again?

ashen trench
#

I mean set it to the constant value defined here:
static const glm::dvec3 SKY_COLOR{ 0.5, 0.7, 1.0 };

hollow grove
#

Alright

#

Aaaaaaaaaaaaaaaaah

#

I think I found the culprit...

hollow grove
#

after I removed the problem

#

The problem is the updating of throughput as a result of the way I am doing russian roulette...

#

Removing the russian roulette lead to me getting that ^ correct image.

ashen trench
#

You shouldn't be doing RR on such an early bounce

hollow grove
#

aaaaaaaaaaaaaah

#

right right

#

omg

#

it's a stupid logic error

#

I only start doing RR after 5 iterations... but I divide by 1/p already from first iteration...

#

no wonder

ashen trench
#

this is what your RR should look like:

            if depth > RUSSIAN_ROULETTE_THRESHOLD {
                let p = tp.component_max();
                if rng.gen() > p {
                    break;
                }
                tp /= p;
            }
hollow grove
#

yeah, I see the exact problem

#

it is a stupid mistake

#
        static uint32_t ITERS_BEFORE_RR = 5;
        const double p = glm::max(glm::max(throughput.x, throughput.y), throughput.z);
        if (i >= ITERS_BEFORE_RR && Util::RandomDouble() > p) {
            break;
        }
        throughput *= (1.0f / p);
#

that is INCORRECT

#

do you see why 😄

ashen trench
#

p is not conditional on ITER_BEFORE_RR

#

well the throughput *= (1.0 / p)

hollow grove
#

that last part, yes

#

that is being done before RR actually kicks in

ashen trench
#

did you actually test it properly?

hollow grove
#

it should? I am not testing for early exit until i >= 5

#

yet I am dividing the throughput to account for the RR even before i >= 5

#

I am introducing bias

ashen trench
hollow grove
#
if (i >= ITERS_BEFORE_RR) {
    throughput *= (1.0f / p);
}
#

is the ugly and naíve (but clear) way of fixing it

ashen trench
#

is there a reason you are avoid nested if statements?

hollow grove
#

I am not avoiding them bro

#

I just found out about this mistake

#

I will now fix it in an appropriate way

#

likely by first checking the ITERS_BEFORE_RR condition, then if true inside of there do the RR

ashen trench
hollow grove
#

which is probably what you have in mind.

#

exactly

#

that was my intended solution that I was just about to implement

#

you just seemed confused as to why the original code was wrong

#

so I just introduced that temporary fix to clarify

#

anyway

#

all should be well now

ashen trench
#

No I'm confused why you went straight for a seperate if statement

hollow grove
#

let me re render the original image

#

there we go

#

that is much nicer 😄

ashen trench
#

fresnel looks off

hollow grove
#

it is a pure mirror

#

a.k.a. not a realistic material

#

I'll be transitioning to using a microfacet based model later on

ashen trench
#

yes but even with a perfect mirror you should not have that darkening

#

a perfect mirror should not be losing energy

#

the reference does not have that darkening too

hollow grove
#

you have a good point

#

hmm

#

struct Mirror : Material {
    Mirror(const glm::dvec3 color, const glm::dvec3 emission = glm::dvec3(0.0f))
        : Material(color, emission) {}

    [[nodiscard]] glm::dvec3 f(const glm::dvec3& wi, const glm::dvec3& wo, const glm::dvec3& normal) const {
        const glm::dvec3 brdf = color;
        return brdf;
    }

    [[nodiscard]] Sample sample(const glm::dvec3& wo, const glm::dvec3& normal) const {
        const Sample sample{
            .wi = glm::reflect(-wo, normal),//-wo - 2 * glm::dot(-wo, normal) * normal,
            .pdf = 1.0f
        };
        return sample;
    }
};
#

What are your initial thoughts on this?

#

My understanding is that the BRDF is simply the albedo, and since we're always choosing the perfect reflection direction the PDF is 1.

ashen trench
#

the brdf is not simply the albedo

#

the BRDF is dirac delta distribution multiplied by the albedo divided by cos(theta)

#

dirac delta distributions are typically not handled in the same way as everything else

#

for reference this is my naive integrator which should be similar to what you have:

impl Naive {
    #[must_use]
    pub fn rgb(mut ray: Ray, rng: &mut impl MinRng) -> (Vec3, u64) {
        let mats = unsafe { MATERIALS.get().as_ref_unchecked() };
        let envmap = unsafe { ENVMAP.get().as_ref_unchecked() };
        let (mut tp, mut rgb) = (Vec3::ONE, Vec3::ZERO);

        let mut depth = 0;

        while depth < MAX_DEPTH {
            depth += 1;

            let sect = get_intersection(&ray, rng);

            if sect.is_none() {
                rgb += tp * envmap.sample_dir(ray.dir);
                break;
            }

            let mat = &mats[sect.mat];

            let wo = -ray.dir;

            rgb += mat.le() * tp;

            if mat
                .scatter(&sect, &mut ray, rng)
                .contains(ScatterStatus::EXIT)
            {
                break;
            }

            // by convention both wo and wi point away from the surface
            tp *= mat.eval(&sect, wo, ray.dir);

            if depth > RUSSIAN_ROULETTE_THRESHOLD {
                let p = tp.component_max();
                if rng.gen() > p {
                    break;
                }
                tp /= p;
            }
        }
        if rgb.contains_nan() {
            log::warn!("NAN encountered!");
            return (Vec3::X, 0);
        }
        (rgb, depth)
    }
}
#

eval is either BXDF * COS / PDF or albedo for dirac delta stuff

hollow grove
ashen trench
#

that looks good congratulations

#

might want to return the sky lerp

hollow grove
#

yeah hehe

#

well

#

thanks for sticking around and helping out

#

[SOLVED] Monte-Carlo Path-Tracer output too bright/saturated