nvpro-samples / vk_raytracing_tutorial_KHR

Ray tracing examples and tutorials using VK_KHR_ray_tracing
Apache License 2.0
1.34k stars 142 forks source link

confusion about Path_tracing cosine distributed Pdf #54

Closed MouseChannel closed 1 year ago

MouseChannel commented 1 year ago

https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/8d4ba64b1816ef020bebe33298d496b305df2aca/ray_tracing_gltf/shaders/pathtrace.rchit#LL114C8-L114C9

the cosine_weight important sampling' pdf is cos_theta / PI why here is 1/PI really Confused

Nbickford commented 1 year ago

Here's one way of thinking about the 1/pi term: imagine a diffuse, white plane. If I shine light on this plane, the amount of light reflected - summing over all directions it might scatter to - should be the same as the incoming amount of light. In other words, the material model should be energy-conserving.

Mathematically, 1/pi is the right factor to make this hold for the Lambertian normal distribution function; if we multiplied by a greater number, the material would reflect more light than it received, and if it was smaller, the material would appear darker than it should be. (The white furnace test is one way to verify that one's normalization factor for a material model is correct.)

In more detail, energy conservation requires that for any incoming light direction (above the surface), the integral over all outgoing light directions of

max(0, dot(outgoing light direction, normal)) * brdf(incoming light direction, outgoing light direction)

is 1. (The dot product comes from https://en.m.wikipedia.org/wiki/Rendering_equation. See also https://en.m.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function#Physically_based_BRDFs). The Lambertian BRDF corresponds to BRDF(incoming, outgoing) := 1/pi.

In practice, we'd like to have an algorithm to estimate the integral in the rendering equation. For perfect material sampling, we'd choose outgoing light directions with a probability density function equal to the integrand (ignoring the visibility and lighting factors^1). This is possible for the Lambertian BRDF, and the algorithm in the code chooses outgoing directions with pdf = cos theta/pi (where cos theta is the dot product in the equation above).

However, with more complex BRDFs (e.g. GGX), perfectly sampling the BRDF like this isn't always possible. So, usually material model code implements

Then when choosing samples, we weight them by cos(n, out) BRDF(in, out)/PDF(in, out). This method is known as importance sampling: https://en.m.wikipedia.org/wiki/Importance_sampling .

Finally, if you've read through vk_mini_path_tracer, you might have noticed that vk_mini_path_tracer never mentions PDFs. This is because I hid this detail: I always chose material models that could be perfectly sampled, and so always had cos(n, out) BRDF(in, out)/PDF(in, out) == 1. (Ray Tracing in One Weekend uses the same idea in Book 1, and introduces PDFs in Book 3.) Vk_raytracing_tutorial_KHR gives more of the story around material models, in this sense.

[^1]: Ignoring these visibility and lighting factors makes sampling the BRDF easy - but means that our sampling pattern won't match the true value of the rendering equation's integrand. The results will still be correct, but they'll be noisy (if we could somehow sample the integrand perfectly, we'd have no noise, but this is usually impossible except for the simplest of scenes). Techniques like ReSTIR incorporate visibility terms, and lighting terms are often used in sampling in combined techniques like multiple importance sampling.

--Neil

On Sun, May 21, 2023, 00:35 莫成莫成你最棒 @.***> wrote:

https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/8d4ba64b1816ef020bebe33298d496b305df2aca/ray_tracing_gltf/shaders/pathtrace.rchit#LL114C8-L114C9

the cosine_weight important sampling' pdf is cos_theta / PI why here is 1/PI really Confused

— Reply to this email directly, view it on GitHub https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/issues/54, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIFUVPRKKRKIUQZ5OIUMMLXHHATHANCNFSM6AAAAAAYJHJV7M . You are receiving this because you are subscribed to this thread.Message ID: @.***>

Nbickford commented 1 year ago

I forgot to mention: https://sakibsaikia.github.io/graphics/2019/09/10/Deriving-Lambertian-BRDF-From-First-Principles.html is another excellent explanation of where the division by pi in the Lambertian BRDF comes from.

--Neil

On Sun, May 21, 2023, 01:15 Neil Bickford @.***> wrote:

Here's one way of thinking about the 1/pi term: imagine a diffuse, white plane. If I shine light on this plane, the amount of light reflected - summing over all directions it might scatter to - should be the same as the incoming amount of light. In other words, the material model should be energy-conserving.

Mathematically, 1/pi is the right factor to make this hold for the Lambertian normal distribution function; if we multiplied by a greater number, the material would reflect more light than it received, and if it was smaller, the material would appear darker than it should be. (The white furnace test is one way to verify that one's normalization factor for a material model is correct.)

In more detail, energy conservation requires that for any incoming light direction (above the surface), the integral over all outgoing light directions of

max(0, dot(outgoing light direction, normal)) * brdf(incoming light direction, outgoing light direction)

is 1. (The dot product comes from https://en.m.wikipedia.org/wiki/Rendering_equation. See also https://en.m.wikipedia.org/wiki/Bidirectional_reflectance_distribution_function#Physically_based_BRDFs). The Lambertian BRDF corresponds to BRDF(incoming, outgoing) := 1/pi.

In practice, we'd like to have an algorithm to estimate the integral in the rendering equation. For perfect material sampling, we'd choose outgoing light directions with a probability density function equal to the integrand (ignoring the visibility and lighting factors^1). This is possible for the Lambertian BRDF, and the algorithm in the code chooses outgoing directions with pdf = cos theta/pi (where cos theta is the dot product in the equation above).

However, with more complex BRDFs (e.g. GGX), perfectly sampling the BRDF like this isn't always possible. So, usually material model code implements

  • A function to evaluate the BRDF
  • An algorithm to produce samples that match the BRDF relatively well
  • A function that returns the probability density function of that algorithm choosing a certain direction.

Then when choosing samples, we weight them by cos(n, out) BRDF(in, out)/PDF(in, out). This method is known as importance sampling: https://en.m.wikipedia.org/wiki/Importance_sampling .

Finally, if you've read through vk_mini_path_tracer, you might have noticed that vk_mini_path_tracer never mentions PDFs. This is because I hid this detail: I always chose material models that could be perfectly sampled, and so always had cos(n, out) BRDF(in, out)/PDF(in, out) == 1. (Ray Tracing in One Weekend uses the same idea in Book 1, and introduces PDFs in Book 3.) Vk_raytracing_tutorial_KHR gives more of the story around material models, in this sense.

[^1]: Ignoring these visibility and lighting factors makes sampling the BRDF easy - but means that our sampling pattern won't match the true value of the rendering equation's integrand. The results will still be correct, but they'll be noisy (if we could somehow sample the integrand perfectly, we'd have no noise, but this is usually impossible except for the simplest of scenes). Techniques like ReSTIR incorporate visibility terms, and lighting terms are often used in sampling in combined techniques like multiple importance sampling.

--Neil

On Sun, May 21, 2023, 00:35 莫成莫成你最棒 @.***> wrote:

https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/8d4ba64b1816ef020bebe33298d496b305df2aca/ray_tracing_gltf/shaders/pathtrace.rchit#LL114C8-L114C9

the cosine_weight important sampling' pdf is cos_theta / PI why here is 1/PI really Confused

— Reply to this email directly, view it on GitHub https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/issues/54, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIFUVPRKKRKIUQZ5OIUMMLXHHATHANCNFSM6AAAAAAYJHJV7M . You are receiving this because you are subscribed to this thread.Message ID: @.***>

MouseChannel commented 1 year ago

maybe I did not make my question clear p = 1/PI here represent the pdf of importance sampling pdf (which there is cos_weight_sampling) line 111 https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/8d4ba64b1816ef020bebe33298d496b305df2aca/ray_tracing_gltf/shaders/pathtrace.rchit#L111 show the out-going ray sampling strategy, and using cos_weight importance sampling $$Lr =\int{H^2} Li BRDF (indir \cdot normal) dw_i $$ $$L_r=\frac{ Li BDRF (indir \cdot normal)}{pdf} $$ here the pdf of cos_weight imporance sampling is $$p = pdf = \frac{cosTheta}{Pi}$$ just like you mentioned

and the algorithm in the code chooses outgoing directions with pdf = cos theta/pi (where cos theta is the dot product in the equation above).

and here you said

and so always had cos(n, out) BRDF(in, out)/PDF(in, out) == 1.

$$\frac{cos(n, out) BRDF(in, out)} {PDF(in, out)} = \frac{cosTheta*1{/}Pi}{cosTheta/Pi} = 1$$

So if p represent pdf of cos_weight sampling here ,why should not be cosTheta/ Pi? I worry about that latex may not be rendered if you read it from mail, and thanks to your reply.

MouseChannel commented 1 year ago

and if p = 1/Pi as the pdf of sampling , in hemisphere the integral of p is not 1

Nbickford commented 1 year ago

Ah, I see! Sorry about that - thank you for following up! Since samplingHemisphere() is cosine-weighted, this line of code was incorrect -- it should have been const float p = cos_theta / M_PI. (As a result, you might notice that running the current code under the white furnace test renders a sphere that is too dark.) We'll put in a fix. Thanks!

MouseChannel commented 1 year ago

:)

NBickford-NV commented 1 year ago

Fixed in #56 and https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/commit/80e00cae4e938050c1ad1d69e8a5dd59846ea95b; closing. Thanks again!