overte-HifiExperiments/libraries/render-utils/src/ShadingModel.slh
2019-06-03 16:14:09 -07:00

237 lines
9.1 KiB
Text

<!
// LightingModel.slh
// fragment shader
//
// Created by Sam Gateau on 1/25/14.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
!>
<@if not SHADING_MODEL_SLH@>
<@def SHADING_MODEL_SLH@>
<@include LightingModel.slh@>
<@func declareBeckmannSpecular()@>
LAYOUT(binding=RENDER_UTILS_TEXTURE_SSSC_SPECULAR_BECKMANN) uniform sampler2D scatteringSpecularBeckmann;
float fetchSpecularBeckmann(float ndoth, float roughness) {
return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0);
}
vec2 skinSpecular(SurfaceData surface, float intensity) {
vec2 result = vec2(0.0, 1.0);
if (surface.ndotl > 0.0) {
float PH = fetchSpecularBeckmann(surface.ndoth, surface.roughness);
float F = fresnelSchlickScalar(0.028, surface);
float frSpec = max(PH * F / dot(surface.halfDir, surface.halfDir), 0.0);
result.x = surface.ndotl * intensity * frSpec;
result.y -= F;
}
return result;
}
<@endfunc@>
<@func declareEvalPBRShading()@>
float evalSmithInvG1(float roughness4, float ndotd) {
return ndotd + sqrt(roughness4+ndotd*ndotd*(1.0-roughness4));
}
SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) {
SurfaceData surface;
surface.eyeDir = eyeDir;
surface.normal = normal;
surface.roughness = mix(0.01, 1.0, roughness);
surface.roughness2 = surface.roughness * surface.roughness;
surface.roughness4 = surface.roughness2 * surface.roughness2;
surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0);
surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv);
// These values will be set when we know the light direction, in updateSurfaceDataWithLight
surface.ndoth = 0.0;
surface.ndotl = 0.0;
surface.ldoth = 0.0;
surface.lightDir = vec3(0,0,1);
surface.halfDir = vec3(0,0,1);
return surface;
}
void updateSurfaceDataWithLight(inout SurfaceData surface, vec3 lightDir) {
surface.lightDir = lightDir;
surface.halfDir = normalize(surface.eyeDir + lightDir);
vec3 dots;
dots.x = dot(surface.normal, surface.halfDir);
dots.y = dot(surface.normal, surface.lightDir);
dots.z = dot(surface.halfDir, surface.lightDir);
dots = clamp(dots, vec3(0), vec3(1));
surface.ndoth = dots.x;
surface.ndotl = dots.y;
surface.ldoth = dots.z;
}
vec3 fresnelSchlickColor(vec3 fresnelColor, SurfaceData surface) {
float base = 1.0 - surface.ldoth;
//float exponential = pow(base, 5.0);
float base2 = base * base;
float exponential = base * base2 * base2;
return vec3(exponential) + fresnelColor * (1.0 - exponential);
}
float fresnelSchlickScalar(float fresnelScalar, SurfaceData surface) {
float base = 1.0 - surface.ldoth;
//float exponential = pow(base, 5.0);
float base2 = base * base;
float exponential = base * base2 * base2;
return (exponential) + fresnelScalar * (1.0 - exponential);
}
float specularDistribution(SurfaceData surface) {
// See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf
// for details of equations, especially page 20
float denom = (surface.ndoth*surface.ndoth * (surface.roughness4 - 1.0) + 1.0);
denom *= denom;
// Add geometric factors G1(n,l) and G1(n,v)
float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl);
denom *= surface.smithInvG1NdotV * smithInvG1NdotL;
// Don't divide by PI as this is part of the light normalization factor
float power = surface.roughness4 / denom;
return power;
}
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) {
// Incident angle attenuation
float angleAttenuation = surface.ndotl;
// Specular Lighting
vec3 fresnelColor = fresnelSchlickColor(fresnel, surface);
float power = specularDistribution(surface);
vec3 specular = fresnelColor * power * angleAttenuation;
float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x);
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, diffuse);
}
// Frag Shading returns the diffuse amount as W and the specular rgb as xyz
vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) {
// Incident angle attenuation
float angleAttenuation = surface.ndotl;
// Specular Lighting
float fresnelScalar = fresnelSchlickScalar(fresnel, surface);
float power = specularDistribution(surface);
vec3 specular = vec3(fresnelScalar) * power * angleAttenuation;
float diffuse = angleAttenuation * (1.0 - fresnelScalar);
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, diffuse);
}
vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) {
// Incident angle attenuation
float angleAttenuation = surface.ndotl;
// Specular Lighting
vec3 fresnelColor = fresnelSchlickColor(fresnel, surface);
float power = specularDistribution(surface);
vec3 specular = fresnelColor * power * angleAttenuation;
// We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that
// we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit
// by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means".
// (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf
// page 23 paragraph "Punctual light sources")
return vec4(specular, 0.f);
}
<@endfunc@>
<$declareEvalPBRShading()$>
void evalFragShading(out vec3 diffuse, out vec3 specular,
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) {
vec4 shading = evalPBRShading(metallic, fresnel, surface);
diffuse = vec3(shading.w);
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
specular = shading.xyz;
}
<$declareBeckmannSpecular()$>
<@include SubsurfaceScattering.slh@>
<$declareSubsurfaceScatteringBRDF()$>
void evalFragShading(out vec3 diffuse, out vec3 specular,
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo,
float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) {
if (scattering * isScatteringEnabled() > 0.0) {
vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
diffuse = mix(vec3(surface.ndotl), brdf, scattering);
// Specular Lighting
vec2 specularBrdf = skinSpecular(surface, 1.0);
diffuse *= specularBrdf.y;
specular = vec3(specularBrdf.x);
} else {
vec4 shading = evalPBRShading(metallic, fresnel, surface);
diffuse = vec3(shading.w);
specular = shading.xyz;
}
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
}
void evalFragShadingScattering(out vec3 diffuse, out vec3 specular,
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo,
float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) {
vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w);
float NdotL = surface.ndotl;
diffuse = mix(vec3(NdotL), brdf, scattering);
// Specular Lighting
vec2 specularBrdf = skinSpecular(surface, 1.0);
diffuse *= specularBrdf.y;
specular = vec3(specularBrdf.x);
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
}
void evalFragShadingGloss(out vec3 diffuse, out vec3 specular,
float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) {
vec4 shading = evalPBRShading(metallic, fresnel, surface);
diffuse = vec3(shading.w);
diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled());
specular = shading.xyz;
}
vec3 evalSpecularWithOpacity(vec3 specular, float opacity) {
return specular / mix(1.0, opacity, float(opacity > 0.0));
}
<@if not GETFRESNEL0@>
<@def GETFRESNEL0@>
vec3 getFresnelF0(float metallic, vec3 metalF0) {
// Enable continuous metallness value by lerping between dielectric
// and metal fresnel F0 value based on the "metallic" parameter
return mix(vec3(0.03), metalF0, metallic);
}
<@endif@>
<@endif@>