overte/libraries/render-utils/src/LightingModel.slh

316 lines
11 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 LIGHTING_MODEL_SLH@>
<@def LIGHTING_MODEL_SLH@>
<@func declareLightingModel()@>
struct LightingModel {
vec4 _UnlitEmissiveLightmapBackground;
vec4 _ScatteringDiffuseSpecularAlbedo;
vec4 _AmbientDirectionalPointSpot;
vec4 _ShowContourObscuranceWireframe;
};
uniform lightingModelBuffer{
LightingModel lightingModel;
};
float isUnlitEnabled() {
return lightingModel._UnlitEmissiveLightmapBackground.x;
}
float isEmissiveEnabled() {
return lightingModel._UnlitEmissiveLightmapBackground.y;
}
float isLightmapEnabled() {
return lightingModel._UnlitEmissiveLightmapBackground.z;
}
float isBackgroundEnabled() {
return lightingModel._UnlitEmissiveLightmapBackground.w;
}
float isObscuranceEnabled() {
return lightingModel._ShowContourObscuranceWireframe.y;
}
float isScatteringEnabled() {
return lightingModel._ScatteringDiffuseSpecularAlbedo.x;
}
float isDiffuseEnabled() {
return lightingModel._ScatteringDiffuseSpecularAlbedo.y;
}
float isSpecularEnabled() {
return lightingModel._ScatteringDiffuseSpecularAlbedo.z;
}
float isAlbedoEnabled() {
return lightingModel._ScatteringDiffuseSpecularAlbedo.w;
}
float isAmbientEnabled() {
return lightingModel._AmbientDirectionalPointSpot.x;
}
float isDirectionalEnabled() {
return lightingModel._AmbientDirectionalPointSpot.y;
}
float isPointEnabled() {
return lightingModel._AmbientDirectionalPointSpot.z;
}
float isSpotEnabled() {
return lightingModel._AmbientDirectionalPointSpot.w;
}
float isShowLightContour() {
return lightingModel._ShowContourObscuranceWireframe.x;
}
float isWireframeEnabled() {
return lightingModel._ShowContourObscuranceWireframe.z;
}
<@endfunc@>
<$declareLightingModel()$>
struct SurfaceData {
vec3 normal;
vec3 eyeDir;
vec3 lightDir;
vec3 halfDir;
float roughness;
float roughness2;
float roughness4;
float ndotv;
float ndotl;
float ndoth;
float ldoth;
float smithInvG1NdotV;
};
<@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@>
<@func declareBeckmannSpecular()@>
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);
if (isAlbedoEnabled() > 0.0) {
diffuse *= albedo;
}
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;
}
<@endif@>