Как рассчитать зеркальный вклад в 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 реализовал эту модель, и теперь существует большая путаница вокруг терминологии и рабочего процесса текстур.