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 Answer 1

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\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .