您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 

425 行
18 KiB

#ifndef LIGHTWEIGHT_LIGHTING_INCLUDED
#define LIGHTWEIGHT_LIGHTING_INCLUDED
#include "LightweightCore.cginc"
#include "LightweightShadows.cginc"
#define PI 3.14159265359f
#define kDieletricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)
#ifndef UNITY_SPECCUBE_LOD_STEPS
#define UNITY_SPECCUBE_LOD_STEPS 6
#endif
#ifdef NO_ADDITIONAL_LIGHTS
#undef _ADDITIONAL_LIGHTS
#endif
// If lightmap is not defined than we evaluate GI (ambient + probes) from SH
// We might do it fully or partially in vertex to save shader ALU
#if !defined(LIGHTMAP_ON)
#if SHADER_TARGET < 30
// Evaluates SH fully in vertex
#define EVALUATE_SH_VERTEX
#else
// Evaluates L2 SH in vertex and L0L1 in pixel
#define EVALUATE_SH_MIXED
#endif
#endif
///////////////////////////////////////////////////////////////////////////////
// BRDF Functions //
///////////////////////////////////////////////////////////////////////////////
struct BRDFData
{
half3 diffuse;
half3 specular;
half perceptualRoughness;
half roughness;
half grazingTerm;
};
half ReflectivitySpecular(half3 specular)
{
#if (SHADER_TARGET < 30)
// SM2.0: instruction count limitation
return specular.r; // Red channel - because most metals are either monocrhome or with redish/yellowish tint
#else
return max(max(specular.r, specular.g), specular.b);
#endif
}
half OneMinusReflectivityMetallic(half metallic)
{
// We'll need oneMinusReflectivity, so
// 1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)
// store (1-dielectricSpec) in kDieletricSpec.a, then
// 1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =
// = alpha - metallic * alpha
half oneMinusDielectricSpec = kDieletricSpec.a;
return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}
inline void InitializeBRDFData(half3 albedo, half metallic, half3 specular, half smoothness, half alpha, out BRDFData outBRDFData)
{
#ifdef _SPECULAR_SETUP
half reflectivity = ReflectivitySpecular(specular);
half oneMinusReflectivity = 1.0 - reflectivity;
outBRDFData.diffuse = albedo * (half3(1.0h, 1.0h, 1.0h) - specular);
outBRDFData.specular = specular;
#else
half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
half reflectivity = 1.0 - oneMinusReflectivity;
outBRDFData.diffuse = albedo * oneMinusReflectivity;
outBRDFData.specular = lerp(kDieletricSpec.rgb, albedo, metallic);
#endif
outBRDFData.grazingTerm = saturate(smoothness + reflectivity);
outBRDFData.perceptualRoughness = 1.0h - smoothness;
outBRDFData.roughness = outBRDFData.perceptualRoughness * outBRDFData.perceptualRoughness;
#ifdef _ALPHAPREMULTIPLY_ON
outBRDFData.diffuse *= alpha;
alpha = alpha * oneMinusReflectivity + reflectivity;
#endif
}
half3 LightweightEnvironmentBRDF(BRDFData brdfData, half3 indirectDiffuse, half3 indirectSpecular, half roughness2, half fresnelTerm)
{
half3 c = indirectDiffuse * brdfData.diffuse;
float surfaceReduction = 1.0 / (roughness2 + 1.0);
c += surfaceReduction * indirectSpecular * lerp(brdfData.specular, brdfData.grazingTerm, fresnelTerm);
return c;
}
// Based on Minimalist CookTorrance BRDF
// Implementation is slightly different from original derivation: http://www.thetenthplanet.de/archives/255
//
// * NDF [Modified] GGX
// * Modified Kelemen and Szirmay-​Kalos for Visibility term
// * Fresnel approximated with 1/LdotH
half3 LightweightDirectBDRF(BRDFData brdfData, half roughness2, half3 normal, half3 lightDirection, half3 viewDir)
{
#ifndef _SPECULARHIGHLIGHTS_OFF
half3 halfDir = SafeNormalize(lightDirection + viewDir);
half NoH = saturate(dot(normal, halfDir));
half LoH = saturate(dot(lightDirection, halfDir));
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
// https://community.arm.com/events/1155
half d = NoH * NoH * (roughness2 - 1.h) + 1.00001h;
half LoH2 = LoH * LoH;
half specularTerm = roughness2 / ((d * d) * max(0.1h, LoH2) * (brdfData.roughness + 0.5h) * 4);
// on mobiles (where half actually means something) denominator have risk of overflow
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
#if defined (SHADER_API_MOBILE)
specularTerm = specularTerm - 1e-4h;
#endif
#if defined (SHADER_API_MOBILE)
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
#endif
half3 color = specularTerm * brdfData.specular + brdfData.diffuse;
return color;
#else
return brdfData.diffuse;
#endif
}
///////////////////////////////////////////////////////////////////////////////
// Attenuation Functions /
///////////////////////////////////////////////////////////////////////////////
half CookieAttenuation(float3 worldPos)
{
#ifdef _MAIN_LIGHT_COOKIE
#ifdef _MAIN_DIRECTIONAL_LIGHT
float2 cookieUV = mul(_WorldToLight, float4(worldPos, 1.0)).xy;
return tex2D(_MainLightCookie, cookieUV).a;
#elif defined(_MAIN_SPOT_LIGHT)
float4 projPos = mul(_WorldToLight, float4(worldPos, 1.0));
float2 cookieUV = projPos.xy / projPos.w + 0.5;
return tex2D(_MainLightCookie, cookieUV).a;
#endif // POINT LIGHT cookie not supported
#endif
return 1;
}
// Matches Unity Vanila attenuation
// Attenuation smoothly decreases to light range.
half DistanceAttenuation(half3 distanceSqr, half4 distanceAttenuation)
{
// We use a shared distance attenuation for additional directional and puctual lights
// for directional lights attenuation will be 1
half quadFalloff = distanceAttenuation.x;
half denom = distanceSqr * quadFalloff + 1.0;
half lightAtten = 1.0 / denom;
// We need to smoothly fade attenuation to light range. We start fading linearly at 80% of light range
// Therefore:
// fadeDistance = (0.8 * 0.8 * lightRangeSq)
// smoothFactor = (lightRangeSqr - distanceSqr) / (lightRangeSqr - fadeDistance)
// We can rewrite that to fit a MAD by doing
// distanceSqr * (1.0 / (fadeDistanceSqr - lightRangeSqr)) + (-lightRangeSqr / (fadeDistanceSqr - lightRangeSqr)
// distanceSqr * distanceAttenuation.y + distanceAttenuation.z
half smoothFactor = saturate(distanceSqr * distanceAttenuation.y + distanceAttenuation.z);
return lightAtten * smoothFactor;
}
half SpotAttenuation(half3 spotDirection, half3 lightDirection, half4 spotAttenuation)
{
// Spot Attenuation with a linear falloff can be defined as
// (SdotL - cosOuterAngle) / (cosInnerAngle - cosOuterAngle)
// This can be rewritten as
// invAngleRange = 1.0 / (cosInnerAngle - cosOuterAngle)
// SdotL * invAngleRange + (-cosOuterAngle * invAngleRange)
// SdotL * spotAttenuation.x + spotAttenuation.y
// If we precompute the terms in a MAD instruction
half SdotL = dot(spotDirection, lightDirection);
return saturate(SdotL * spotAttenuation.x + spotAttenuation.y);
}
inline half GetLightDirectionAndRealtimeAttenuation(LightInput lightInput, half3 normal, float3 worldPos, out half3 lightDirection)
{
float3 posToLightVec = lightInput.pos.xyz - worldPos * lightInput.pos.w;
float distanceSqr = max(dot(posToLightVec, posToLightVec), 0.001);
// normalized light dir
lightDirection = half3(posToLightVec * rsqrt(distanceSqr));
half lightAtten = DistanceAttenuation(distanceSqr, lightInput.distanceAttenuation);
lightAtten *= SpotAttenuation(lightInput.spotDirection.xyz, lightDirection, lightInput.spotAttenuation);
return lightAtten;
}
inline half GetMainLightDirectionAndRealtimeAttenuation(LightInput lightInput, half3 normalWS, float3 positionWS, out half3 lightDirection)
{
#ifdef _MAIN_DIRECTIONAL_LIGHT
// Light pos holds normalized light dir
lightDirection = lightInput.pos;
half attenuation = 1.0;
#else
half attenuation = GetLightDirectionAndRealtimeAttenuation(lightInput, normalWS, positionWS, lightDirection);
#endif
// Cookies and shadows are only computed for main light
attenuation *= CookieAttenuation(positionWS);
attenuation *= LIGHTWEIGHT_SHADOW_ATTENUATION(positionWS, normalWS, lightDirection);
return attenuation;
}
///////////////////////////////////////////////////////////////////////////////
// Lighting Functions //
///////////////////////////////////////////////////////////////////////////////
half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal)
{
half NdotL = saturate(dot(normal, lightDir));
return lightColor * NdotL;
}
half3 LightingSpecular(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specularGloss, half shininess)
{
half3 halfVec = SafeNormalize(lightDir + viewDir);
half NdotH = saturate(dot(normal, halfVec));
half3 specularReflection = specularGloss.rgb * pow(NdotH, shininess) * specularGloss.a;
return lightColor * specularReflection;
}
half3 VertexLighting(float3 positionWS, half3 normalWS)
{
half3 vertexLightColor = half3(0.0, 0.0, 0.0);
#if defined(_VERTEX_LIGHTS)
int vertexLightStart = _AdditionalLightCount.x;
int vertexLightEnd = min(_AdditionalLightCount.y, unity_LightIndicesOffsetAndCount.y);
for (int lightIter = vertexLightStart; lightIter < vertexLightEnd; ++lightIter)
{
LightInput light;
INITIALIZE_LIGHT(light, lightIter);
half3 lightDirection;
half atten = GetLightDirectionAndRealtimeAttenuation(light, normalWS, positionWS, lightDirection);
half3 lightColor = light.color * atten;
vertexLightColor += LightingLambert(lightColor, lightDirection, normalWS);
}
#endif
return vertexLightColor;
}
///////////////////////////////////////////////////////////////////////////////
// Global Illumination //
///////////////////////////////////////////////////////////////////////////////
half3 DiffuseGI(half3 indirectDiffuse, half3 lambert, half mainLightRealtimeAttenuation, half occlusion)
{
// If shadows and mixed subtractive mode is enabled we need to remove direct
// light contribution from lightmap from occluded pixels so we can have dynamic objects
// casting shadows onto static correctly.
#if defined(_MIXED_LIGHTING_SUBTRACTIVE) && defined(LIGHTMAP_ON) && defined(_SHADOWS)
indirectDiffuse = SubtractDirectMainLightFromLightmap(indirectDiffuse, mainLightRealtimeAttenuation, lambert);
#endif
return indirectDiffuse * occlusion;
}
half3 GlossyEnvironmentReflection(half3 viewDirectionWS, half3 normalWS, half perceptualRoughness, half occlusion)
{
half3 reflectVector = reflect(-viewDirectionWS, normalWS);
#if !defined(_GLOSSYREFLECTIONS_OFF)
half roughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness);
half mip = roughness * UNITY_SPECCUBE_LOD_STEPS;
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflectVector, mip);
return DecodeHDR(rgbm, unity_SpecCube0_HDR) * occlusion;
#endif
return _GlossyEnvironmentColor * occlusion;
}
///////////////////////////////////////////////////////////////////////////////
// Fragment Functions //
// Used by ShaderGraph and others builtin renderers //
///////////////////////////////////////////////////////////////////////////////
half4 LightweightFragmentPBR(float3 positionWS, half3 normalWS, half3 viewDirectionWS,
half3 bakedGI, half3 vertexLighting, half3 albedo, half metallic, half3 specular,
half smoothness, half occlusion, half3 emission, half alpha)
{
half4 bakedOcclusion = half4(0, 0, 0, 0);
BRDFData brdfData;
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
half3 lightDirectionWS;
LightInput mainLight;
INITIALIZE_MAIN_LIGHT(mainLight);
// No distance fade.
half realtimeMainLightAtten = GetMainLightDirectionAndRealtimeAttenuation(mainLight, normalWS, positionWS, lightDirectionWS);
half NdotL = saturate(dot(normalWS, lightDirectionWS));
half3 radiance = mainLight.color * NdotL;
half3 indirectDiffuse = DiffuseGI(bakedGI, radiance, realtimeMainLightAtten, occlusion);
half3 indirectSpecular = GlossyEnvironmentReflection(viewDirectionWS, normalWS, brdfData.perceptualRoughness, occlusion);
half roughness2 = brdfData.roughness * brdfData.roughness;
half fresnelTerm = _Pow4(1.0 - saturate(dot(normalWS, viewDirectionWS)));
half3 color = LightweightEnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, roughness2, fresnelTerm);
half mainLightAtten = MixRealtimeAndBakedOcclusion(realtimeMainLightAtten, bakedOcclusion, mainLight.distanceAttenuation);
radiance *= mainLightAtten;
color += LightweightDirectBDRF(brdfData, roughness2, normalWS, lightDirectionWS, viewDirectionWS) * radiance;
color += vertexLighting * brdfData.diffuse;
#ifdef _ADDITIONAL_LIGHTS
int pixelLightCount = min(_AdditionalLightCount.x, unity_LightIndicesOffsetAndCount.y);
for (int lightIter = 0; lightIter < pixelLightCount; ++lightIter)
{
LightInput light;
INITIALIZE_LIGHT(light, lightIter);
half lightAttenuation = GetLightDirectionAndRealtimeAttenuation(light, normalWS, positionWS, lightDirectionWS);
lightAttenuation = MixRealtimeAndBakedOcclusion(lightAttenuation, bakedOcclusion, light.distanceAttenuation);
half NdotL = saturate(dot(normalWS, lightDirectionWS));
half3 radiance = light.color * (lightAttenuation * NdotL);
color += LightweightDirectBDRF(brdfData, roughness2, normalWS, lightDirectionWS, viewDirectionWS) * radiance;
}
#endif
color += emission;
return half4(color, alpha);
}
half4 LightweightFragmentLambert(float3 positionWS, half3 normalWS, half3 viewDirectionWS,
half fogFactor, half3 diffuseGI, half3 diffuse, half3 emission, half alpha)
{
half4 bakedOcclusion = half4(0, 0, 0, 0);
half3 lightDirection;
LightInput mainLight;
INITIALIZE_MAIN_LIGHT(mainLight);
half realtimeMainLightAtten = GetMainLightDirectionAndRealtimeAttenuation(mainLight, normalWS, positionWS, lightDirection);
half3 NdotL = saturate(dot(normalWS, lightDirection));
half3 lambert = mainLight.color * NdotL;
half3 indirectDiffuse = DiffuseGI(diffuseGI, lambert, realtimeMainLightAtten, 1.0);
half mainLightAtten = MixRealtimeAndBakedOcclusion(realtimeMainLightAtten, bakedOcclusion, mainLight.distanceAttenuation);
half3 diffuseColor = lambert * mainLightAtten + indirectDiffuse;
#ifdef _ADDITIONAL_LIGHTS
int pixelLightCount = min(_AdditionalLightCount.x, unity_LightIndicesOffsetAndCount.y);
for (int lightIter = 0; lightIter < pixelLightCount; ++lightIter)
{
LightInput light;
INITIALIZE_LIGHT(light, lightIter);
half lightAttenuation = GetLightDirectionAndRealtimeAttenuation(light, normalWS, positionWS, lightDirection);
lightAttenuation = MixRealtimeAndBakedOcclusion(lightAttenuation, bakedOcclusion, light.distanceAttenuation);
half3 attenuatedLightColor = light.color * lightAttenuation;
diffuseColor += LightingLambert(attenuatedLightColor, lightDirection, normalWS);
}
#endif
half3 finalColor = diffuseColor * diffuse + emission;
// Computes Fog Factor per vextex
ApplyFog(finalColor, fogFactor);
half4 color = half4(finalColor, alpha);
return OUTPUT_COLOR(color);
}
half4 LightweightFragmentBlinnPhong(float3 positionWS, half3 normalWS, half3 viewDirectionWS,
half fogFactor, half3 diffuseGI, half3 diffuse, half4 specularGloss, half shininess, half3 emission, half alpha)
{
half4 bakedOcclusion = half4(0, 0, 0, 0);
half3 lightDirection;
LightInput mainLight;
INITIALIZE_MAIN_LIGHT(mainLight);
half realtimeMainLightAtten = GetMainLightDirectionAndRealtimeAttenuation(mainLight, normalWS, positionWS, lightDirection);
half3 NdotL = saturate(dot(normalWS, lightDirection));
half3 lambert = mainLight.color * NdotL;
half3 indirectDiffuse = DiffuseGI(diffuseGI, lambert, realtimeMainLightAtten, 1.0);
half mainLightAtten = MixRealtimeAndBakedOcclusion(realtimeMainLightAtten, bakedOcclusion, mainLight.distanceAttenuation);
half3 diffuseColor = lambert * mainLightAtten + indirectDiffuse;
half3 specularColor = LightingSpecular(mainLight.color * mainLightAtten, lightDirection, normalWS, viewDirectionWS, specularGloss, shininess);
#ifdef _ADDITIONAL_LIGHTS
int pixelLightCount = min(_AdditionalLightCount.x, unity_LightIndicesOffsetAndCount.y);
for (int lightIter = 0; lightIter < pixelLightCount; ++lightIter)
{
LightInput light;
INITIALIZE_LIGHT(light, lightIter);
half lightAttenuation = GetLightDirectionAndRealtimeAttenuation(light, normalWS, positionWS, lightDirection);
lightAttenuation = MixRealtimeAndBakedOcclusion(lightAttenuation, bakedOcclusion, light.distanceAttenuation);
half3 attenuatedLightColor = light.color * lightAttenuation;
diffuseColor += LightingLambert(attenuatedLightColor, lightDirection, normalWS);
specularColor += LightingSpecular(attenuatedLightColor, lightDirection, normalWS, viewDirectionWS, specularGloss, shininess);
}
#endif
half3 finalColor = diffuseColor * diffuse + emission;
finalColor += specularColor;
// Computes Fog Factor per vextex
ApplyFog(finalColor, fogFactor);
half4 color = half4(finalColor, alpha);
return OUTPUT_COLOR(color);
}
#endif