Как рассчитать зеркальный вклад в 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 ответ

То, что вы ищете, это функция распределения двунаправленного отражения (BRDF) для материала. В своем коде вы ссылаетесь на "модель Кука - Торранса", которая является распространенной и эффективной (но также и дорогостоящей в вычислительном отношении) BRDF. Кажется, что вы можете получить идеи как от модели "металлик / шероховатость", так и от модели "зеркальность / блеск". Это огромная тема, но понимание двух может помочь.

В любом случае, в физической модели затенения BRDF должен сохранять энергию. Поэтому вклад diffuse + specular не должен превышать 1 или:

Kd = 1 - Ks

Физическая точность ваших шейдеров зависит от вычислений, которые вы выполняете для свойств материала, но в вашем случае вы можете включить металлический термин в BRDF следующим образом:

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

Отсюда вы можете управлять освещением и т. Д.

- Происхождение шейдеров Metalness/Roughness

Дисней придумал более реалистичный шейдерный метод. UnrealEngine4 реализовал эту модель, и теперь существует большая путаница вокруг терминологии и рабочего процесса текстур.

Код BRDF UE4 - требуется регистрация

BRDF Диснея

Основной фон

Другие вопросы по тегам