Giter Site home page Giter Site logo

Comments (16)

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

Hey,

I believe you would need to flip eta if you are using the formula from the paper.

PBRT does that here:
https://github.com/mmp/pbrt-v3/blob/aaa552a4b9cbf9dccb71450f47b268e0ed6370e2/src/core/reflection.cpp#L255-L256

Here is a quick shader toy: https://www.shadertoy.com/view/ttKczy. You can flip between lines 25/26 and 48/49 and you would end up with the same half vector that is parallel to the microfacet normal. I was struggling with the transmission half vector earlier and my understanding could be wrong here, but it works as expected.

As for the factor of 4.0, it comes up due to a slight difference in the smith shadowing function that I'm using

The smith shadowing function for the view direction is
image

But the same function in the code does not have the numerator

//-----------------------------------------------------------------------
float SmithG_GGX(float NDotV, float alphaG)
//-----------------------------------------------------------------------
{
float a = alphaG * alphaG;
float b = NDotV * NDotV;
return 1.0 / (NDotV + sqrt(a + b - a * b));
}

And since we calculate the shadowing from the view as well as the light directions here:

float G = SmithG_GGX(NDotL, a) * SmithG_GGX(NDotV, a);

We would end up having 4.0 * NDotL * NDotV in the numerator if we had used the original function and this would then cancel out with the denominator of the reflection term of the microfacet shown next. With the modified smith function, we don't need to bother about the denominator of the reflection term.

image

The refraction term is as follows and this still has NDotV and NDotL in the denominator which cancels out with the numerator of the smith shadowing term but the 4.0 is missing and needs to be added which is why you see the 4.0 factor in the eval function.

image

Lol it'll probably make more sense to change the function to the original form to make the code clearer.

from glsl-pathtracer.

JAGJ10 avatar JAGJ10 commented on May 18, 2024

Thank you so much for the explanation regarding the 4.0, couldn't figure out where it was coming from!

For the half-vector, I'm not entirely positive either, but your shadertoy example seems correct, so I'll assume you're right lol.

from glsl-pathtracer.

JAGJ10 avatar JAGJ10 commented on May 18, 2024

A couple of follow up questions:

It looks like you have the specTrans parameter (transmission) take precedence over the metallic parameter. Shouldn't that be the other way around?

Additionally, if I'm understanding the code correctly, you do a lerp between the clearcoat lobe and the regular specular lobe in the PDF function. However, it doesn't look like you ever actually sample from the clearcoat lobe? I would think that when sampling you would want to basically pick between these two lobes using the same weight you're using for the PDF lerp part.

My understanding on the above could totally be wrong so I really appreciate any insight.

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

You're correct about both of these :D The clearcoat pdf was leftover from an older version of the Disney BRDF I was working on and it looks like I forgot to pick between the specular and clearcoat lobes when sampling. Either the clearcoat pdf can be removed or the lobe has to be sampled as well.

As for the transmission taking precedence, it seemed simpler to code it that way so it's not really the same as the paper. I'll be fixing these issues along with a third one where currently, the anisotropy is not taken into account when sampling the GTR2 lobe.

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

An update: I refactored the entire BSDF and it should be much cleaner and hopefully easier to understand now. Sampling of the clearcoat lobe has been added and the parameter precedence is also fixed now.

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

Hi,
I think I have found that your EvalDielectricRefraction function has a slight mistake.
Because you defined η=1/ior when entering and η=ior when exiting (line 134 in pathtrace.glsl), line 58 and 62 in disney.glsl should be corrected as below to become same with the equation 21 in the paper that you have referred.
(You can easily notice it by dividing numerator and denominator in equation 21 by η_o^2)

  • line 58 :
    float denomSqrt = dot(L, H) * state.eta + dot(V, H);
    to
    float denomSqrt = dot(L, H) + dot(V, H) * state.eta;
  • line 62 :
    return state.mat.albedo * (1.0 - F) * D * G * abs(dot(V, H)) * abs(dot(L, H)) * 4.0 * state.eta * state.eta / (denomSqrt * denomSqrt);
    to
    return state.mat.albedo * (1.0 - F) * D * G * abs(dot(V, H)) * abs(dot(L, H)) * 4.0 / (denomSqrt * denomSqrt);

This change seems to remove artifacts of the image that contains dielectric material.
Please correct me if my understanding is wrong.
Thank you.

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

Hi @juhyeonkim95,

Thanks a lot for the explanation as well as for the fix. Rough dielectrics have been bugging me for quite sometime now.

What you suggested make sense and I tried it out and it does indeed get rid of the fireflies and the image converges much faster :) However, I've been comparing my renderer to Mitsuba and Tungsten earlier and I see a difference in the image after this change. I'm not really sure where the bug is and I somehow doubt both those renderers could be wrong.

Incase you want to try it out, here is a file that has the render comparisons (before vs after) and the scene files for all three renderers. I've disabled tone mapping in all three while comparing the images.

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

@knightcrawler25
I cannot access the file. Can you reupload it in a public folder?

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

@juhyeonkim95 I changed the permissions on the file. It should be accessible now.

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

I've thoroughly compared your code with the paper and tungsten code, but the changed code seems correct! I'm also confused what causes the difference in the output.

Yours (based on changed code)

Compared to Tungsten and the paper, your SmithG_GGX function doesn't include 2 * NdotV term, which results G_glsl = G * 1 / (4 * NdotV * NdotL). I will write down equations using G.

  • First, compare f with equation 21
    CodeCogsEqn
    -> exactly same with equation 21
    This is also same with Tungsten code. (Note that Tungsten code also includes cosine term)
  • Now compare weight(including cosine term) with equation 41
    CodeCogsEqn (1)
    CodeCogsEqn (2)
    -> also exactly same with equation 41.
    This is also same with Tungsten code

Please let me know if I am wrong or if you have found anything else.

(Add) Tungsten renderer used slightly modified roughness for sampling which was also proposed in the paper (page 8, below figure 9), so I changed the code accordingly, but there was no big difference.

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

The first change you suggested looks good (float denomSqrt = dot(L, H) + dot(V, H) * state.eta;) and for the second one, I dug into the Tungsten code and it looks the weight is being multiplied by eta^2 here which is why there was a difference in the image but even after this, it still didn't match my renderer.

After more digging, the second issue I found is that direct lighting/next event estimation when the ray is inside the object doesn't work since it's unable to connect to a light source. Either the refraction event should be treated as a specular bounce or maybe, the shadow ray position has to be pushed out of the surface towards the surface normal while trying to connect to a light source (which I'm not really sure is the correct way since it does not account for refraction) Please let me know if you know more about direct lighting when it comes to the refraction case.

Adding state.specularBounce = true; just before the following line seems to work for now but I'll need to revisit the NEE and light sampling code again.

L = normalize(refract(-V, H, state.eta));

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

I forgot that BTDF does not obey reciprocity... (page 5 below equation 18)
f(i,o) / η_o^2 = f(o,i) / η_i^2 or f(i,o) * η^2 = f(o,i)
Because actual light's direction is reverse of path from camera, we have to use f(o,i) instead of f(i,o), and we have to multiply η^2! Your original code was correct.

For the second issue, I will check the Tungsten code, but it will take some time.

I've changed the code as you suggested, but dielectric material still seems weird in some scenes.

  • bedroom.scene
    캡처

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

Sure. Thanks a lot for the help btw :)

Also, if you're referring to the glass looking dark in the image, you'll have to increase the ray depth to something greater than 4. Hopefully, that should fix that.

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

It works after I increased ray depth. Thank for the help :)
But I'm still curious about multiplying η^2.
In your previous OptiX pathtracer, you did not multiply η^2 for dielectric material, but it worked well.
To investigate it more, I changed Eval function in glass.cu as following, to multiply η^2 if refraction occurs.

// glass.cu
RT_CALLABLE_PROGRAM float3 Eval(MaterialParameter &mat, State &state, PerRayData_radiance &prd)
{
    const float3 wo = prd.wo;
    const float3 wi = prd.bsdfDir;
    const float3 normal = state.normal;

    if(dot(wi, normal) * dot(wo, normal) >= 0){
        return mat.color;
    }
    float eta = dot(wo, normal) < 0 ?  1.45f : 1 / 1.45f;
    return mat.color * eta * eta;
	
    // return mat.color;
}

and here is the result for bedroom.scene. (max depth 16)

  • before (without multiplying eta)
    bedroom2
  • after (with multiplying eta)
    bedroom3

which seems really weird!

from glsl-pathtracer.

knightcrawler25 avatar knightcrawler25 commented on May 18, 2024

Oh the Optix path tracer is quite old and there are probably a lot of things wrong with the code there lol. I was mostly learning during that time (still learning actually :D) and it was put together from several other Optix samples, so I wouldn't trust the code there to do the right thing.

With the η2 there's a very subtle difference which is why it's not noticeable if it is not added. I compared the difference using a smooth dielectric material against Tungsten. Basically changed the following code and commented out the call to direct lighting since its a perfectly smooth material.

vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float pdf)
{
    state.specularBounce = true;
    vec3 f = vec3(0.0);

    float F = DielectricFresnel(abs(dot(V, N)), state.eta);

    // Reflection/Total internal reflection
    if (rand() < F)
    {
        L = normalize(reflect(-V, N));
        f = vec3(1.0);
    }
    else // Transmission
    {
        L = normalize(refract(-V, N, state.eta));
        f = vec3(1.0) * state.eta * state.eta;
    }

    pdf = 1.0;
    return f / abs(dot(N, L));
}

Here are the results:

With η2:

with

Tungsten: (matches pretty much exactly with the previous screenshot)

TungstenRender012

Without η2: (a very subtle difference near the neck and the base)

without

from glsl-pathtracer.

juhyeonkim95 avatar juhyeonkim95 commented on May 18, 2024

Thanks for the kind explanation. Your comments helped me a lot. I think I have to study more...

from glsl-pathtracer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.