Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to calculate specular contribution in PBR?

I'm trying to implement physically-based rendering (PBR) in our project (we started a small game engine for academic and learning purposes) and I cannot understand what is the right way to calculate specular and diffuse contribution based on material's metallic and roughness.

We don't use any third party libraries/engines for rendering, everything is hand written in OpenGL 3.3.

Right now I have this (I'll put the full code below):

// Calculate contribution based on metallicity
vec3 diffuseColor  = baseColor - baseColor * metallic;
vec3 specularColor = mix(vec3(0.00), baseColor, metallic);

But I'm under the impression that specular term has to be depended by roughness somehow. I was thinking to change it to this:

vec3 specularColor = mix(vec3(0.00), baseColor, roughness);

But again, I'm not sure. What is the right way to do it? Is there even a right way or should I just use the 'trial and error' method until I get a satisfying result?

Here is the full GLSL code:

// Calculates specular intensity according to the Cook - Torrance model
float CalcCookTorSpec(vec3 normal, vec3 lightDir, vec3 viewDir, float roughness, float F0)
{
    // Calculate intermediary values
    vec3 halfVector = normalize(lightDir + viewDir);
    float NdotL = max(dot(normal, lightDir), 0.0);
    float NdotH = max(dot(normal, halfVector), 0.0);
    float NdotV = max(dot(normal, viewDir), 0.0); // Note: this could also be NdotL, which is the same value
    float VdotH = max(dot(viewDir, halfVector), 0.0);

    float specular = 0.0;
    if(NdotL > 0.0)
    {
        float G = GeometricalAttenuation(NdotH, NdotV, VdotH, NdotL);
        float D = BeckmannDistribution(roughness, NdotH);
        float F = Fresnel(F0, VdotH);

        specular = (D * F * G) / (NdotV * NdotL * 4);
    }
    return specular;
}

vec3 CalcLight(vec3 lightColor, vec3 normal, vec3 lightDir, vec3 viewDir, Material material, float shadowFactor)
{
    // Helper variables
    vec3  baseColor = material.diffuse;
    vec3  specColor = material.specular;
    vec3  emissive  = material.emissive;
    float roughness = material.roughness;
    float fresnel   = material.fresnel;
    float metallic  = material.metallic;

    // Calculate contribution based on metallicity
    vec3 diffuseColor  = baseColor - baseColor * metallic;
    vec3 specularColor = mix(vec3(0.00), baseColor, metallic);

    // Lambertian reflectance
    float Kd = DiffuseLambert(normal, lightDir);

    // Specular shading (Cook-Torrance model)
    float Ks = CalcCookTorSpec(normal, lightDir, viewDir, roughness, fresnel);

    // Combine results
    vec3 diffuse  = diffuseColor * Kd;
    vec3 specular = specularColor * Ks;
    vec3 result   = lightColor * (emissive + diffuse + specular);
    return result * (1.0 - shadowFactor);
}
like image 555
TheCrafter Avatar asked Nov 08 '22 17:11

TheCrafter


1 Answers

What you are looking for is the bidirectional reflectance distribution function (BRDF) for a material. In your code you reference the "Cook - Torrance model" which is a common and effective (but also computationally expensive) BRDF. It seems like you might be getting ideas from both "metallic/roughness" model and the "specular/glossiness" model. This is a huge topic but understanding the two might help.

Anyway, in a physically based shading model the BRDF must conserve energy. Therefore the contribution of diffuse + specular must not exceed 1 or:

Kd = 1 - Ks

The physical accuracy of your shaders are dependent on the computations you perform on the material properties, but in your case you can incorporate the metallic term into the BRDF like this:

BRDF = (1-m)*diffuse + m*specular

From here you can handle the lighting etc.

-- Metalness/Roughness shader origins

Disney came up with a shader method that was more realistic. UnrealEngine4 implemented this model-ish and now there is a lot of confusion around terminology and texture workflow.

UE4 BRDF code - signup required

Disney's BRDF

Basic Background

like image 123
adon Avatar answered Dec 31 '22 20:12

adon