mirror of
https://github.com/overte-org/overte.git
synced 2025-06-05 20:34:24 +02:00
316 lines
11 KiB
Text
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@>
|