334 lines
12 KiB
HLSL
334 lines
12 KiB
HLSL
#if !defined(SHADERGRAPH_PREVIEW) || defined(LIGHTWEIGHT_LIGHTING_INCLUDED)
|
|
|
|
// As we do not have access to the vertex lights we will make the shader always sample add lights per pixel
|
|
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
|
|
#undef _ADDITIONAL_LIGHTS_VERTEX
|
|
#define _ADDITIONAL_LIGHTS
|
|
#endif
|
|
|
|
#if defined(LIGHTWEIGHT_LIGHTING_INCLUDED) || defined(UNIVERSAL_LIGHTING_INCLUDED)
|
|
|
|
struct AdditionalData {
|
|
half coatThickness;
|
|
half3 coatSpecular;
|
|
half3 normalWS;
|
|
half perceptualRoughness;
|
|
half roughness;
|
|
half roughness2;
|
|
half normalizationTerm;
|
|
half roughness2MinusOne; // roughness² - 1.0
|
|
half reflectivity;
|
|
half grazingTerm;
|
|
half specOcclusion;
|
|
|
|
bool enableSecondaryLobe;
|
|
};
|
|
|
|
half3 DirectBDRF_LuxClearCoat(BRDFData brdfData, AdditionalData addData, half3 normalWS, half3 lightDirectionWS, half3 viewDirectionWS, half NdotL)
|
|
{
|
|
#ifndef _SPECULARHIGHLIGHTS_OFF
|
|
|
|
float3 halfDir = SafeNormalize(lightDirectionWS + viewDirectionWS);
|
|
half LoH = saturate(dot(lightDirectionWS, halfDir));
|
|
|
|
// Base Lobe
|
|
float NoH = saturate(dot(normalWS, halfDir));
|
|
float d = NoH * NoH * brdfData.roughness2MinusOne + 1.00001f;
|
|
half LoH2 = LoH * LoH;
|
|
LoH2 = max(0.1h, LoH2); // as we can reuse it
|
|
half specularTerm = brdfData.roughness2 / ((d * d) * LoH2 /* max(0.1h, LoH2) */ * brdfData.normalizationTerm);
|
|
#if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
|
|
specularTerm = specularTerm - HALF_MIN;
|
|
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
|
|
#endif
|
|
|
|
half3 spec = specularTerm * brdfData.specular * NdotL;
|
|
|
|
// Coat Lobe
|
|
|
|
// From HDRP: Scale base specular
|
|
|
|
//#if defined (_MASKMAP) && defined(_STANDARDLIGHTING)
|
|
// [branch]
|
|
// if (addData.coatThickness > 0.0h) {
|
|
//#endif
|
|
half coatF = F_Schlick(addData.reflectivity /*addData.coatSpecular*/ /*CLEAR_COAT_F0*/, LoH) * addData.coatThickness;
|
|
spec *= Sq(1.0h - coatF);
|
|
//spec *= (1.0h - coatF); // as used by filament, na, not really
|
|
NoH = saturate(dot(addData.normalWS, halfDir));
|
|
d = NoH * NoH * addData.roughness2MinusOne + 1.00001f;
|
|
//LoH2 = LoH * LoH; no need to recalculate LoH2!
|
|
specularTerm = addData.roughness2 / ((d * d) * LoH2 /* max(0.1h, LoH2) */ * addData.normalizationTerm);
|
|
#if defined (SHADER_API_MOBILE) || defined (SHADER_API_SWITCH)
|
|
specularTerm = specularTerm - HALF_MIN;
|
|
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
|
|
#endif
|
|
spec += specularTerm * addData.coatSpecular * saturate(dot(addData.normalWS, lightDirectionWS));
|
|
//#if defined (_MASKMAP) && defined(_STANDARDLIGHTING)
|
|
// }
|
|
//#endif
|
|
|
|
half3 color = spec + brdfData.diffuse * NdotL; // * lerp(1.0h, 1.0h - coatF, addData.coatThickness);
|
|
return color;
|
|
#else
|
|
return brdfData.diffuse * NdotL;
|
|
#endif
|
|
}
|
|
|
|
half3 LightingPhysicallyBased_LuxClearCoat(BRDFData brdfData, AdditionalData addData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS)
|
|
{
|
|
half NdotL = saturate(dot(normalWS, lightDirectionWS));
|
|
half3 radiance = lightColor * (lightAttenuation); // * NdotL);
|
|
return DirectBDRF_LuxClearCoat(brdfData, addData, normalWS, lightDirectionWS, viewDirectionWS, NdotL) * radiance;
|
|
}
|
|
|
|
half3 LightingPhysicallyBased_LuxClearCoat(BRDFData brdfData, AdditionalData addData, Light light, half3 normalWS, half3 viewDirectionWS)
|
|
{
|
|
return LightingPhysicallyBased_LuxClearCoat(brdfData, addData, light.color, light.direction, light.distanceAttenuation * light.shadowAttenuation, normalWS, viewDirectionWS);
|
|
}
|
|
|
|
half3 EnvironmentBRDF_LuxClearCoat(BRDFData brdfData, AdditionalData addData, half3 indirectDiffuse, half3 indirectSpecular, half fresnelTerm)
|
|
{
|
|
half3 c = indirectDiffuse * brdfData.diffuse;
|
|
float surfaceReduction = 1.0 / (addData.roughness2 + 1.0);
|
|
c += surfaceReduction * indirectSpecular * lerp(addData.coatSpecular, addData.grazingTerm, fresnelTerm);
|
|
return c;
|
|
}
|
|
|
|
|
|
half3 GlobalIllumination_LuxClearCoat(BRDFData brdfData, AdditionalData addData, half3 bakedGI, half occlusion, half3 normalWS, half3 baseNormalWS, half3 viewDirectionWS, half NdotV)
|
|
{
|
|
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
|
|
half fresnelTerm = Pow4(1.0h - NdotV);
|
|
|
|
half3 indirectDiffuse = bakedGI * occlusion;
|
|
half3 indirectSpecular = GlossyEnvironmentReflection(reflectVector, addData.perceptualRoughness, addData.specOcclusion);
|
|
half3 res = EnvironmentBRDF_LuxClearCoat(brdfData, addData, indirectDiffuse, indirectSpecular, fresnelTerm);
|
|
|
|
//#if defined(_SECONDARYLOBE)
|
|
if (addData.enableSecondaryLobe) {
|
|
#if defined (_MASKMAP) && defined(_STANDARDLIGHTING)
|
|
[branch]
|
|
if (addData.coatThickness > 0.0h) {
|
|
#endif
|
|
reflectVector = reflect(-viewDirectionWS, baseNormalWS);
|
|
indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, 1);
|
|
float surfaceReduction = 1.0 / (brdfData.roughness2 + 1.0);
|
|
res += NdotV * surfaceReduction * indirectSpecular * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm);
|
|
#if defined (_MASKMAP) && defined(_STANDARDLIGHTING)
|
|
}
|
|
#endif
|
|
//#endif
|
|
}
|
|
return res;
|
|
}
|
|
|
|
half3 f0ClearCoatToSurface_Lux(half3 f0)
|
|
{
|
|
// Approximation of iorTof0(f0ToIor(f0), 1.5)
|
|
// This assumes that the clear coat layer has an IOR of 1.5
|
|
#if defined(SHADER_API_MOBILE)
|
|
return saturate(f0 * (f0 * 0.526868h + 0.529324h) - 0.0482256h);
|
|
#else
|
|
return saturate(f0 * (f0 * (0.941892h - 0.263008h * f0) + 0.346479h) - 0.0285998h);
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
#endif
|
|
|
|
|
|
void Lighting_half(
|
|
|
|
// Base inputs
|
|
float3 positionWS,
|
|
half3 viewDirectionWS,
|
|
|
|
// Normal inputs
|
|
half3 normalWS,
|
|
half3 tangentWS,
|
|
half3 bitangentWS,
|
|
bool enableNormalMapping,
|
|
half3 normalTS,
|
|
|
|
// Surface description
|
|
half3 albedo, // albedo is baseColor
|
|
half metallic,
|
|
half3 specular,
|
|
half smoothness,
|
|
half occlusion,
|
|
half alpha,
|
|
|
|
// Lighting specific inputs
|
|
half clearcoatSmoothness,
|
|
half clearcoatThickness,
|
|
half3 clearcoatSpecular,
|
|
|
|
half3 secondaryColor,
|
|
|
|
bool enableSecondaryColor,
|
|
bool enableSecondaryLobe,
|
|
|
|
// Lightmapping
|
|
float2 lightMapUV,
|
|
|
|
// Final lit color
|
|
out half3 MetaAlbedo,
|
|
out half3 FinalLighting,
|
|
out half3 MetaSpecular
|
|
)
|
|
{
|
|
|
|
//#ifdef SHADERGRAPH_PREVIEW
|
|
#if defined(SHADERGRAPH_PREVIEW) || ( !defined(LIGHTWEIGHT_LIGHTING_INCLUDED) && !defined(UNIVERSAL_LIGHTING_INCLUDED) )
|
|
FinalLighting = albedo;
|
|
MetaAlbedo = half3(0,0,0);
|
|
MetaSpecular = half3(0,0,0);
|
|
#else
|
|
|
|
|
|
// Real Lighting ----------
|
|
|
|
// Cache the geometry normal used by the coat
|
|
half3 vertexNormalWS = NormalizeNormalPerPixel(normalWS);
|
|
|
|
if (enableNormalMapping) {
|
|
normalWS = TransformTangentToWorld(normalTS, half3x3(tangentWS.xyz, bitangentWS.xyz, normalWS.xyz));
|
|
}
|
|
normalWS = NormalizeNormalPerPixel(normalWS);
|
|
viewDirectionWS = SafeNormalize(viewDirectionWS);
|
|
|
|
// GI Lighting
|
|
half3 bakedGI;
|
|
#ifdef LIGHTMAP_ON
|
|
lightMapUV = lightMapUV * unity_LightmapST.xy + unity_LightmapST.zw;
|
|
bakedGI = SAMPLE_GI(lightMapUV, half3(0,0,0), normalWS);
|
|
#else
|
|
bakedGI = SampleSH(normalWS);
|
|
#endif
|
|
|
|
// Clear Coat Lighting
|
|
|
|
half NdotV = saturate( dot(vertexNormalWS, viewDirectionWS) );
|
|
#if !defined(LIGHTWEIGHT_META_PASS_INCLUDED) && !defined(UNIVERSAL_META_PASS_INCLUDED)
|
|
if(enableSecondaryColor) {
|
|
albedo = lerp(secondaryColor, albedo, NdotV);
|
|
}
|
|
#endif
|
|
|
|
BRDFData brdfData;
|
|
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
|
|
|
|
// Adjust specular as we have a transition from coat to material and not air to material
|
|
brdfData.specular = lerp(brdfData.specular, f0ClearCoatToSurface_Lux(brdfData.specular), clearcoatThickness);
|
|
|
|
|
|
AdditionalData addData;
|
|
addData.coatThickness = clearcoatThickness;
|
|
addData.coatSpecular = clearcoatSpecular;
|
|
addData.normalWS = vertexNormalWS;
|
|
addData.perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(clearcoatSmoothness);
|
|
addData.roughness = PerceptualRoughnessToRoughness(addData.perceptualRoughness);
|
|
addData.roughness2 = addData.roughness * addData.roughness;
|
|
addData.normalizationTerm = addData.roughness * 4.0h + 2.0h;
|
|
addData.roughness2MinusOne = addData.roughness2 - 1.0h;
|
|
addData.reflectivity = ReflectivitySpecular(clearcoatSpecular);
|
|
addData.grazingTerm = saturate(clearcoatSmoothness + addData.reflectivity);
|
|
addData.specOcclusion = 1;
|
|
|
|
addData.enableSecondaryLobe = enableSecondaryLobe;
|
|
|
|
|
|
// Get Shadow Sampling Coords / Unfortunately per pixel...
|
|
#if SHADOWS_SCREEN
|
|
float4 clipPos = TransformWorldToHClip(positionWS);
|
|
float4 shadowCoord = ComputeScreenPos(clipPos);
|
|
#else
|
|
float4 shadowCoord = TransformWorldToShadowCoord(positionWS);
|
|
#endif
|
|
|
|
Light mainLight = GetMainLight(shadowCoord);
|
|
MixRealtimeAndBakedGI(mainLight, normalWS, bakedGI, half4(0, 0, 0, 0));
|
|
|
|
// Approximation of refraction on BRDF
|
|
half refractionScale = ((NdotV * 0.5 + 0.5) * NdotV - 1.0) * saturate(1.25 - 1.25 * (1.0 - clearcoatSmoothness)) + 1;
|
|
brdfData.diffuse = lerp(brdfData.diffuse, brdfData.diffuse * refractionScale, clearcoatThickness);
|
|
|
|
// GI
|
|
FinalLighting = GlobalIllumination_LuxClearCoat(brdfData, addData, bakedGI, occlusion, addData.normalWS, normalWS, viewDirectionWS, NdotV);
|
|
|
|
// Main Light
|
|
FinalLighting += LightingPhysicallyBased_LuxClearCoat(brdfData, addData, mainLight, normalWS, viewDirectionWS);
|
|
|
|
// Handle additional lights
|
|
#ifdef _ADDITIONAL_LIGHTS
|
|
uint pixelLightCount = GetAdditionalLightsCount();
|
|
for (uint i = 0u; i < pixelLightCount; ++i) {
|
|
Light light = GetAdditionalLight(i, positionWS);
|
|
FinalLighting += LightingPhysicallyBased_LuxClearCoat(brdfData, addData, light, normalWS, viewDirectionWS);
|
|
}
|
|
#endif
|
|
|
|
// Set Albedo for meta pass
|
|
#if defined(LIGHTWEIGHT_META_PASS_INCLUDED) || defined(UNIVERSAL_META_PASS_INCLUDED)
|
|
FinalLighting = half3(0,0,0);
|
|
MetaAlbedo = albedo;
|
|
MetaSpecular = specular;
|
|
#else
|
|
MetaAlbedo = half3(0,0,0);
|
|
MetaSpecular = half3(0,0,0);
|
|
#endif
|
|
|
|
// End Real Lighting ----------
|
|
|
|
#endif
|
|
}
|
|
|
|
// Unity 2019.1. needs a float version
|
|
|
|
void Lighting_float(
|
|
|
|
// Base inputs
|
|
float3 positionWS,
|
|
half3 viewDirectionWS,
|
|
|
|
// Normal inputs
|
|
half3 normalWS,
|
|
half3 tangentWS,
|
|
half3 bitangentWS,
|
|
bool enableNormalMapping,
|
|
half3 normalTS,
|
|
|
|
// Surface description
|
|
half3 albedo,
|
|
half metallic,
|
|
half3 specular,
|
|
half smoothness,
|
|
half occlusion,
|
|
half alpha,
|
|
|
|
// Lighting specific inputs
|
|
half clearcoatSmoothness,
|
|
half clearcoatThickness,
|
|
half3 clearcoatSpecular,
|
|
|
|
half3 secondaryColor,
|
|
|
|
bool enableSecondaryColor,
|
|
bool enableSecondaryLobe,
|
|
|
|
// Lightmapping
|
|
float2 lightMapUV,
|
|
|
|
// Final lit color
|
|
out half3 MetaAlbedo,
|
|
out half3 FinalLighting,
|
|
out half3 MetaSpecular
|
|
)
|
|
{
|
|
Lighting_half(
|
|
positionWS, viewDirectionWS, normalWS, tangentWS, bitangentWS, enableNormalMapping, normalTS,
|
|
albedo, metallic, specular, smoothness, occlusion, alpha,
|
|
clearcoatSmoothness, clearcoatThickness, clearcoatSpecular, secondaryColor, enableSecondaryColor, enableSecondaryLobe,
|
|
lightMapUV, MetaAlbedo, FinalLighting, MetaSpecular);
|
|
} |