<p>Let's take a look at what' going on here. The Monte Carlo estimator is given by:
<span class="math-container">$$
\hat{I} = \sum_{i=1}^N\frac{f(X_i)}{p(X_i)}
$$</span>
The <span class="math-container">$f(x)$</span> (evaluation function) here can be writen as (simplified form, ignored some terms in the BRDF):
<span class="math-container">$$
f(\omega) = \biggl((1 - F)K_df_{\text{Lambert}}(\omega) + FK_sf_{\text{C-T}}(\omega) \biggr)\cos\theta
$$</span>
<span class="math-container">$F$</span> here is the Fresnel term, which weights the direct reflected specular radiance and the radiance that <em>penetrates</em> to the substrate and gets reflected back. The goal of importance sampling is to approximate <span class="math-container">$f(x)$</span> as closely as possible to achieve maximum variance reduction. So what's with the two approaches given?</p>
<ul>
<li>The first approach: since we only sample the NDF, the <span class="math-container">$p(X)$</span> should simply be related to <span class="math-container">$f_{\text{C-T}}$</span> part (specular component), which is normally good, since diffuse component is generally easy to sample.</li>
<li>The second approach: Note that we are alternating between Lambertian sampling and Cook-Torrance sampling, therefore the PDF is a weighted one:
<span class="math-container">$$
p(\omega) = p_{\text{Lambert}}(\omega|\text{diffuse})p(\text{diffuse}) + p_{\text{C-T}}(\omega|\text{specular})p(\text{specular})
$$</span>
So generally, I think the PDF of the second approach is a better approximation to the integrand. So in this way I will prefer the second approach.</li>
</ul>
<p>Also, the first approach will always compute NDF sampling and Cook-Torrance BRDF evaluation while the second approach can avoid this. NDF sampling and Cook-Torrance BRDF evaluation are significantly (yes, they are) more complex than our simple, adorable cosine-weighted sampling and Lambertian BRDF evaluation. So this might actually save you some rendering time (but, well, this should be negligible).</p>
<p>Moreover, we can actually find some similar examples to discourage you from using the first approach. For example, we have a material with IOR = 1. What should happen is that since <span class="math-container">$F$</span> is 0, we don't even have to consider the specular lobe. Therefore, it is just a waste of CPU cycles to sample the NDF and evaluate Cook-Torrance BRDF.</p>
<hr />
<p>So, when should you use the two approaches? My suggestion is:</p>
<ul>
<li>For the first approach: When you are implementing your path tracer on GPU, or the random sampler is complex and takes time to do the state update (like, Sobol or something): 1. GPU works more efficiently when each thread has similar workload (Google: thread divergence). 2. The second approach will have an extra sample to draw from the sampler, so when the sampling process is (too) expensive, the first approach is prefered.</li>
<li>For the second approach: well you can just implement this in your code, generally. If I recall correctly, Tungsten renderer does this the same way. Do note that if the BSDF contains both reflection and transmission... I think only the second approach works.</li>
</ul>
<p>Hope this would help. Feel free to point out anything wrong.</p>

CC BY-SA 4.0
2024-01-05T09:05:17.003Z
Thank you I'll probably go with the second approach then. 

Don't really understand this notation though: ()=Lambert(|diffuse)(diffuse)+C-T(|specular)(specular).

How are they weighed?
Ruben
CC BY-SA 4.0
2024-01-05T11:28:05.150Z
This is a conditional probability notation. As, for example, when you are sampling the specular lobe, you are already assuming to have a specular scattering event. So, the probability of having a specular event is $F$, which is the value taken by $p(\text{specular})$. And basically, you will sample the NDF **under the condition** that you have a specular event. So $p(\omega | \text{specular})$ is the PDF of your specular lobe (NDF sampling PDF).
Enigmatisms
CC BY-SA 4.0
2024-01-05T07:47:50.490Z
2024-01-05T07:47:50.490Z
Enigmatisms
<p>You just need to get the second reflection after a fresnel transmit,  then ur kicking butt.  Fresnel does most the work in the shader,  D is the collected dot product and the G is just computing the microshadows,  you can run it without d and it looks fine as long as u have g,  because g is second most important.
But F is the most important,  without the scattering event (getting the second reflection.) its no where near as good.</p>

CC BY-SA 4.0
2024-09-08T16:53:29.723Z
2024-09-08T16:53:29.723Z
Magnus Wootton
<p>Trying to implement the Cook-Torrance BRDF for my path tracer. Confused about how to do it though. I've seen two approaches:
1: Sample the NDF, evaluate diffuse and specular components and add them weighed by the fresnel (between microfacet normal and view direction?)
2: Based on the fresnel (again, between microfacet normal and view direction?), randomly choose to evaluate a diffuse or specular path. If diffuse, sample uniformly/cosine, and if specular, sample the NDF. Correct/weigh by fresnel factor.</p>
<p>Why would you use the second approach though? When roughness is 1.0, sampling the NDF should be equivalent to cosine sampling, so approach 2 just seems like a more complicated way to do achieve the same thing.</p>

<pathtracing><importance-sampling><ggx>
How to approach implementing Cook-Torrence microfacet brdf?
Ruben
# How to approach implementing Cook-Torrence microfacet brdf?

(Asked by: username_0 on 2024-01-04)

Trying to implement the Cook-Torrance BRDF for my path tracer. Confused about how to do it though. I've seen two approaches:
1: Sample the NDF, evaluate diffuse and specular components and add them weighed by the fresnel (between microfacet normal and view direction?)
2: Based on the fresnel (again, between microfacet normal and view direction?), randomly choose to evaluate a diffuse or specular path. If diffuse, sample uniformly/cosine, and if specular, sample the NDF. Correct/weigh by fresnel factor.

Why would you use the second approach though? When roughness is 1.0, sampling the NDF should be equivalent to cosine sampling, so approach 2 just seems like a more complicated way to do achieve the same thing.

**Tags:** pathtracing, importance-sampling, ggx


---

## 2 answers

### Answer by username_1 on 2024-01-05. Score: 2 | Accepted answer

Let's take a look at what' going on here. The Monte Carlo estimator is given by:
$$
\hat{I} = \sum\_{i=1}^N\frac{f(X\_i)}{p(X\_i)}
$$
The $f(x)$ (evaluation function) here can be writen as (simplified form, ignored some terms in the BRDF):
$$
f(\omega) = \biggl((1 - F)K\_df\_{\text{Lambert}}(\omega) + FK\_sf\_{\text{C-T}}(\omega) \biggr)\cos\theta
$$
$F$ here is the Fresnel term, which weights the direct reflected specular radiance and the radiance that *penetrates* to the substrate and gets reflected back. The goal of importance sampling is to approximate $f(x)$ as closely as possible to achieve maximum variance reduction. So what's with the two approaches given?

* The first approach: since we only sample the NDF, the $p(X)$ should simply be related to $f\_{\text{C-T}}$ part (specular component), which is normally good, since diffuse component is generally easy to sample.
* The second approach: Note that we are alternating between Lambertian sampling and Cook-Torrance sampling, therefore the PDF is a weighted one:
  $$
  p(\omega) = p\_{\text{Lambert}}(\omega|\text{diffuse})p(\text{diffuse}) + p\_{\text{C-T}}(\omega|\text{specular})p(\text{specular})
  $$
  So generally, I think the PDF of the second approach is a better approximation to the integrand. So in this way I will prefer the second approach.

Also, the first approach will always compute NDF sampling and Cook-Torrance BRDF evaluation while the second approach can avoid this. NDF sampling and Cook-Torrance BRDF evaluation are significantly (yes, they are) more complex than our simple, adorable cosine-weighted sampling and Lambertian BRDF evaluation. So this might actually save you some rendering time (but, well, this should be negligible).

Moreover, we can actually find some similar examples to discourage you from using the first approach. For example, we have a material with IOR = 1. What should happen is that since $F$ is 0, we don't even have to consider the specular lobe. Therefore, it is just a waste of CPU cycles to sample the NDF and evaluate Cook-Torrance BRDF.

---

So, when should you use the two approaches? My suggestion is:

* For the first approach: When you are implementing your path tracer on GPU, or the random sampler is complex and takes time to do the state update (like, Sobol or something): 1. GPU works more efficiently when each thread has similar workload (Google: thread divergence). 2. The second approach will have an extra sample to draw from the sampler, so when the sampling process is (too) expensive, the first approach is prefered.
* For the second approach: well you can just implement this in your code, generally. If I recall correctly, Tungsten renderer does this the same way. Do note that if the BSDF contains both reflection and transmission... I think only the second approach works.

Hope this would help. Feel free to point out anything wrong.

> **Comments:**
> **username_0**: Thank you I'll probably go with the second approach then.   Don't really understand this notation though: ()=Lambert(|diffuse)(diffuse)+C-T(|specular)(specular).  How are they weighed?  
> **username_1**: This is a conditional probability notation. As, for example, when you are sampling the specular lobe, you are already assuming to have a specular scattering event. So, the probability of having a specular event is $F$, which is the value taken by $p(\text{specular})$. And basically, you will sample the NDF **under the condition** that you have a specular event. So $p(\omega | \text{specular})$ is the PDF of your specular lobe (NDF sampling PDF).  

---

### Answer by username_2 on 2024-09-08. Score: 1

You just need to get the second reflection after a fresnel transmit, then ur kicking butt. Fresnel does most the work in the shader, D is the collected dot product and the G is just computing the microshadows, you can run it without d and it looks fine as long as u have g, because g is second most important.
But F is the most important, without the scattering event (getting the second reflection.) its no where near as good.


---

