<p>Looks like the boundary of the integration changes to positive m.n and so you drop the Heaviside fnc to reflect that. (Actually a sample for m.n&lt;0 is not a valid sample, you ain't taking its value at all but just discard it.. meaning in code it's an if() before the actual sampling routine).</p>
<p>Ie. something like this :
<a href="https://i.sstatic.net/zMHZ5.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/zMHZ5.png" alt="enter image description here" /></a></p>
<p>The 2|i.m|m-i is actually 2(i.m)m-i (Walter is writing it like that in ie. Notes on the Ward BRDF).</p>

CC BY-SA 4.0
2023-01-11T01:07:01.880Z
Hello, I'm not quite sure what you mean by "boundary of the integration change[s] to positive m.n and so you drop the Heaviside f[u]nc[tion] to reflect that". I thought the boundary of integration with the microfacet normal is positive m.n due to the Heaviside function and presumably when we drop the Heaviside function when integrating the output (rather than the microfacet normal) direction m.n can be < 0 which works for some reason. Also rather than having an if statement I just set the pdf to 0 (with the microfacet normal which has the Heaviside function) which should function the same.
Nicholas Leon
CC BY-SA 4.0
2023-01-18T19:17:38.320Z
I have added a simple example to the answer. The Heaviside fnc is a constant after all and can be put outside the integral while changing the boundaries.
Max Tarpini
CC BY-SA 4.0
2023-01-21T02:59:04.763Z
I understand you can drop the Heaviside function to integrate into 1. As for why it doesn't integrate to 1 with the Heaviside function: It turns out the mapping from m to o is not one-to-one since both 2(i . m) m - i and 2(i . -m) -m are valid and for the jacobian to work you need to take into consideration both. So in practice, this looks like if  (m . n < 0) { m = -m }. After this change, the function integrates into one (with the Heaviside function) as you would expect.
Nicholas Leon
CC BY-SA 4.0
2023-01-10T21:31:56.057Z
2023-01-18T19:15:14.327Z
2023-01-18T19:15:14.327Z
Max Tarpini
<p>Context: I am attempting to implement Trowbridge–Reitz (GGX) based on <a href="https://www.cs.cornell.edu/%7Esrm/publications/EGSR07-btdf.pdf" rel="nofollow noreferrer">Microfacet Models for Refraction through Rough Surfaces</a> in a pathtracer. I use numerical integration to check if the pdfs integrate to 1 (I convert solid angle to spherical coordinates using the jacobian and integrate that):</p>
<p>It is stated that the probability of generating a given microfacet normal for GGX is <a href="https://i.sstatic.net/qAQD6.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/qAQD6.png" alt="p_m function" /></a></p>
<p>Where m is the microfacet normal and n is the macrofacet normal (which I set to (0, 0, 1) for the sake of testing). Note that || on the dot product isn't needed since D(m) &lt; 0 if the dot product is below zero due to the positive characteristic function (X+) on all the given microfacet distributions including GGX (see below).
<a href="https://i.sstatic.net/4YPda.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/4YPda.png" alt="GGX microfacet distribution" /></a></p>
<p>As expected when I integrate p_m(m) it indeed does integrate to 1 as you would expect.
<a href="https://i.sstatic.net/Sl3fg.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/Sl3fg.png" alt="integration of p_m = 1" /></a></p>
<p>The problems arise when I try to integrate the pdf of the sampled direction (o). Which I should be able to do by using the jacobian.</p>
<p><a href="https://i.sstatic.net/Jnsjm.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/Jnsjm.png" alt="p_o = p_m(m) * || omega_w_h / omega_w_o ||" /></a></p>
<p>The paper states that for ideal reflection the jacobian is 1/(4|o dot h|)</p>
<p><a href="https://i.sstatic.net/4Rxda.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/4Rxda.png" alt="ideal reflection section of paper" /></a></p>
<p>So I reasoned that this would be true:
<a href="https://i.sstatic.net/XoPha.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/XoPha.png" alt="integral of p_o can be done by modifying integral above with jacobian" /></a></p>
<p>The reason why I'm assuming the first equality is true is due to:</p>
<p>&quot;Let us assume that for any given incident
and outgoing directions, there is at most one microsurface
normal that scatters energy from i to o, and that we can compute that normal as h(i, o), which we call the half-direction&quot;. As well as the equation above (38).</p>
<p>I would like to point out that I am only doing ideal reflection, so no refraction.</p>
<p>The problem is when I attempt to integrate the expression in the image above. It does not integrate to 1 or a consistent value.</p>
<p>To make the distribution integrate to 1 I have to get rid of the positive characteristic function in D(m). Which results in p_m(m) integrating to 0 but p_o(o) integrating to 1.</p>
<p>Why does removing the positive characteristic function have this result and why doesn't p_o(o) integrate to 1 when it remains?</p>
<p>Also as a side note, I find that using</p>
<p><a href="https://i.sstatic.net/YzMm6.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/YzMm6.png" alt="reflection formula in paper" /></a></p>
<p>to calculate o given h and i leads to sampled distribution not matching up with the pdf (I use a chi-squared test of the binned values to verify this) compared to not using the absolute value (which matches up to most formulas online). Note that for my testing  I generate i in the top hemisphere and pointing away from the surface.</p>

<pathtracing><brdf><ggx>
Why do I need to remove the positive charactaristic function for the pdf of the outgoing direction to integrate to 1 with GGX?
Nicholas Leon
# Why do I need to remove the positive charactaristic function for the pdf of the outgoing direction to integrate to 1 with GGX?

(Asked by: username_0 on 2023-01-08)

Context: I am attempting to implement Trowbridge–Reitz (GGX) based on [Microfacet Models for Refraction through Rough Surfaces](https://www.cs.cornell.edu/%7Esrm/publications/EGSR07-btdf.pdf) in a pathtracer. I use numerical integration to check if the pdfs integrate to 1 (I convert solid angle to spherical coordinates using the jacobian and integrate that):

It is stated that the probability of generating a given microfacet normal for GGX is [![p_m function](https://i.sstatic.net/qAQD6.png)](https://i.sstatic.net/qAQD6.png)

Where m is the microfacet normal and n is the macrofacet normal (which I set to (0, 0, 1) for the sake of testing). Note that || on the dot product isn't needed since D(m) < 0 if the dot product is below zero due to the positive characteristic function (X+) on all the given microfacet distributions including GGX (see below).
[![GGX microfacet distribution](https://i.sstatic.net/4YPda.png)](https://i.sstatic.net/4YPda.png)

As expected when I integrate p\_m(m) it indeed does integrate to 1 as you would expect.
[![integration of p_m = 1](https://i.sstatic.net/Sl3fg.png)](https://i.sstatic.net/Sl3fg.png)

The problems arise when I try to integrate the pdf of the sampled direction (o). Which I should be able to do by using the jacobian.

[![p_o = p_m(m) * || omega_w_h / omega_w_o ||](https://i.sstatic.net/Jnsjm.png)](https://i.sstatic.net/Jnsjm.png)

The paper states that for ideal reflection the jacobian is 1/(4|o dot h|)

[![ideal reflection section of paper](https://i.sstatic.net/4Rxda.png)](https://i.sstatic.net/4Rxda.png)

So I reasoned that this would be true:
[![integral of p_o can be done by modifying integral above with jacobian](https://i.sstatic.net/XoPha.png)](https://i.sstatic.net/XoPha.png)

The reason why I'm assuming the first equality is true is due to:

"Let us assume that for any given incident
and outgoing directions, there is at most one microsurface
normal that scatters energy from i to o, and that we can compute that normal as h(i, o), which we call the half-direction". As well as the equation above (38).

I would like to point out that I am only doing ideal reflection, so no refraction.

The problem is when I attempt to integrate the expression in the image above. It does not integrate to 1 or a consistent value.

To make the distribution integrate to 1 I have to get rid of the positive characteristic function in D(m). Which results in p\_m(m) integrating to 0 but p\_o(o) integrating to 1.

Why does removing the positive characteristic function have this result and why doesn't p\_o(o) integrate to 1 when it remains?

Also as a side note, I find that using

[![reflection formula in paper](https://i.sstatic.net/YzMm6.png)](https://i.sstatic.net/YzMm6.png)

to calculate o given h and i leads to sampled distribution not matching up with the pdf (I use a chi-squared test of the binned values to verify this) compared to not using the absolute value (which matches up to most formulas online). Note that for my testing I generate i in the top hemisphere and pointing away from the surface.

**Tags:** pathtracing, brdf, ggx


---

## 1 answers

### Answer by username_1 on 2023-01-10. Score: 1 | Accepted answer

Looks like the boundary of the integration changes to positive m.n and so you drop the Heaviside fnc to reflect that. (Actually a sample for m.n<0 is not a valid sample, you ain't taking its value at all but just discard it.. meaning in code it's an if() before the actual sampling routine).

Ie. something like this :
[![enter image description here](https://i.sstatic.net/zMHZ5.png)](https://i.sstatic.net/zMHZ5.png)

The 2|i.m|m-i is actually 2(i.m)m-i (Walter is writing it like that in ie. Notes on the Ward BRDF).

> **Comments:**
> **username_0**: Hello, I'm not quite sure what you mean by "boundary of the integration change[s] to positive m.n and so you drop the Heaviside f[u]nc[tion] to reflect that". I thought the boundary of integration with the microfacet normal is positive m.n due to the Heaviside function and presumably when we drop the Heaviside function when integrating the output (rather than the microfacet normal) direction m.n can be < 0 which works for some reason. Also rather than having an if statement I just set the pdf to 0 (with the microfacet normal which has the Heaviside function) which should function the same.  
> **username_1**: I have added a simple example to the answer. The Heaviside fnc is a constant after all and can be put outside the integral while changing the boundaries.  
> **username_0**: I understand you can drop the Heaviside function to integrate into 1. As for why it doesn't integrate to 1 with the Heaviside function: It turns out the mapping from m to o is not one-to-one since both 2(i . m) m - i and 2(i . -m) -m are valid and for the jacobian to work you need to take into consideration both. So in practice, this looks like if  (m . n < 0) { m = -m }. After this change, the function integrates into one (with the Heaviside function) as you would expect.  

---

