如何计算PBR中的镜面贡献?

时间:2016-04-08 22:15:09

标签: opengl glsl game-engine specular pbr

我试图在我们的项目中实现基于物理的渲染(PBR)(我们开始用于学术和学习目的的小型游戏引擎)并且我无法理解什么是基于计算镜面反射和漫反射贡献的正确方法材料的金属和粗糙度。

我们不使用任何第三方库/引擎进行渲染,所有内容都是用OpenGL 3.3手写的。

现在我有了这个(我将在下面填写完整的代码):

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

但我的印象是,镜面术语必须以某种方式依赖于粗糙度。我想把它改成这个:

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

但是,我不确定。做正确的方法是什么?是否有正确的方法,或者我应该只使用'试验和错误'方法,直到我得到满意的结果?

以下是完整的GLSL代码:

// 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);
}

1 个答案:

答案 0 :(得分:2)

您正在寻找的是材料的双向反射分布函数(BRDF)。在您的代码中,您可以参考" Cook - Torrance模型"这是一种常见且有效(但计算上也很昂贵)的BRDF。看起来你可能会从金属/粗糙度"模型和"镜面/光泽"模型。这是一个很大的话题,但理解这两个可能有所帮助。

无论如何,在基于物理的着色模型中,BRDF必须节省能量。因此,漫反射+镜面反射的贡献不得超过1或:

for (int count = 0; count <= 3; count++)
    for (int count2 = 0; count2 < count; count2++)
         System.out.println(count2);

着色器的物理精度取决于您对材料属性执行的计算,但在您的情况下,您可以将金属术语合并到BRDF中,如下所示:

Kd = 1 - Ks

从这里你可以处理照明等。

- 金属度/粗糙度着色器起源

迪士尼想出了一种更逼真的着色方法。 UnrealEngine4实现了这个模型,现在在术语和纹理工作流程方面存在很多混淆。

UE4 BRDF code - signup required

Disney's BRDF

Basic Background