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);
        return 2.f * abs(wo.z) / PI;

float pdf(wi, wo) {
    if (isSpecular(wi, wo))
        return (1.f - diffuseProbability);
        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.


1 Answer 1


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:



  • $\begingroup$ 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, although this one in particular doesn't give me good results with my current functions. $\endgroup$
    – Balfar
    Commented Jun 24, 2021 at 14:01
  • $\begingroup$ 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. $\endgroup$
    – Peter
    Commented Jun 28, 2021 at 14:22
  • $\begingroup$ 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. $\endgroup$
    – Peter
    Commented Jun 28, 2021 at 14:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.