Files
H3VR-TNH-Quality-of-Life-Im…/Assets/Alloy/Shaders/Framework/LightingImpl.cginc
T

762 lines
23 KiB
HLSL
Raw Normal View History

2022-01-22 20:13:49 -08:00
// Alloy Physical Shader Framework
// Copyright 2013-2017 RUST LLC.
// http://www.alloy.rustltd.com/
/////////////////////////////////////////////////////////////////////////////////
/// @file LightingImpl.cginc
/// @brief Lighting method implementations to allow disabling of features.
/////////////////////////////////////////////////////////////////////////////////
#ifndef ALLOY_SHADERS_FRAMEWORK_LIGHTING_IMPL_CGINC
#define ALLOY_SHADERS_FRAMEWORK_LIGHTING_IMPL_CGINC
// Headers both for this file, and for all Definition and Feature modules.
#include "Assets/Alloy/Shaders/Config.cginc"
#include "Assets/Alloy/Shaders/Framework/Lighting.cginc"
#include "Assets/Alloy/Shaders/Framework/Utility.cginc"
#include "HLSLSupport.cginc"
#include "UnityCG.cginc"
#include "UnityShaderVariables.cginc"
#if !A_USE_UNITY_ATTENUATION || defined(USING_DIRECTIONAL_LIGHT)
#define A_UNITY_ATTENUATION(d, tex, lightCoord, scale)
#else
#define A_UNITY_ATTENUATION(d, tex, lightCoord, scale) d.color *= tex2D(tex, (dot(lightCoord, lightCoord) * scale).rr).UNITY_ATTEN_CHANNEL;
#endif
#if !defined(A_REFLECTION_PROBES_ON) && (!defined(A_GBUFFER_PASS) || !UNITY_ENABLE_REFLECTION_BUFFERS)
#define A_REFLECTION_PROBES_ON
#endif
// Lighting required vertex data flags.
#ifndef A_UNLIT_MODE
#ifdef A_DIRECT_LIGHTING_PASS
#define A_DIRECT_ON
#endif
#ifdef A_INDIRECT_LIGHTING_PASS
#define A_INDIRECT_ON
#endif
#if defined(A_DIRECT_ON) || defined(A_INDIRECT_ON)
#define A_LIGHTING_ON
#endif
#endif
#ifndef A_LIGHTING_ON
#if !defined(A_NORMAL_WORLD_ON) && defined(A_GBUFFER_PASS)
#define A_NORMAL_WORLD_ON
#endif
#else
#ifndef A_NORMAL_WORLD_ON
#define A_NORMAL_WORLD_ON
#endif
#ifndef A_VIEW_DIR_WORLD_ON
#define A_VIEW_DIR_WORLD_ON
#endif
#ifndef A_POSITION_WORLD_ON
#define A_POSITION_WORLD_ON
#endif
#if !defined(A_REFLECTION_VECTOR_WORLD_ON) && (defined(A_DIRECT_ON) || defined(A_REFLECTION_PROBES_ON))
#define A_REFLECTION_VECTOR_WORLD_ON
#endif
#endif
#if !defined(A_TANGENT_TO_WORLD_ON) && defined(A_NORMAL_MAPPED_PASS) && (defined(A_VIEW_DIR_TANGENT_ON) || defined(A_NORMAL_MAPPING_ON))
#define A_TANGENT_TO_WORLD_ON
#endif
#ifndef A_SCATTERING_ON
#define A_SKIN_SUBSURFACE_WEIGHT 1.0h
#else
#define A_SKIN_SUBSURFACE_WEIGHT _TransWeight
#ifdef A_FORWARD_ONLY_SHADER
#define A_SCATTERING_LUT _SssBrdfTex
#define A_SCATTERING_WEIGHT _SssWeight
#define A_SCATTERING_INV_MASK_CUTOFF 1.0h / _SssMaskCutoff
#define A_SCATTERING_BIAS _SssBias
#define A_SCATTERING_SCALE _SssScale
#define A_SCATTERING_NORMAL_BLUR _SssBumpBlur
#define A_SCATTERING_ABSORPTION _SssTransmissionAbsorption
#define A_SCATTERING_AO_COLOR_BLEED _SssColorBleedAoWeights
#else
#define A_SCATTERING_LUT _DeferredSkinLut
#define A_SCATTERING_WEIGHT _DeferredSkinParams.x
#define A_SCATTERING_INV_MASK_CUTOFF _DeferredSkinParams.y
#define A_SCATTERING_BIAS _DeferredSkinTransmissionAbsorption.w
#define A_SCATTERING_SCALE _DeferredSkinColorBleedAoWeights.w
#define A_SCATTERING_NORMAL_BLUR _DeferredSkinParams.z
#define A_SCATTERING_ABSORPTION _DeferredSkinTransmissionAbsorption.xyz
#define A_SCATTERING_AO_COLOR_BLEED _DeferredSkinColorBleedAoWeights.xyz
#endif
#endif
#ifdef A_SUBSURFACE_ON
#ifdef A_FORWARD_ONLY_SHADER
#define A_SUBSURFACE_WEIGHT A_SKIN_SUBSURFACE_WEIGHT
#define A_SUBSURFACE_FALLOFF _TransPower
#define A_SUBSURFACE_DISTORTION _TransDistortion
#define A_SUBSURFACE_SHADOW _TransShadowWeight
#else
#define A_SUBSURFACE_WEIGHT _DeferredTransmissionParams.x
#define A_SUBSURFACE_FALLOFF _DeferredTransmissionParams.y
#define A_SUBSURFACE_DISTORTION _DeferredTransmissionParams.z
#define A_SUBSURFACE_SHADOW _DeferredTransmissionParams.w
#endif
#endif
#if !A_USE_DEFERRED_MATERIAL_TYPE_BRANCHING || !defined(A_DEFERRED_PASS)
#define A_DEFERRED_BRANCH(CONDITION)
#else
#define A_DEFERRED_BRANCH(CONDITION) \
UNITY_BRANCH \
if (CONDITION)
#endif
AIndirect aNewIndirect() {
AIndirect i;
UNITY_INITIALIZE_OUTPUT(AIndirect, i);
i.diffuse = 0.0h;
i.specular = 0.0h;
return i;
}
ADirect aNewDirect()
{
ADirect d;
UNITY_INITIALIZE_OUTPUT(ADirect, d);
d.color = 0.0h;
d.shadow = 1.0h;
d.specularIntensity = 1.0h;
d.direction = A_AXIS_Y;
return d;
}
void aLightCookie(
inout ADirect d,
half4 cookie)
{
#if defined(UNITY_PASS_FORWARDBASE) || A_USE_UNITY_LIGHT_COOKIES
d.color *= cookie.a;
#else
d.color *= cookie.rgb * cookie.a;
#endif
}
void aSetLightRangeLimit(
inout ADirect d,
half range)
{
#if !A_USE_UNITY_ATTENUATION
// Light Range Limit Falloff.
// cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p12-13
half ratio = 1.0h / (range * d.centerDistInverse);
half ratio2 = ratio * ratio;
half num = saturate(1.0h - (ratio2 * ratio2));
d.color *= num * num;
#endif
}
void aUpdateLightingInputs(
inout ADirect d,
ASurface s)
{
d.halfAngleWorld = normalize(d.direction + s.viewDirWorld);
d.LdotH = aDotClamp(d.direction, d.halfAngleWorld);
d.NdotH = aDotClamp(s.normalWorld, d.halfAngleWorld);
}
void aSetAreaSpecularInputs(
inout ADirect d,
ASurface s,
half radius)
{
// Representative Point Area Lights.
// cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16
#ifdef A_AREA_SPECULAR_OFF
d.direction = d.Ldiff;
#else
float3 R = s.reflectionVectorWorld;
float3 centerToRay = dot(d.Lspec, R) * R - d.Lspec;
float3 closestPoint = d.Lspec + centerToRay * saturate(radius * rsqrt(dot(centerToRay, centerToRay)));
half LspecLengthInverse = rsqrt(dot(closestPoint, closestPoint));
half a = s.beckmannRoughness;
half normalizationFactor = a / saturate(a + (radius * 0.5h * LspecLengthInverse));
d.direction = closestPoint * LspecLengthInverse;
d.specularIntensity = normalizationFactor * normalizationFactor;
#endif
aUpdateLightingInputs(d, s);
}
half aSetAreaLightingInputs(
inout ADirect d,
ASurface s,
half radius)
{
// Specular.
aSetAreaSpecularInputs(d, s, radius);
// Diffuse.
// Set diffuse light direction last to fix transmission & hair.
half LdiffLengthSquared = dot(d.Ldiff, d.Ldiff);
half LdiffLengthInverse = rsqrt(LdiffLengthSquared);
d.direction = d.Ldiff * LdiffLengthInverse;
d.NdotLm = dot(s.normalWorld, d.direction);
d.NdotL = saturate(d.NdotLm);
#if !A_USE_UNITY_ATTENUATION
d.color /= (LdiffLengthSquared + 1.0h); // Attenuation.
#endif
return LdiffLengthInverse;
}
void aDirectionalLight(
inout ADirect d,
ASurface s)
{
d.NdotLm = dot(s.normalWorld, d.direction);
d.NdotL = saturate(d.NdotLm);
aUpdateLightingInputs(d, s);
}
void aDirectionalDiscLight(
inout ADirect d,
ASurface s,
half3 direction,
half radius)
{
d.Ldiff = direction;
d.Lspec = direction;
// Specular.
aSetAreaSpecularInputs(d, s, radius);
// Diffuse.
// Set diffuse light direction last to fix transmission & hair.
d.direction = d.Ldiff;
d.NdotLm = dot(s.normalWorld, d.direction);
d.NdotL = saturate(d.NdotLm);
}
void aSphereLight(
inout ADirect d,
ASurface s,
float3 L,
half radius)
{
// Sphere Area Light.
// cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p15-16
d.Ldiff = L;
d.Lspec = L;
d.centerDistInverse = aSetAreaLightingInputs(d, s, radius);
}
void aTubeLight(
inout ADirect d,
ASurface s,
float3 L,
half3 axis,
half radius,
half halfLength)
{
// Tube Area Light.
// cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p16-18
float3 R = s.reflectionVectorWorld;
float3 tubeLightDir = axis * halfLength;
float3 L0 = L + tubeLightDir;
float3 L1 = L - tubeLightDir;
float3 Ld = tubeLightDir * -2.0f;
float RdotL0 = dot(R, L0);
float RdotLd = dot(R, Ld);
float L0dotLd = dot(L0, Ld);
float t = (RdotL0 * RdotLd - L0dotLd) / (dot(Ld, Ld) - RdotLd * RdotLd);
// Modified diffuse term for true tube diffuse lighting.
d.Ldiff = L - clamp(dot(L, axis), -halfLength, halfLength) * axis;
d.Lspec = L0 + Ld * saturate(t);
d.centerDistInverse = rsqrt(dot(L, L));
// Attentuation normalization.
d.color /= 1.0h + (0.25h * halfLength * aSetAreaLightingInputs(d, s, radius));
}
void aAreaLight(
inout ADirect d,
ASurface s,
half4 color,
half3 axis,
float3 L,
half range)
{
// Packed float light configuration.
// +/- llll.rrrr: l=length, r=radius, and sign as specular toggle.
// Radius externally clamped to .999 max to simplify math.
half lightParams = abs(color.a);
#ifdef USING_DIRECTIONAL_LIGHT
// 0.1 needed to make material inspector look okay.
aDirectionalDiscLight(d, s, L, lightParams * 0.1h);
#else
#if !defined(SPOT) && A_USE_TUBE_LIGHTS
// Enable when length is non-zero, and specular is enabled.
UNITY_BRANCH
if (color.a >= 1) {
half dec = frac(lightParams);
half radius = dec * range;
half halfLength = (lightParams - dec) * 0.001f * range;
aTubeLight(d, s, L, axis, radius, halfLength);
}
else
#endif
{
aSphereLight(d, s, L, lightParams * range);
}
aSetLightRangeLimit(d, range);
#endif
// Specular highlight toggle.
d.specularIntensity = color.a > 0.0h ? d.specularIntensity : 0.0h;
}
ASurface aNewSurface() {
ASurface s;
UNITY_INITIALIZE_OUTPUT(ASurface, s);
s.vertexNormalWorld = A_FLAT_NORMAL;
s.normalWorld = A_FLAT_NORMAL;
s.viewDirWorld = A_AXIS_X;
s.viewDirTangent = A_AXIS_X;
s.reflectionVectorWorld = A_AXIS_X;
s.tangentToWorld = 0.0h;
s.normalTangent = A_FLAT_NORMAL;
s.blurredNormalTangent = A_FLAT_NORMAL;
s.facingSign = 1.0h;
s.fogCoord = 0.0f;
s.NdotV = 0.0h;
s.FV = 0.0h;
s.materialType = 1.0h;
s.mask = 1.0h;
s.baseColor = 0.0h;
s.opacity = 1.0h;
s.metallic = 0.0h;
s.ambientOcclusion = 1.0h;
s.specularity = 0.5h;
s.specularTint = 0.0h;
s.roughness = 0.0h;
s.emissiveColor = 0.0h;
s.subsurfaceColor = 0.0h;
s.subsurface = 0.0h;
s.clearCoat = 0.0h;
s.clearCoatRoughness = 0.0h;
return s;
}
void aBlendRangeMask(
inout ASurface s,
half mask,
half weight,
half cutoff,
half blendRange,
half vertexTint)
{
cutoff = lerp(cutoff, 1.0h, s.vertexColor.a * vertexTint);
mask = 1.0h - saturate((mask - cutoff) / blendRange);
s.mask *= weight * mask;
}
half3 aTangentToWorld(
ASurface s,
half3 normalTangent)
{
#ifndef A_TANGENT_TO_WORLD_ON
return s.vertexNormalWorld;
#else
return normalize(mul(normalTangent, s.tangentToWorld));
#endif
}
half3 aWorldToTangent(
ASurface s,
half3 normalWorld)
{
#ifndef A_TANGENT_TO_WORLD_ON
return A_FLAT_NORMAL;
#else
return normalize(mul(s.tangentToWorld, normalWorld));
#endif
}
void aUpdateViewData(
inout ASurface s)
{
#if defined(A_NORMAL_WORLD_ON) && defined(A_VIEW_DIR_WORLD_ON)
// Area lights need this to be per-pixel.
#ifdef A_REFLECTION_VECTOR_WORLD_ON
s.reflectionVectorWorld = reflect(-s.viewDirWorld, s.normalWorld);
#endif
// Skip re-calculating world normals in some cases.
#ifndef A_VIEW_DIR_TANGENT_ON
s.NdotV = aDotClamp(s.normalWorld, s.viewDirWorld);
#else
s.NdotV = aDotClamp(s.normalTangent, s.viewDirTangent);
#endif
s.FV = aFresnel(s.NdotV);
#endif
}
void aUpdateNormalTangent(
inout ASurface s)
{
#ifndef A_TANGENT_TO_WORLD_ON
s.normalTangent = A_FLAT_NORMAL;
#else
s.normalWorld = aTangentToWorld(s, s.normalTangent);
s.ambientNormalWorld = s.normalWorld;
aUpdateViewData(s);
#endif
}
void aUpdateNormalWorld(
inout ASurface s)
{
#ifndef A_TANGENT_TO_WORLD_ON
s.normalWorld = s.vertexNormalWorld;
#else
s.normalTangent = aWorldToTangent(s, s.normalWorld);
s.ambientNormalWorld = s.normalWorld;
aUpdateViewData(s);
#endif
}
void aUpdateSubsurface(
inout ASurface s)
{
s.subsurfaceColor = aGammaToLinear(s.subsurface).rrr;
}
void aUpdateSubsurfaceColor(
inout ASurface s)
{
s.subsurface = LinearToGammaSpace(s.subsurfaceColor).g;
}
half3 aSpecularityToF0(
half specularity)
{
return (specularity * A_MAX_DIELECTRIC_F0).rrr;
}
half aLinearToBeckmannRoughness(
half roughness)
{
// Remap roughness to prevent specular artifacts.
roughness = lerp(A_MIN_AREA_ROUGHNESS, 1.0h, roughness);
return roughness * roughness;
}
half aFresnel(
half w)
{
// Sebastien Lagarde's spherical gaussian approximation of Schlick fresnel.
// cf http://seblagarde.wordpress.com/2011/08/17/hello-world/
return exp2((-5.55473h * w - 6.98316h) * w);
}
half3 aSpecularLightingToggle(
ASurface s,
half3 specular)
{
#if !A_USE_BLACK_SPECULAR_COLOR_TOGGLE
return specular;
#else
return any(s.f0) ? specular : A_BLACK;
#endif
}
half aSpecularOcclusion(
half ao,
half NdotV)
{
// Yoshiharu Gotanda's specular occlusion approximation:
// cf http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx pg59
half d = NdotV + ao;
return saturate((d * d) - 1.0h + ao);
}
void aStandardPreLighting(
inout ASurface s)
{
// In Forward mode, set material type.
#ifndef A_DEFERRED_PASS
s.baseColor = saturate(s.baseColor);
s.albedo = s.baseColor;
s.f0 = aSpecularityToF0(s.specularity);
#ifndef A_SPECULAR_TINT_ON
s.specularTint = 0.0h;
#else
s.f0 *= aLerpWhiteTo(aChromaticity(s.baseColor), s.specularTint);
#endif
#ifndef A_METALLIC_ON
s.metallic = 0.0h;
#else
half metallicInv = 1.0h - s.metallic;
s.albedo *= metallicInv; // Affects transmission through albedo.
s.f0 = lerp(s.f0, s.baseColor, s.metallic);
#ifdef _ALPHAPREMULTIPLY_ON
// Interpolate from a translucent dielectric to an opaque metal.
s.opacity = s.metallic + metallicInv * s.opacity;
#endif
#endif
#ifndef A_CLEARCOAT_ON
s.clearCoat = 0.0h;
s.clearCoatRoughness = 0.0h;
#else
// f0 of 0.04 gives us a polyurethane-like coating.
half clearCoat = s.clearCoat * lerp(0.04h, 1.0h, s.FV);
s.albedo *= aLerpOneTo(0.0h, clearCoat);
s.f0 = lerp(s.f0, A_WHITE, clearCoat);
s.roughness = lerp(s.roughness, s.clearCoatRoughness, clearCoat);
#endif
#ifdef _ALPHAPREMULTIPLY_ON
// Premultiply opacity with albedo for translucent shaders.
s.albedo *= s.opacity;
#endif
s.beckmannRoughness = aLinearToBeckmannRoughness(s.roughness);
#ifndef A_AMBIENT_OCCLUSION_ON
s.ambientOcclusion = 1.0h;
s.specularOcclusion = 1.0h;
#else
s.specularOcclusion = aSpecularOcclusion(s.ambientOcclusion, s.NdotV);
#endif
#if defined(A_SCATTERING_ON)
s.materialType = A_MATERIAL_TYPE_SUBSURFACE_SCATTERING;
s._subsurfaceShadowWeight = 0.0h;
s._skinScatteringMask = 1.0h;
s.ambientNormalWorld = aTangentToWorld(s, s.blurredNormalTangent);
#elif defined(A_SUBSURFACE_ON)
#ifdef A_TWO_SIDED_SHADER
s.materialType = A_MATERIAL_TYPE_SHADOWED_SUBSURFACE;
s._subsurfaceShadowWeight = A_SUBSURFACE_SHADOW;
#else
s.materialType = _ShadowCullMode == A_CULL_MODE_FRONT ? A_MATERIAL_TYPE_SHADOWED_SUBSURFACE : A_MATERIAL_TYPE_UNSHADOWED_SUBSURFACE;
s._subsurfaceShadowWeight = _ShadowCullMode == A_CULL_MODE_FRONT ? A_SUBSURFACE_SHADOW : 0.0h;
#endif
#else
s.materialType = A_MATERIAL_TYPE_OPAQUE;
#endif
#endif
#if defined(A_SCATTERING_ON) || defined(A_SUBSURFACE_ON)
A_DEFERRED_BRANCH(s.materialType != A_MATERIAL_TYPE_OPAQUE)
{
// In Deferred mode, determine material type and sample extra G-buffer data.
#if defined(A_DEFERRED_PASS)
half4 buffer = tex2Dlod(_DeferredPlusBuffer, float4(s.screenUv, 0.0f, 0.0f));
s.subsurface = buffer.a;
s._subsurfaceShadowWeight = s.materialType == A_MATERIAL_TYPE_SHADOWED_SUBSURFACE ? A_SUBSURFACE_SHADOW : 0.0h;
#if defined(A_SCATTERING_ON)
s._skinScatteringMask = s.materialType == A_MATERIAL_TYPE_SUBSURFACE_SCATTERING ? 1.0h : 0.0h;
s.ambientNormalWorld = normalize(buffer.xyz * 2.0h - 1.0h);
#endif
#endif
// Subsurface color calculation.
#ifdef A_SUBSURFACE_ON
#ifdef A_FORWARD_ONLY_SHADER
s.subsurfaceColor *= s.albedo;
#else
s.subsurfaceColor = s.albedo * aGammaToLinear(s.subsurface);
#endif
#endif
// Scattering input data.
#ifdef A_SCATTERING_ON
A_DEFERRED_BRANCH(s.materialType == A_MATERIAL_TYPE_SUBSURFACE_SCATTERING)
{
// Scattering mask.
s._skinScatteringMask *= A_SCATTERING_WEIGHT * saturate(A_SCATTERING_INV_MASK_CUTOFF * s.subsurface);
s._skinScattering = saturate(s.subsurface * A_SCATTERING_SCALE + A_SCATTERING_BIAS);
// Skin subsurface depth absorption tint.
// cf http://www.crytek.com/download/2014_03_25_CRYENGINE_GDC_Schultz.pdf pg 35
half3 absorption = exp((1.0h - s.subsurface) * A_SCATTERING_ABSORPTION);
// Albedo scale for absorption assumes ~0.5 luminance for Caucasian skin.
absorption *= saturate(s.albedo * unity_ColorSpaceDouble.rgb);
s.subsurfaceColor = lerp(s.subsurfaceColor, absorption, s._skinScatteringMask);
// Blurred normals for indirect diffuse and direct scattering.
s.ambientNormalWorld = normalize(lerp(s.normalWorld, s.ambientNormalWorld, A_SCATTERING_NORMAL_BLUR * s._skinScatteringMask));
}
#endif
// Subsurface color weight.
#ifdef A_SUBSURFACE_ON
s.subsurfaceColor *= A_SUBSURFACE_WEIGHT;
#endif
}
#endif
}
half3 aStandardDirectLighting(
ADirect d,
ASurface s)
{
half3 illum = A_BLACK;
half3 specular = A_BLACK;
#if defined(A_SCATTERING_ON) || defined(A_SUBSURFACE_ON)
A_DEFERRED_BRANCH(s.materialType != A_MATERIAL_TYPE_OPAQUE)
{
#ifdef A_SUBSURFACE_ON
// Subsurface transmission.
// cf http://www.farfarer.com/blog/2012/09/11/translucent-shader-unity3d/
half3 transLightDir = d.direction + s.normalWorld * A_SUBSURFACE_DISTORTION;
half transLight = pow(aDotClamp(s.viewDirWorld, -transLightDir), A_SUBSURFACE_FALLOFF);
half shadow = aLerpOneTo(d.shadow, s._subsurfaceShadowWeight);
illum += s.subsurfaceColor * (shadow * transLight);
#endif
#ifdef A_SCATTERING_ON
A_DEFERRED_BRANCH(s.materialType == A_MATERIAL_TYPE_SUBSURFACE_SCATTERING)
{
// Pre-Integrated Skin Shading.
// cf http://www.farfarer.com/blog/2013/02/11/pre-integrated-skin-shader-unity-3d/
float ndlBlur = dot(s.ambientNormalWorld, d.direction) * 0.5h + 0.5h;
float4 sssLookupUv = float4(ndlBlur, s._skinScattering * aLuminance(d.color), 0.0f, 0.0f);
half3 sss = (s._skinScatteringMask * d.shadow) * tex2Dlod(A_SCATTERING_LUT, sssLookupUv).rgb;
illum += s.albedo * sss;
s.albedo *= 1.0h - s._skinScatteringMask;
}
#endif
}
#endif
// Cook-Torrance microfacet model.
// cf http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
half LdotH2 = d.LdotH * d.LdotH;
// Brent Burley diffuse BRDF.
// cf https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf pg14
half FL = aFresnel(d.NdotL);
half Fd90 = 0.5h + (2.0h * LdotH2 * s.roughness);
half Fd = aLerpOneTo(Fd90, FL) * aLerpOneTo(Fd90, s.FV);
half3 diffuse = s.albedo * Fd;
#ifndef _SPECULARHIGHLIGHTS_OFF
// Schlick's Fresnel approximation.
half3 F = lerp(s.f0, A_WHITE, aFresnel(d.LdotH));
// John Hable's Visibility function.
// cf http://www.filmicworlds.com/2014/04/21/optimizing-ggx-shaders-with-dotlh/
half a2 = s.beckmannRoughness * s.beckmannRoughness;
half k2 = a2 * 0.25h; // k = a/2; k*k = (a*a)/(2*2) = (a^2)/4.
half invV = lerp(k2, 1.0h, LdotH2);
// GGX (Trowbridge-Reitz) NDF.
// cf http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
half denom = aLerpOneTo(a2, d.NdotH * d.NdotH);
half mDV = k2 / (invV * denom * denom); // k2 is GGX a^2 and microfacet 1/4.
specular = aSpecularLightingToggle(s, F * (mDV * s.specularOcclusion * d.specularIntensity));
#endif
// Punctual lighting equation.
// cf http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
illum += (d.shadow * d.NdotL) * (diffuse + specular);
return d.color * illum;
}
half3 aStandardIndirectLighting(
AIndirect i,
ASurface s)
{
half3 illum = A_BLACK;
#if defined(A_DEFERRED_PASS) || defined(A_REFLECTION_PROBES_ON)
// Brian Karis' modification of Dimitar Lazarov's Environment BRDF.
// cf https://www.unrealengine.com/blog/physically-based-shading-on-mobile
const half4 c0 = half4(-1.0h, -0.0275h, -0.572h, 0.022h);
const half4 c1 = half4(1.0h, 0.0425h, 1.04h, -0.04h);
half4 r = s.roughness * c0 + c1;
half a004 = min(r.x * r.x, exp2(-9.28h * s.NdotV)) * r.x + r.y;
half2 AB = half2(-1.04h, 1.04h) * a004 + r.zw;
half3 specular = i.specular * (s.f0 * AB.x + AB.yyy);
#endif
#ifdef A_DEFERRED_PASS
illum = aSpecularLightingToggle(s, specular * s.specularOcclusion);
#else
#ifndef A_AMBIENT_OCCLUSION_ON
half3 diffuse = s.albedo * i.diffuse;
#ifndef A_REFLECTION_PROBES_ON
illum = diffuse;
#else
illum = diffuse + aSpecularLightingToggle(s, specular);
#endif
#else
#ifndef A_SCATTERING_ON
half ao = s.ambientOcclusion;
#else
// Color Bleed AO.
// cf http://www.iryoku.com/downloads/Next-Generation-Character-Rendering-v6.pptx pg113
half3 ao = pow(s.ambientOcclusion.rrr, A_ONE - (A_SCATTERING_AO_COLOR_BLEED * s._skinScatteringMask));
#endif
// Yoshiharu Gotanda's fake interreflection for specular occlusion.
// Modified to better account for surface f0.
// cf http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx pg65
half3 ambient = i.diffuse * ao;
#ifndef A_REFLECTION_PROBES_ON
// Diffuse and fake interreflection only.
illum = ambient * (s.albedo + aSpecularLightingToggle(s, s.f0 * (1.0h - s.specularOcclusion)));
#else
// Full equation.
illum = ambient * s.albedo
+ aSpecularLightingToggle(s, lerp(ambient * s.f0, specular, s.specularOcclusion));
#endif
#endif
#endif
return illum;
}
#endif // ALLOY_SHADERS_FRAMEWORK_LIGHTING_IMPL_CGINC