summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean Harmer <sean.harmer@kdab.com>2017-05-11 15:03:20 +0100
committerJani Heikkinen <jani.heikkinen@qt.io>2017-05-16 10:03:36 +0000
commitafbf3d45f6e440b1d71d62a85289bd8fcc98a134 (patch)
tree56b67664545cb84646e48aa21c40136c0293dec0
parent6b231725b2b5e0f2d797f8939bd02637d0196942 (diff)
Match the Blinn-Phong specular power to be consistent with IBL
Use the same specular power as a function of roughness as used by Blinn-Phong in other engines and in Lys which is used to generate the image based lighting specular maps. During testing, noticed the specular highlight from punctual lights seems more like it's per-vertex based. Task-number: QTBUG-60181 Change-Id: Id379d59a5e1295c2cdf9bdabf246b7e0c0a9c499 Reviewed-by: Kevin Ottens <kevin.ottens@kdab.com>
-rw-r--r--src/extras/shaders/gl3/metalrough.frag61
-rw-r--r--src/extras/shaders/gl3/metalroughuniform.frag56
2 files changed, 56 insertions, 61 deletions
diff --git a/src/extras/shaders/gl3/metalrough.frag b/src/extras/shaders/gl3/metalrough.frag
index 0bbef9167..7f2f3d20e 100644
--- a/src/extras/shaders/gl3/metalrough.frag
+++ b/src/extras/shaders/gl3/metalrough.frag
@@ -85,6 +85,18 @@ int mipLevelCount(const in samplerCube cube)
return nMips;
}
+float remapRoughness(const in float roughness)
+{
+ // As per page 14 of
+ // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ // we remap the roughness to give a more perceptually linear response
+ // of "bluriness" as a function of the roughness specified by the user.
+ // r = roughness^2
+ const float maxSpecPower = 999999.0;
+ const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
+ return max(roughness * roughness, minRoughness);
+}
+
mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTangent)
{
// Make the tangent truly orthogonal to the normal by using Gram-Schmidt.
@@ -105,12 +117,9 @@ mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTa
return worldToTangentMatrix;
}
-float roughnessToMipLevel(float roughness)
+float alphaToMipLevel(float alpha)
{
- const float maxSpecPower = 999999.0;
- const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
- float r = max(roughness, minRoughness);
- float specPower = 2.0 / (r * r) - 2.0;
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
// We use the mip level calculation from Lys' default power drop, which in
// turn is a slight modification of that used in Marmoset Toolbag. See
@@ -138,24 +147,12 @@ float roughnessToMipLevel(float roughness)
return mipLevel;
}
-// Helper function to map from linear roughness value to non-linear alpha (shininess)
-float roughnessToAlpha(const in float roughness)
+float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
{
- // Constants to control how to convert from roughness [0,1] to
- // shininess (alpha) [minAlpha, maxAlpha] using a power law with
- // a power of 1 / rho.
- const float minAlpha = 1.0;
- const float maxAlpha = 1024.0;
- const float rho = 3.0;
-
- return minAlpha + (maxAlpha - minAlpha) * (1.0 - pow(roughness, 1.0 / rho));
-}
-
-float normalDistribution(const in vec3 n, const in vec3 h, const in float roughness)
-{
- // Blinn-Phong approximation
- float alpha = roughnessToAlpha(roughness);
- return (alpha + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), alpha);
+ // Blinn-Phong approximation - see
+ // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+ return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
}
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
@@ -203,7 +200,7 @@ vec3 pbrModel(const in int lightIndex,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
- const in float roughness,
+ const in float alpha,
const in float ambientOcclusion)
{
// Calculate some useful quantities
@@ -261,7 +258,7 @@ vec3 pbrModel(const in int lightIndex,
vec3 specularFactor = vec3(0.0);
if (sDotN > 0.0) {
specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
- specularFactor *= normalDistribution(n, h, roughness);
+ specularFactor *= normalDistribution(n, h, alpha);
}
vec3 specularColor = lights[lightIndex].color;
vec3 specular = specularColor * specularFactor;
@@ -279,7 +276,7 @@ vec3 pbrIblModel(const in vec3 wNormal,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
- const in float roughness,
+ const in float alpha,
const in float ambientOcclusion)
{
// Calculate reflection direction of view vector about surface normal
@@ -304,12 +301,7 @@ vec3 pbrIblModel(const in vec3 wNormal,
vec3 F0 = mix(dielectricColor, baseColor, metalness);
vec3 specularFactor = specularModel(F0, lDotH, lDotN, vDotN, n, h);
- // As per page 14 of
- // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
- // we remap the roughness to give a more perceptually linear response
- // of "bluriness" as a function of the roughness specified by the user.
- // r = roughness^2
- float lod = roughnessToMipLevel(roughness * roughness);
+ float lod = alphaToMipLevel(alpha);
//#define DEBUG_SPECULAR_LODS
#ifdef DEBUG_SPECULAR_LODS
if (lod > 7.0)
@@ -368,12 +360,15 @@ void main()
vec3 tNormal = 2.0 * texture(normalMap, texCoord).rgb - vec3(1.0);
vec3 wNormal = normalize(transpose(worldToTangentMatrix) * tNormal);
+ // Remap roughness for a perceptually more linear correspondence
+ float alpha = remapRoughness(roughness);
+
for (int i = 0; i < envLightCount; ++i) {
cLinear += pbrIblModel(wNormal,
wView,
baseColor,
metalness,
- roughness,
+ alpha,
ambientOcclusion);
}
@@ -384,7 +379,7 @@ void main()
wView,
baseColor.rgb,
metalness,
- roughness,
+ alpha,
ambientOcclusion);
}
diff --git a/src/extras/shaders/gl3/metalroughuniform.frag b/src/extras/shaders/gl3/metalroughuniform.frag
index 17773515d..c9191adbf 100644
--- a/src/extras/shaders/gl3/metalroughuniform.frag
+++ b/src/extras/shaders/gl3/metalroughuniform.frag
@@ -80,6 +80,18 @@ int mipLevelCount(const in samplerCube cube)
return nMips;
}
+float remapRoughness(const in float roughness)
+{
+ // As per page 14 of
+ // http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
+ // we remap the roughness to give a more perceptually linear response
+ // of "bluriness" as a function of the roughness specified by the user.
+ // r = roughness^2
+ const float maxSpecPower = 999999.0;
+ const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
+ return max(roughness * roughness, minRoughness);
+}
+
mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTangent)
{
// Make the tangent truly orthogonal to the normal by using Gram-Schmidt.
@@ -100,12 +112,9 @@ mat3 calcWorldSpaceToTangentSpaceMatrix(const in vec3 wNormal, const in vec4 wTa
return worldToTangentMatrix;
}
-float roughnessToMipLevel(float roughness)
+float alphaToMipLevel(float alpha)
{
- const float maxSpecPower = 999999.0;
- const float minRoughness = sqrt(2.0 / (maxSpecPower + 2));
- float r = max(roughness, minRoughness);
- float specPower = 2.0 / (r * r) - 2.0;
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
// We use the mip level calculation from Lys' default power drop, which in
// turn is a slight modification of that used in Marmoset Toolbag. See
@@ -133,24 +142,12 @@ float roughnessToMipLevel(float roughness)
return mipLevel;
}
-// Helper function to map from linear roughness value to non-linear alpha (shininess)
-float roughnessToAlpha(const in float roughness)
+float normalDistribution(const in vec3 n, const in vec3 h, const in float alpha)
{
- // Constants to control how to convert from roughness [0,1] to
- // shininess (alpha) [minAlpha, maxAlpha] using a power law with
- // a power of 1 / rho.
- const float minAlpha = 1.0;
- const float maxAlpha = 1024.0;
- const float rho = 3.0;
-
- return minAlpha + (maxAlpha - minAlpha) * (1.0 - pow(roughness, 1.0 / rho));
-}
-
-float normalDistribution(const in vec3 n, const in vec3 h, const in float roughness)
-{
- // Blinn-Phong approximation
- float alpha = roughnessToAlpha(roughness);
- return (alpha + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), alpha);
+ // Blinn-Phong approximation - see
+ // http://graphicrants.blogspot.co.uk/2013/08/specular-brdf-reference.html
+ float specPower = 2.0 / (alpha * alpha) - 2.0;
+ return (specPower + 2.0) / (2.0 * 3.14159) * pow(max(dot(n, h), 0.0), specPower);
}
vec3 fresnelFactor(const in vec3 color, const in float cosineFactor)
@@ -198,7 +195,7 @@ vec3 pbrModel(const in int lightIndex,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
- const in float roughness)
+ const in float alpha)
{
// Calculate some useful quantities
vec3 n = wNormal;
@@ -255,7 +252,7 @@ vec3 pbrModel(const in int lightIndex,
vec3 specularFactor = vec3(0.0);
if (sDotN > 0.0) {
specularFactor = specularModel(F0, sDotH, sDotN, vDotN, n, h);
- specularFactor *= normalDistribution(n, h, roughness);
+ specularFactor *= normalDistribution(n, h, alpha);
}
vec3 specularColor = lights[lightIndex].color;
vec3 specular = specularColor * specularFactor;
@@ -268,7 +265,7 @@ vec3 pbrIblModel(const in vec3 wNormal,
const in vec3 wView,
const in vec3 baseColor,
const in float metalness,
- const in float roughness)
+ const in float alpha)
{
// Calculate reflection direction of view vector about surface normal
// vector in world space. This is used in the fragment shader to sample
@@ -297,7 +294,7 @@ vec3 pbrIblModel(const in vec3 wNormal,
// we remap the roughness to give a more perceptually linear response
// of "bluriness" as a function of the roughness specified by the user.
// r = roughness^2
- float lod = roughnessToMipLevel(roughness * roughness);
+ float lod = alphaToMipLevel(alpha);
//#define DEBUG_SPECULAR_LODS
#ifdef DEBUG_SPECULAR_LODS
if (lod > 7.0)
@@ -338,13 +335,16 @@ void main()
{
vec3 cLinear = vec3(0.0);
+ // Remap roughness for a perceptually more linear correspondence
+ float alpha = remapRoughness(roughness);
+
vec3 worldView = normalize(eyePosition - worldPosition);
for (int i = 0; i < envLightCount; ++i) {
cLinear += pbrIblModel(worldNormal,
worldView,
baseColor.rgb,
metalness,
- roughness);
+ alpha);
}
for (int i = 0; i < lightCount; ++i) {
@@ -354,7 +354,7 @@ void main()
worldView,
baseColor.rgb,
metalness,
- roughness);
+ alpha);
}
// Apply exposure correction