2
\$\begingroup\$

I'm trying to implement physically-based rendering, in a small game engine built for academic and learning purposes. I cannot understand the right way to calculate specular and diffuse contribution, based on the materials metallic and roughness values.

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

Right now, I ues this:

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

I'm under the impression that the specular has to be dependant on the roughness, somehow. I was thinking to change it to this:

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

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);
    // Note: the following line could also be NdotL, which is the same value
    float NdotV = max(dot(normal, viewDir), 0.0); 
    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);
}
\$\endgroup\$
1
\$\begingroup\$

Specular color for non metallic objects is typically .04 not zero. (That should be your F0) at glancing angles that goes up to 1.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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