<p>A common way of combining diffuse and specular brdfs is by using a fresnel equation.</p>
<p>Essentially, for some specular materials, the amount reflected and transmitted (passed through the object) depends on the angle you view it. For example water will reflect more if you look at it from one angle, but you can see through it if you look at it from another.</p>
<p>A fresnel equation is an approximation of how much is reflected vs transmitted.</p>
<p>One way of using this is to model a thin specular layer, and any light transmitted through this layer hits a diffuse layer underneath. Look up clear coat materials if you want to see some examples.</p>
<p>Essentially you calcuale fr = &quot;fresnel equation of your view angle&quot;, and then weight your reflection bxdf with fr and your diffuse bxdf with (1 - fr).</p>
<p>Here are some resources that might help you:</p>
<p><a href="https://en.wikipedia.org/wiki/Schlick%27s_approximation" rel="nofollow noreferrer">https://en.wikipedia.org/wiki/Schlick%27s_approximation</a></p>
<p><a href="https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/reflection-refraction-fresnel" rel="nofollow noreferrer">https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/reflection-refraction-fresnel</a></p>

CC BY-SA 4.0
2021-06-24T14:01:19.160Z
Thank you for your answer. Unfortunately, even if you are right, I will not be able to use any other parameter here than specular and diffuse coefficients. What I expect as a solution is something close to [this discussion](https://computergraphics.stackexchange.com/questions/9853/how-to-properly-combine-diffuse-and-rough-specular-brdfs-in-a-path-tracer), although this one in particular doesn't give me good results with my current functions.
Balfar
CC BY-SA 4.0
2021-06-28T14:22:01.587Z
Okay, well the examle I showed above requires zero parameters. All it is really doing is setting your diffuseProbablity automatically based on the viewing angle.
Peter
CC BY-SA 4.0
2021-06-28T14:41:53.960Z
To critique your code: "if (randomNumber < 0.5f)" I'm guessing should be "if (randomNumebr < (1 - diffuseProbabilty))". The pdf and bxdf should be the sum of both individual pdfs. Also the pdf of a reflection lobe isn't 1 it is technically infinite although you can just use a very high value. I'm also not sure why you added a 2 in your bxdf function. So I belive your bxdf function should be "bxdf = diffuseProbabilty * abs(wo.z) / PI; if(isSpecular(wi, wo)) bxdf += (1.f - diffuseProbability) * 10^16; return bxdf;". The pdf would be exactly the same.
Peter
CC BY-SA 4.0
2021-06-24T13:50:18.717Z
2021-06-24T13:50:18.717Z
Peter
<p>I am having troubles making a BRDF that has both specular and diffuse part. Each one is weighted by a coefficient and the sum of coefficients should be equal to one.</p>
<p>I want this layered BRDF to be the simplest possible and physically right.</p>
<p>What I am doing for the moment is:</p>
<pre><code>Vector3f sampleBRDF(Vector3f&amp; wi) {
    float randomNumber = generateUniform();
    if (randomNumber &lt; 0.5f) {
        // We will create a specular ray
        return reflectZUp(wi);
    }
    else {
        // We will create a diffuse ray
        float phi = 2_PI * generateUniform();
        float cosTheta = sqrt(generateUniform());
        float theta = acos(cosTheta);
        
        Vector3f wo = toCarthesian(Vector2f(phi, theta));
        wo.z = isSameHemisphereZUp(wi, wo) ? wo.z : -wo.z;
        
        return wo;
    }
}

float brdf(Vector3f&amp; wi, Vector3f&amp; wo, float diffuseProbability) {
    if (isSpecular(wi, wo))
        return 2.f * (1.f - diffuseProbability);
    else
        return 2.f * abs(wo.z) / PI;
}

float pdf(wi, wo) {
    if (isSpecular(wi, wo))
        return (1.f - diffuseProbability);
    else
        return diffuseProbability * abs(wo.z) / PI;
}
    
</code></pre>
<p>I am pretty sure my sampling method is right, because it is a very common one. But there may have mistakes in my BRDF et and PDF methods. The results that I obtain from this BRDF don't satisfy me.</p>
<p>If you have any ideas, advices or questions, I would be happy to read/answer you !</p>
<p>Have a good day.</p>

<brdf><importance-sampling><pdf>
How can I make the simplest physically based mixed specular-diffuse BRDF (and how to properly sample it)?
Balfar
# How can I make the simplest physically based mixed specular-diffuse BRDF (and how to properly sample it)?

(Asked by: username_0 on 2021-06-24)

I am having troubles making a BRDF that has both specular and diffuse part. Each one is weighted by a coefficient and the sum of coefficients should be equal to one.

I want this layered BRDF to be the simplest possible and physically right.

What I am doing for the moment is:

```
Vector3f sampleBRDF(Vector3f& wi) {
    float randomNumber = generateUniform();
    if (randomNumber < 0.5f) {
        // We will create a specular ray
        return reflectZUp(wi);
    }
    else {
        // We will create a diffuse ray
        float phi = 2_PI * generateUniform();
        float cosTheta = sqrt(generateUniform());
        float theta = acos(cosTheta);
        
        Vector3f wo = toCarthesian(Vector2f(phi, theta));
        wo.z = isSameHemisphereZUp(wi, wo) ? wo.z : -wo.z;
        
        return wo;
    }
}

float brdf(Vector3f& wi, Vector3f& wo, float diffuseProbability) {
    if (isSpecular(wi, wo))
        return 2.f * (1.f - diffuseProbability);
    else
        return 2.f * abs(wo.z) / PI;
}

float pdf(wi, wo) {
    if (isSpecular(wi, wo))
        return (1.f - diffuseProbability);
    else
        return diffuseProbability * abs(wo.z) / PI;
}
    

```

I am pretty sure my sampling method is right, because it is a very common one. But there may have mistakes in my BRDF et and PDF methods. The results that I obtain from this BRDF don't satisfy me.

If you have any ideas, advices or questions, I would be happy to read/answer you !

Have a good day.

**Tags:** brdf, importance-sampling, pdf


---

## 1 answers

### Answer by username_1 on 2021-06-24. Score: 2

A common way of combining diffuse and specular brdfs is by using a fresnel equation.

Essentially, for some specular materials, the amount reflected and transmitted (passed through the object) depends on the angle you view it. For example water will reflect more if you look at it from one angle, but you can see through it if you look at it from another.

A fresnel equation is an approximation of how much is reflected vs transmitted.

One way of using this is to model a thin specular layer, and any light transmitted through this layer hits a diffuse layer underneath. Look up clear coat materials if you want to see some examples.

Essentially you calcuale fr = "fresnel equation of your view angle", and then weight your reflection bxdf with fr and your diffuse bxdf with (1 - fr).

Here are some resources that might help you:

<https://en.wikipedia.org/wiki/Schlick%27s_approximation>

<https://www.scratchapixel.com/lessons/3d-basic-rendering/introduction-to-shading/reflection-refraction-fresnel>

> **Comments:**
> **username_0**: Thank you for your answer. Unfortunately, even if you are right, I will not be able to use any other parameter here than specular and diffuse coefficients. What I expect as a solution is something close to [this discussion](https://computergraphics.stackexchange.com/questions/9853/how-to-properly-combine-diffuse-and-rough-specular-brdfs-in-a-path-tracer), although this one in particular doesn't give me good results with my current functions.  
> **username_1**: Okay, well the examle I showed above requires zero parameters. All it is really doing is setting your diffuseProbablity automatically based on the viewing angle.  
> **username_1**: To critique your code: "if (randomNumber < 0.5f)" I'm guessing should be "if (randomNumebr < (1 - diffuseProbabilty))". The pdf and bxdf should be the sum of both individual pdfs. Also the pdf of a reflection lobe isn't 1 it is technically infinite although you can just use a very high value. I'm also not sure why you added a 2 in your bxdf function. So I belive your bxdf function should be "bxdf = diffuseProbabilty * abs(wo.z) / PI; if(isSpecular(wi, wo)) bxdf += (1.f - diffuseProbability) * 10^16; return bxdf;". The pdf would be exactly the same.  

---

