Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly interpret material roughness as perceptual and convert to material #34

Merged
merged 4 commits into from
Jul 8, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions shaders/pbr-frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,12 @@ struct PBRInfo
float NdotH;
float LdotH;
float VdotH;
float roughness;
float perceptualRoughness;
float metalness;
vec3 baseColor;
vec3 reflectance0;
vec3 reflectance90;
float alphaRoughness;
};

const float M_PI = 3.141592653589793;
Expand All @@ -72,7 +73,7 @@ const float c_MinRoughness = 0.04;
// Implementation of diffuse from "Physically-Based Shading at Disney" by Brent Burley
vec3 disneyDiffuse(PBRInfo pbrInputs)
{
float f90 = 2.*pbrInputs.LdotH*pbrInputs.LdotH*pbrInputs.roughness - 0.5;
float f90 = 2.*pbrInputs.LdotH*pbrInputs.LdotH*pbrInputs.alphaRoughness - 0.5;

return (pbrInputs.baseColor/M_PI)*(1.0+f90*pow((1.0-pbrInputs.NdotL),5.0))*(1.0+f90*pow((1.0-pbrInputs.NdotV),5.0));
}
Expand Down Expand Up @@ -106,9 +107,9 @@ float geometricOcclusionCookTorrance(PBRInfo pbrInputs)
// implementation of microfacet occlusion from “An Inexpensive BRDF Model for Physically based Rendering” by Christophe Schlick
float geometricOcclusionSchlick(PBRInfo pbrInputs)
{
float k = pbrInputs.roughness * 0.79788; // 0.79788 = sqrt(2.0/3.1415);
float k = pbrInputs.perceptualRoughness * 0.79788; // 0.79788 = sqrt(2.0/3.1415); perceptualRoughness = sqrt(alphaRoughness);
// alternately, k can be defined with
// float k = (pbrInputs.roughness + 1)*(pbrInputs.roughness + 1)/8;
// float k = (pbrInputs.alphaRoughness + 1)*(pbrInputs.alphaRoughness + 1)/8;
Copy link
Contributor

@abwood abwood Jun 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to Epic's implementation notes, this commented-out alternative should use pbrInputs.perceptualRoughness rather than alphaRoughness:
float k = (pbrInputs.perceptualRoughness + 1)*(pbrInputs.perceptualRoughness + 1)/8;

See Equation 4 in http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my impression, too.

We also chose to use Disney’s modification to reduce “hotness” by remapping roughness using (Roughness+1)/2 before squaring.


float l = pbrInputs.LdotH / (pbrInputs.LdotH * (1.0 - k) + k);
float n = pbrInputs.NdotH / (pbrInputs.NdotH * (1.0 - k) + k);
Expand All @@ -120,8 +121,8 @@ float geometricOcclusionSmith(PBRInfo pbrInputs)
{
float NdotL2 = pbrInputs.NdotL * pbrInputs.NdotL;
float NdotV2 = pbrInputs.NdotV * pbrInputs.NdotV;
float v = ( -1. + sqrt ( pbrInputs.roughness * (1. - NdotL2 ) / NdotL2 + 1.)) * 0.5;
float l = ( -1. + sqrt ( pbrInputs.roughness * (1. - NdotV2 ) / NdotV2 + 1.)) * 0.5;
float v = ( -1. + sqrt ( pbrInputs.alphaRoughness * (1. - NdotL2 ) / NdotL2 + 1.)) * 0.5;
float l = ( -1. + sqrt ( pbrInputs.alphaRoughness * (1. - NdotV2 ) / NdotV2 + 1.)) * 0.5;
return (1. / max((1. + v + l ),0.000001));
}

Expand All @@ -138,14 +139,14 @@ float SmithG1(float NdotV, float r)

float geometricOcclusionSmithGGX(PBRInfo pbrInputs)
{
return SmithG1_var2(pbrInputs.NdotL, pbrInputs.roughness) * SmithG1_var2(pbrInputs.NdotV, pbrInputs.roughness);
return SmithG1_var2(pbrInputs.NdotL, pbrInputs.alphaRoughness) * SmithG1_var2(pbrInputs.NdotV, pbrInputs.alphaRoughness);
}

// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
// implementation from “Average Irregularity Representation of a Roughened Surface for Ray Reflection” by T. S. Trowbridge, and K. P. Reitz
float GGX(PBRInfo pbrInputs)
{
float roughnessSq = pbrInputs.roughness*pbrInputs.roughness;
float roughnessSq = pbrInputs.alphaRoughness*pbrInputs.alphaRoughness;
float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
return roughnessSq / (M_PI * f * f);
}
Expand Down Expand Up @@ -191,15 +192,15 @@ void main() {
float LdotH = clamp(dot(l,h), 0.0, 1.0);
float VdotH = clamp(dot(v,h), 0.0, 1.0);

float roughness = u_MetallicRoughnessValues.y;
float perceptualRoughness = u_MetallicRoughnessValues.y;
float metallic = u_MetallicRoughnessValues.x;
#ifdef HAS_METALROUGHNESSMAP
vec4 mrSample = texture2D(u_MetallicRoughnessSampler, v_UV);
roughness = mrSample.g * roughness;
perceptualRoughness = mrSample.g * perceptualRoughness;
metallic = mrSample.b * metallic;
#endif

roughness = clamp(roughness, c_MinRoughness, 1.0);
perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0);
metallic = clamp(metallic, 0.0, 1.0);

#ifdef HAS_BASECOLORMAP
Expand All @@ -226,17 +227,21 @@ void main() {
vec3 specularEnvironmentR0 = specularColor.rgb;
vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;

// roughness is authored as perceptual roughness; as is convention, convert to material roughness by squaring the perceptual roughness
float alphaRoughness = perceptualRoughness*perceptualRoughness;

PBRInfo pbrInputs = PBRInfo(
NdotL,
NdotV,
NdotH,
LdotH,
VdotH,
roughness,
perceptualRoughness,
metallic,
diffuseColor,
specularEnvironmentR0,
specularEnvironmentR90
specularEnvironmentR90,
alphaRoughness
);

vec3 F = fresnelSchlick2(pbrInputs);
Expand All @@ -257,8 +262,8 @@ void main() {

#ifdef USE_IBL
float mipCount = 9.0; // resolution of 512x512
float lod = (roughness * mipCount);
vec3 brdf = texture2D(u_brdfLUT, vec2(NdotV, 1.0 - roughness)).rgb;
float lod = (perceptualRoughness * mipCount);
vec3 brdf = texture2D(u_brdfLUT, vec2(NdotV, 1.0 - perceptualRoughness)).rgb;
vec3 diffuseLight = textureCube(u_DiffuseEnvSampler, n).rgb;

#ifdef USE_TEX_LOD
Expand Down Expand Up @@ -292,7 +297,7 @@ void main() {
color = mix(color, diffuseContrib, u_scaleDiffBaseMR.x);
color = mix(color, baseColor.rgb, u_scaleDiffBaseMR.y);
color = mix(color, vec3(metallic), u_scaleDiffBaseMR.z);
color = mix(color, vec3(roughness), u_scaleDiffBaseMR.w);
color = mix(color, vec3(perceptualRoughness), u_scaleDiffBaseMR.w);
#endif

gl_FragColor = vec4(color, baseColor.a);
Expand Down