764 lines
25 KiB
HLSL
764 lines
25 KiB
HLSL
#ifndef SCSS_CORE_INCLUDED
|
|
#define SCSS_CORE_INCLUDED
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "AutoLight.cginc"
|
|
#include "Lighting.cginc"
|
|
|
|
#include "SCSS_Config.cginc"
|
|
#include "SCSS_UnityGI.cginc"
|
|
#include "SCSS_Utils.cginc"
|
|
#include "SCSS_Input.cginc"
|
|
#include "SCSS_Attributes.cginc"
|
|
|
|
|
|
// Shade4PointLights from UnityCG.cginc but only returns their attenuation.
|
|
float4 Shade4PointLightsAtten (
|
|
float4 lightPosX, float4 lightPosY, float4 lightPosZ,
|
|
float4 lightAttenSq,
|
|
float3 pos, float3 normal)
|
|
{
|
|
// to light vectors
|
|
float4 toLightX = lightPosX - pos.x;
|
|
float4 toLightY = lightPosY - pos.y;
|
|
float4 toLightZ = lightPosZ - pos.z;
|
|
// squared lengths
|
|
float4 lengthSq = 0;
|
|
lengthSq += toLightX * toLightX;
|
|
lengthSq += toLightY * toLightY;
|
|
lengthSq += toLightZ * toLightZ;
|
|
// don't produce NaNs if some vertex position overlaps with the light
|
|
lengthSq = max(lengthSq, 0.000001);
|
|
|
|
// NdotL
|
|
float4 ndotl = 0;
|
|
ndotl += toLightX * normal.x;
|
|
ndotl += toLightY * normal.y;
|
|
ndotl += toLightZ * normal.z;
|
|
// correct NdotL
|
|
float4 corr = 0;//rsqrt(lengthSq);
|
|
corr.x = fastRcpSqrtNR0(lengthSq.x);
|
|
corr.y = fastRcpSqrtNR0(lengthSq.y);
|
|
corr.z = fastRcpSqrtNR0(lengthSq.z);
|
|
corr.w = fastRcpSqrtNR0(lengthSq.x);
|
|
|
|
ndotl = corr * (ndotl * 0.5 + 0.5); // Match with Forward for light ramp sampling
|
|
ndotl = max (float4(0,0,0,0), ndotl);
|
|
// attenuation
|
|
// Fixes popin. Thanks, d4rkplayer!
|
|
float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);
|
|
float4 atten2 = saturate(1 - (lengthSq * lightAttenSq / 25));
|
|
atten = min(atten, atten2 * atten2);
|
|
|
|
float4 diff = ndotl * atten;
|
|
#if defined(SCSS_UNIMPORTANT_LIGHTS_FRAGMENT)
|
|
return atten;
|
|
#else
|
|
return diff;
|
|
#endif
|
|
}
|
|
|
|
// Based on Standard Shader's forwardbase vertex lighting calculations in VertexGIForward
|
|
// This revision does not pass the light values themselves, but only their attenuation.
|
|
inline half4 VertexLightContribution(float3 posWorld, half3 normalWorld)
|
|
{
|
|
half4 vertexLight = 0;
|
|
|
|
// Static lightmapped materials are not allowed to have vertex lights.
|
|
#ifdef LIGHTMAP_ON
|
|
return 0;
|
|
#elif UNITY_SHOULD_SAMPLE_SH
|
|
#ifdef VERTEXLIGHT_ON
|
|
// Approximated illumination from non-important point lights
|
|
vertexLight = Shade4PointLightsAtten(
|
|
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
|
|
unity_4LightAtten0, posWorld, normalWorld);
|
|
#endif
|
|
#endif
|
|
|
|
return vertexLight;
|
|
}
|
|
|
|
#if defined(_SUBSURFACE)
|
|
//SSS method from GDC 2011 conference by Colin Barre-Bresebois & Marc Bouchard and modified by Xiexe
|
|
float3 getSubsurfaceScatteringLight (SCSS_Light l, float3 normalDirection, float3 viewDirection,
|
|
float attenuation, float3 thickness, float3 indirectLight = 0)
|
|
{
|
|
float3 vSSLight = l.dir + normalDirection * _SSSDist; // Distortion
|
|
float3 vdotSS = pow(saturate(dot(viewDirection, -vSSLight)), _SSSPow)
|
|
* _SSSIntensity;
|
|
|
|
return lerp(1, attenuation, float(any(_WorldSpaceLightPos0.xyz)))
|
|
* (vdotSS + _SSSAmbient) * abs(_ThicknessMapInvert-thickness)
|
|
* (l.color + indirectLight) * _SSSCol;
|
|
|
|
}
|
|
#endif
|
|
|
|
float3 sampleCrossToneLighting(inout float x, SCSS_TonemapInput tone0, SCSS_TonemapInput tone1, float3 albedo)
|
|
{
|
|
// A three-tiered tone system.
|
|
// Input x is potentially affected by occlusion map.
|
|
|
|
x = x;
|
|
half offset0 = _1st_ShadeColor_Step * tone0.bias;
|
|
half width0 = _1st_ShadeColor_Feather;
|
|
half factor0 = saturate(simpleSharpen(x, width0, offset0));
|
|
|
|
half offset1 = _2nd_ShadeColor_Step * tone1.bias;
|
|
half width1 = _2nd_ShadeColor_Feather;
|
|
half factor1 = saturate(simpleSharpen(x, width1, offset1));
|
|
|
|
float3 final;
|
|
|
|
// 2nd separation determines whether 1st and 2nd shading tones are combined.
|
|
if (_Crosstone2ndSeparation == 0) tone1.col = tone1.col * tone0.col;
|
|
// if (_Crosstone2ndSeparation == 1) tone1.col = tone1.col; // Just here for completeness
|
|
|
|
// Either way, the result is interpolated against tone 0 by the 2nd factor.
|
|
final = lerp(tone1.col, tone0.col, factor1);
|
|
|
|
// Tone separation determines whether albedo and 1st shading tones are combined.
|
|
if (_CrosstoneToneSeparation == 0) final = lerp(final, 1.0, factor0) * albedo;
|
|
if (_CrosstoneToneSeparation == 1) final = lerp(final, albedo, factor0);
|
|
|
|
x = factor0;
|
|
|
|
return final;
|
|
}
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
float applyShadowLift(float baseLight, float occlusion)
|
|
{
|
|
baseLight *= occlusion;
|
|
baseLight = _ShadowLift + baseLight * (1-_ShadowLift);
|
|
return baseLight;
|
|
}
|
|
|
|
float applyShadowLift(float4 baseLight, float occlusion)
|
|
{
|
|
baseLight *= occlusion;
|
|
baseLight = _ShadowLift + baseLight * (1-_ShadowLift);
|
|
return baseLight;
|
|
}
|
|
#endif
|
|
|
|
float getRemappedLight(half perceptualRoughness, SCSS_LightParam d)
|
|
{
|
|
float diffuseShadowing = DisneyDiffuse(abs(d.NdotV), abs(d.NdotL), d.LdotH, perceptualRoughness);
|
|
float remappedLight = d.NdotL * LerpOneTo(diffuseShadowing, _DiffuseGeomShadowFactor);
|
|
return remappedLight;
|
|
}
|
|
|
|
float applyAttenuation(half NdotL, half attenuation)
|
|
{
|
|
#if defined(SCSS_CROSSTONE)
|
|
//attenuation = round(attenuation);
|
|
half shadeVal = _1st_ShadeColor_Step - _1st_ShadeColor_Feather * 0.5;
|
|
shadeVal = shadeVal-0.01;
|
|
//NdotL = min(lerp(shadeVal, NdotL, attenuation), NdotL);
|
|
NdotL = lerp(shadeVal*NdotL, NdotL, attenuation);
|
|
#else
|
|
NdotL = min(NdotL * attenuation, NdotL);
|
|
//NdotL = lerp(0.5, NdotL, attenuation);
|
|
#endif
|
|
return NdotL;
|
|
}
|
|
|
|
half3 calcVertexLight(float4 vertexAttenuation, float occlusion, SCSS_TonemapInput tone[2], half softness)
|
|
{
|
|
float3 vertexContribution = 0;
|
|
#if defined(UNITY_PASS_FORWARDBASE)
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
vertexAttenuation = applyShadowLift(vertexAttenuation, occlusion);
|
|
for (int num = 0; num < 4; num++) {
|
|
vertexContribution += unity_LightColor[num] *
|
|
(sampleRampWithOptions(vertexAttenuation[num], softness)+tone[0].col);
|
|
}
|
|
#endif
|
|
|
|
#if defined(SCSS_CROSSTONE)
|
|
for (int num = 0; num < 4; num++) {
|
|
vertexContribution += unity_LightColor[num] *
|
|
sampleCrossToneLighting(vertexAttenuation[num], tone[0], tone[1], 1.0);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
return vertexContribution;
|
|
}
|
|
|
|
void getDirectIndirectLighting(float3 normal, out float3 directLighting, out float3 indirectLighting)
|
|
{
|
|
directLighting = 0.0;
|
|
indirectLighting = 0.0;
|
|
switch (_LightingCalculationType)
|
|
{
|
|
case 0: // Unbiased
|
|
directLighting = GetSHMaxL1();
|
|
indirectLighting = GetSHAverage();
|
|
break;
|
|
case 1: // Standard
|
|
directLighting =
|
|
indirectLighting = BetterSH9(half4(normal, 1.0));
|
|
break;
|
|
case 2: // Cubed
|
|
directLighting = BetterSH9(half4(0.0, 1.0, 0.0, 1.0));
|
|
indirectLighting = BetterSH9(half4(0.0, -1.0, 0.0, 1.0));
|
|
break;
|
|
case 3: // True Directional
|
|
float4 ambientDir = float4(Unity_SafeNormalize(unity_SHAr.xyz + unity_SHAg.xyz + unity_SHAb.xyz), 1.0);
|
|
directLighting = BetterSH9(ambientDir);
|
|
indirectLighting = BetterSH9(-ambientDir);
|
|
break;
|
|
case 4: // Biased
|
|
directLighting = GetSHMaxL1();
|
|
indirectLighting = BetterSH9(half4(0.0, 0.0, 0.0, 1.0));
|
|
break;
|
|
}
|
|
// Workaround for scenes with HDR off blowing out in VRchat.
|
|
if (getLightClampActive())
|
|
{
|
|
directLighting = saturate(directLighting);
|
|
indirectLighting = saturate(indirectLighting);
|
|
}
|
|
}
|
|
|
|
// For baked lighting.
|
|
half3 calcDiffuseGI(float3 albedo, SCSS_TonemapInput tone[2], float occlusion, half softness,
|
|
float3 indirectLighting, float3 directLighting, SCSS_LightParam d)
|
|
{
|
|
float ambientLight = d.NdotAmb;
|
|
|
|
/*
|
|
Ambient lighting splitting:
|
|
Strong shading looks good, but weak shading looks bad.
|
|
This system removes shading if it's too weak.
|
|
*/
|
|
|
|
float3 indirectAverage = 0.5 * (indirectLighting + directLighting);
|
|
|
|
// Make this a UI value later.
|
|
const half ambientLightSplitThreshold = 1.0/1.0;
|
|
half ambientLightSplitFactor =
|
|
saturate(
|
|
dot(abs((directLighting-indirectLighting)/indirectAverage),
|
|
ambientLightSplitThreshold * sRGB_Luminance));
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
ambientLight = applyShadowLift(ambientLight, occlusion);
|
|
float3 indirectContribution = sampleRampWithOptions(ambientLight, softness);
|
|
indirectLighting = lerp(indirectLighting, directLighting, tone[0].col);
|
|
indirectAverage = lerp(indirectAverage, directLighting, tone[0].col);
|
|
#endif
|
|
|
|
#if defined(SCSS_CROSSTONE)
|
|
ambientLight *= occlusion;
|
|
indirectAverage *= albedo;
|
|
float3 indirectContribution = sampleCrossToneLighting(ambientLight, tone[0], tone[1], albedo);
|
|
#endif
|
|
|
|
float3 lightContribution;
|
|
|
|
#if defined(SCSS_CROSSTONE)
|
|
if (_CrosstoneToneSeparation == 0) lightContribution =
|
|
lerp(indirectAverage,
|
|
lerp(indirectLighting, directLighting, indirectContribution),
|
|
ambientLightSplitFactor) * albedo;
|
|
|
|
if (_CrosstoneToneSeparation == 1) lightContribution =
|
|
lerp(indirectAverage,
|
|
directLighting*indirectContribution,
|
|
ambientLightSplitFactor);
|
|
#endif
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
lightContribution =
|
|
lerp(indirectAverage,
|
|
lerp(indirectLighting, directLighting, indirectContribution),
|
|
ambientLightSplitFactor) * albedo;
|
|
#endif
|
|
|
|
return lightContribution;
|
|
}
|
|
|
|
// For directional lights where attenuation is shadow.
|
|
half3 calcDiffuseBase(float3 albedo, SCSS_TonemapInput tone[2], float occlusion, half perceptualRoughness,
|
|
half attenuation, half softness, SCSS_LightParam d, SCSS_Light l)
|
|
{
|
|
float remappedLight = getRemappedLight(perceptualRoughness, d);
|
|
remappedLight = remappedLight * 0.5 + 0.5;
|
|
|
|
remappedLight = applyAttenuation(remappedLight, attenuation);
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
remappedLight = applyShadowLift(remappedLight, occlusion);
|
|
float3 lightContribution = lerp(tone[0].col, 1.0, sampleRampWithOptions(remappedLight, softness)) * albedo;
|
|
#endif
|
|
|
|
#if defined(SCSS_CROSSTONE)
|
|
remappedLight *= occlusion;
|
|
float3 lightContribution = sampleCrossToneLighting(remappedLight, tone[0], tone[1], albedo);
|
|
#endif
|
|
|
|
lightContribution *= l.color;
|
|
|
|
return lightContribution;
|
|
}
|
|
|
|
// For point/spot lights where attenuation is shadow+attenuation.
|
|
half3 calcDiffuseAdd(float3 albedo, SCSS_TonemapInput tone[2], float occlusion, half perceptualRoughness,
|
|
half softness, SCSS_LightParam d, SCSS_Light l)
|
|
{
|
|
float remappedLight = getRemappedLight(perceptualRoughness, d);
|
|
remappedLight = remappedLight * 0.5 + 0.5;
|
|
|
|
#if !defined(SCSS_CROSSTONE)
|
|
remappedLight = applyShadowLift(remappedLight, occlusion);
|
|
float3 lightContribution = sampleRampWithOptions(remappedLight, softness);
|
|
|
|
float3 directLighting = l.color;
|
|
float3 indirectLighting = l.color * tone[0].col;
|
|
|
|
lightContribution = lerp(indirectLighting, directLighting, lightContribution) * albedo;
|
|
#endif
|
|
|
|
#if defined(SCSS_CROSSTONE)
|
|
float3 lightContribution = sampleCrossToneLighting(remappedLight, tone[0], tone[1], albedo);
|
|
lightContribution *= l.color;
|
|
#endif
|
|
|
|
return lightContribution;
|
|
}
|
|
|
|
#if defined(_SPECULAR)
|
|
void getSpecularVD(float roughness, SCSS_LightParam d, SCSS_Light l, VertexOutput i,
|
|
out half V, out half D)
|
|
{
|
|
V = 0; D = 0;
|
|
|
|
#ifndef SHADER_TARGET_GLSL
|
|
[call]
|
|
#endif
|
|
switch(_SpecularType)
|
|
{
|
|
case 1: // GGX
|
|
V = SmithJointGGXVisibilityTerm (d.NdotL, d.NdotV, roughness);
|
|
D = GGXTerm (d.NdotH, roughness);
|
|
break;
|
|
|
|
case 2: // Charlie (cloth)
|
|
V = V_Neubelt (d.NdotV, d.NdotL);
|
|
D = D_Charlie (roughness, d.NdotH);
|
|
break;
|
|
|
|
case 3: // GGX anisotropic
|
|
float anisotropy = _Anisotropy;
|
|
float at = max(roughness * (1.0 + anisotropy), 0.002);
|
|
float ab = max(roughness * (1.0 - anisotropy), 0.002);
|
|
|
|
V = SmithJointGGXVisibilityTerm (d.NdotL, d.NdotV, roughness);
|
|
D = D_GGX_Anisotropic(d.NdotH, d.halfDir, i.tangentDir, i.bitangentDir, at, ab);
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
half3 calcSpecularBase(float3 specColor, float smoothness, float3 normal, float oneMinusReflectivity, float perceptualRoughness,
|
|
float attenuation, float occlusion, SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
UnityGI gi = (UnityGI)0;
|
|
|
|
half V = 0; half D = 0;
|
|
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
|
|
// "GGX with roughness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughness remapping."
|
|
// This also fixes issues with the other specular types.
|
|
roughness = max(roughness, 0.002);
|
|
|
|
d = saturate(d);
|
|
|
|
getSpecularVD(roughness, d, l, i, /*out*/ V, /*out*/ D);
|
|
|
|
half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later
|
|
specularTerm = max(0, specularTerm * d.NdotL);
|
|
|
|
#if defined(_SPECULARHIGHLIGHTS_OFF)
|
|
specularTerm = 0.0;
|
|
#endif
|
|
|
|
half surfaceReduction = 1.0 / (roughness*roughness + 1);
|
|
|
|
// To provide true Lambert lighting, we need to be able to kill specular completely.
|
|
specularTerm *= any(specColor) ? 1.0 : 0.0;
|
|
|
|
gi = GetUnityGI(l.color.rgb, l.dir, normal,
|
|
d.viewDir, d.reflDir, attenuation, occlusion, perceptualRoughness, i.posWorld.xyz);
|
|
|
|
float grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
|
|
|
|
return
|
|
specularTerm * (gi.light.color) * FresnelTerm(specColor, d.LdotH) +
|
|
surfaceReduction * (gi.indirect.specular.rgb) * FresnelLerp(specColor, grazingTerm, d.NdotV);
|
|
|
|
}
|
|
|
|
half3 calcSpecularBase(SCSS_Input c, float perceptualRoughness, float attenuation,
|
|
SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
return calcSpecularBase(c.specColor, c.smoothness, c.normal, c.oneMinusReflectivity,
|
|
perceptualRoughness, attenuation, c.occlusion, d, l, i);
|
|
}
|
|
|
|
half3 calcSpecularAdd(float3 specColor, float smoothness, float3 normal, float oneMinusReflectivity, float perceptualRoughness,
|
|
SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
#if defined(_SPECULARHIGHLIGHTS_OFF)
|
|
return 0.0;
|
|
#endif
|
|
|
|
half V = 0; half D = 0;
|
|
float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
|
|
// "GGX with roughness to 0 would mean no specular at all, using max(roughness, 0.002) here to match HDrenderloop roughness remapping."
|
|
// This also fixes issues with the other specular types.
|
|
roughness = max(roughness, 0.002);
|
|
|
|
d = saturate(d);
|
|
|
|
getSpecularVD(roughness, d, l, i, /*out*/ V, /*out*/ D);
|
|
|
|
half specularTerm = V*D * UNITY_PI; // Torrance-Sparrow model, Fresnel is applied later
|
|
specularTerm = max(0, specularTerm * d.NdotL);
|
|
|
|
// To provide true Lambert lighting, we need to be able to kill specular completely.
|
|
specularTerm *= any(specColor) ? 1.0 : 0.0;
|
|
|
|
return
|
|
specularTerm * l.color * FresnelTerm(specColor, d.LdotH);
|
|
|
|
}
|
|
|
|
half3 calcSpecularAdd(SCSS_Input c, float perceptualRoughness,
|
|
SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
return calcSpecularAdd(c.specColor, c.smoothness, c.normal, c.oneMinusReflectivity, perceptualRoughness, d, l, i);
|
|
}
|
|
|
|
half3 calcSpecularCel(float3 specColor, float smoothness, float3 normal, float oneMinusReflectivity, float perceptualRoughness,
|
|
float attenuation, SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
if (_SpecularType == 4) {
|
|
//
|
|
float spec = max(d.NdotH, 0);
|
|
spec = pow(spec, (smoothness)*40) * _CelSpecularSteps;
|
|
spec = sharpenLighting(frac(spec), _CelSpecularSoftness)+floor(spec);
|
|
spec = max(0.02,spec);
|
|
spec *= UNITY_PI * rcp(_CelSpecularSteps);
|
|
|
|
float3 envLight = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, normal, UNITY_SPECCUBE_LOD_STEPS);
|
|
return (spec * specColor * l.color) + (spec * specColor * envLight);
|
|
}
|
|
if (_SpecularType == 5) {
|
|
// It might be better if these are passed in parameters in future
|
|
float anisotropy = _Anisotropy;
|
|
float softness = _CelSpecularSoftness;
|
|
float3 strandTangent = (anisotropy < 0)
|
|
? i.tangentDir
|
|
: i.bitangentDir;
|
|
anisotropy = abs(anisotropy);
|
|
strandTangent = lerp(normal, strandTangent, anisotropy);
|
|
float exponent = smoothness;
|
|
float spec = StrandSpecular(strandTangent, d.halfDir,
|
|
exponent*80, 1.0 );
|
|
float spec2 = StrandSpecular(strandTangent, d.halfDir,
|
|
exponent*40, 0.5 );
|
|
spec = sharpenLighting(frac(spec), softness)+floor(spec);
|
|
spec2 = sharpenLighting(frac(spec2), softness)+floor(spec2);
|
|
spec += spec2;
|
|
|
|
float3 envLight = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, normal, UNITY_SPECCUBE_LOD_STEPS);
|
|
return (spec * specColor * l.color) + (spec * specColor * envLight);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
half3 calcSpecularCel(SCSS_Input c, float perceptualRoughness, float attenuation, SCSS_LightParam d, SCSS_Light l, VertexOutput i)
|
|
{
|
|
return calcSpecularCel(c.specColor, c.smoothness, c.normal, c.oneMinusReflectivity, perceptualRoughness, attenuation, d, l, i);
|
|
}
|
|
|
|
#endif // _SPECULAR
|
|
|
|
float3 SCSS_ShadeBase(SCSS_Input c, VertexOutput i, SCSS_Light l, float attenuation)
|
|
{
|
|
float3 finalColor;
|
|
|
|
float isOutline = i.extraData.x;
|
|
|
|
SCSS_LightParam d = initialiseLightParam(l, c.normal, i.posWorld.xyz);
|
|
|
|
float3 directLighting, indirectLighting;
|
|
|
|
getDirectIndirectLighting(c.normal, /*out*/ directLighting, /*out*/ indirectLighting);
|
|
|
|
finalColor = calcDiffuseGI(c.albedo, c.tone, c.occlusion, c.softness, indirectLighting, directLighting, d);
|
|
finalColor += calcDiffuseBase(c.albedo, c.tone, c.occlusion,
|
|
c.perceptualRoughness, attenuation, c.softness, d, l);
|
|
|
|
// Prepare fake light params for subsurface scattering.
|
|
SCSS_Light iL = l;
|
|
SCSS_LightParam iD = d;
|
|
iL.color = GetSHMaxL1();
|
|
iL.dir = Unity_SafeNormalize((unity_SHAr.xyz + unity_SHAg.xyz + unity_SHAb.xyz) * _LightSkew.xyz);
|
|
iD = initialiseLightParam(iL, c.normal, i.posWorld.xyz);
|
|
|
|
// Prepare fake light params for spec/fresnel which simulate specular.
|
|
SCSS_Light fL = l;
|
|
SCSS_LightParam fD = d;
|
|
fL.color = attenuation * fL.color + GetSHMaxL1();
|
|
fL.dir = Unity_SafeNormalize(fL.dir + (unity_SHAr.xyz + unity_SHAg.xyz + unity_SHAb.xyz) * _LightSkew.xyz);
|
|
fD = initialiseLightParam(fL, c.normal, i.posWorld.xyz);
|
|
|
|
if (isOutline <= 0)
|
|
{
|
|
#if defined(_SUBSURFACE)
|
|
#if defined(USING_DIRECTIONAL_LIGHT)
|
|
finalColor += getSubsurfaceScatteringLight(l, c.normal, d.viewDir,
|
|
attenuation, c.thickness) * c.albedo;
|
|
#endif
|
|
finalColor += getSubsurfaceScatteringLight(iL, c.normal, iD.viewDir,
|
|
1, c.thickness) * c.albedo;
|
|
#endif
|
|
|
|
#if defined(_METALLICGLOSSMAP)
|
|
finalColor += calcSpecularBase(c, c.perceptualRoughness, attenuation, d, l, i);
|
|
#endif
|
|
|
|
#if defined(_SPECGLOSSMAP)
|
|
finalColor += calcSpecularCel(c, c.perceptualRoughness, attenuation, fD, fL, i);
|
|
#endif
|
|
};
|
|
|
|
return finalColor;
|
|
}
|
|
|
|
float3 SCSS_ShadeLight(SCSS_Input c, VertexOutput i, SCSS_Light l, half attenuation)
|
|
{
|
|
float3 finalColor;
|
|
|
|
float isOutline = i.extraData.x;
|
|
|
|
SCSS_LightParam d = initialiseLightParam(l, c.normal, i.posWorld.xyz);
|
|
|
|
finalColor = calcDiffuseAdd(c.albedo, c.tone, c.occlusion, c.perceptualRoughness, c.softness, d, l);
|
|
|
|
if (isOutline <= 0)
|
|
{
|
|
#if defined(_SUBSURFACE)
|
|
finalColor += c.albedo * getSubsurfaceScatteringLight(l, c.normal, d.viewDir,
|
|
attenuation, c.thickness, c.tone[0].col);
|
|
#endif
|
|
|
|
#if defined(_METALLICGLOSSMAP)
|
|
finalColor += calcSpecularAdd(c, c.perceptualRoughness, d, l, i);
|
|
#endif
|
|
|
|
#if defined(_SPECGLOSSMAP)
|
|
finalColor += calcSpecularCel(c, c.perceptualRoughness, attenuation, d, l, i);
|
|
#endif
|
|
};
|
|
return finalColor;
|
|
}
|
|
|
|
|
|
void addEmissive(const SCSS_Input c, const VertexOutput i, float3 effectLighting, inout float3 color)
|
|
{
|
|
float isOutline = i.extraData.x;
|
|
float3 emission;
|
|
float2 emissionDetailUV = EmissionDetailTexCoords(i.uv0, i.uv1);
|
|
float4 emissionDetail = EmissionDetail(emissionDetailUV);
|
|
|
|
color = max(0, color - saturate((1-emissionDetail.w)- (1-c.emission)));
|
|
emission = emissionDetail.rgb * c.emission * _EmissionColor.rgb;
|
|
|
|
// Glow in the dark modifier.
|
|
#if defined(_EMISSION)
|
|
float glowModifier = smoothstep(_EmissiveLightSenseStart, _EmissiveLightSenseEnd, dot(effectLighting, sRGB_Luminance));
|
|
if (_UseEmissiveLightSense) emission *= glowModifier;
|
|
#endif
|
|
|
|
emission *= (1-isOutline);
|
|
color += emission;
|
|
}
|
|
|
|
float3 SCSS_ApplyLighting(SCSS_Input c, VertexOutput i, float4 texcoords)
|
|
{
|
|
UNITY_LIGHT_ATTENUATION(attenuation, i, i.posWorld.xyz);
|
|
|
|
#if defined(SCSS_SCREEN_SHADOW_FILTER) && defined(USING_SHADOWS_UNITY)
|
|
correctedScreenShadowsForMSAA(i._ShadowCoord, attenuation);
|
|
#endif
|
|
|
|
float isOutline = i.extraData.x;
|
|
|
|
// Lighting parameters
|
|
SCSS_Light l = MainLight(i.posWorld.xyz);
|
|
#if defined(UNITY_PASS_FORWARDADD) && !defined(USING_DIRECTIONAL_LIGHT)
|
|
l.dir = normalize(_WorldSpaceLightPos0.xyz - i.posWorld.xyz);
|
|
#endif
|
|
|
|
SCSS_LightParam d = initialiseLightParam(l, c.normal, i.posWorld.xyz);
|
|
|
|
#if defined(_METALLICGLOSSMAP)
|
|
// Geometric Specular AA from HDRP
|
|
c.smoothness = GeometricNormalFiltering(c.smoothness, i.normalDir.xyz, 0.25, 0.5);
|
|
#endif
|
|
|
|
#if defined(_METALLICGLOSSMAP)
|
|
// Perceptual roughness transformation. Without this, roughness handling is wrong.
|
|
c.perceptualRoughness = SmoothnessToPerceptualRoughness(c.smoothness);
|
|
#else
|
|
// Disable DisneyDiffuse for cel specular.
|
|
#endif
|
|
|
|
// Generic lighting for effects.
|
|
float3 effectLighting = l.color;
|
|
#if defined(UNITY_PASS_FORWARDBASE)
|
|
effectLighting += GetSHAverage();
|
|
#endif
|
|
|
|
// Generic lighting for effects with shadow applied.
|
|
float3 effectLightShadow = l.color * max((1+d.NdotL)*attenuation, 0);
|
|
#if defined(UNITY_PASS_FORWARDBASE)
|
|
effectLightShadow += GetSHAverage();
|
|
#endif
|
|
|
|
// Workaround for scenes with HDR off blowing out in VRchat.
|
|
if (getLightClampActive())
|
|
{
|
|
// Colour-preserving clamp.
|
|
// This light value is used later to flatten the overall output intensity.
|
|
// Get the maximum input value from the lighting.
|
|
float maxEffectLight = max3(effectLighting);
|
|
// The effect lighting is remapped to be within the 0-1.25 range when clamped.
|
|
float modLight = min(maxEffectLight, 1.25);
|
|
// Scale the values by the highest value.
|
|
// Needs a bit more testing, but should look nice.
|
|
effectLighting = (effectLighting/maxEffectLight)*modLight;
|
|
}
|
|
|
|
float3 finalColor = 0;
|
|
|
|
// Apply matcap before specular effect.
|
|
if (_UseMatcap >= 1 && isOutline <= 0)
|
|
{
|
|
half2 matcapUV;
|
|
if (_UseMatcap == 1) matcapUV = getMatcapUVsOriented(c.normal, d.viewDir, float3(0, 1, 0));
|
|
if (_UseMatcap == 2) matcapUV = getMatcapUVsOriented(c.normal, d.viewDir, i.bitangentDir.xyz);
|
|
|
|
float4 _MatcapMask_var = MatcapMask(i.uv0.xy);
|
|
c.albedo = applyMatcap(_Matcap1, matcapUV, c.albedo, _Matcap1Tint, _Matcap1Blend, _Matcap1Strength * _MatcapMask_var.r);
|
|
c.albedo = applyMatcap(_Matcap2, matcapUV, c.albedo, _Matcap2Tint, _Matcap2Blend, _Matcap2Strength * _MatcapMask_var.g);
|
|
c.albedo = applyMatcap(_Matcap3, matcapUV, c.albedo, _Matcap3Tint, _Matcap3Blend, _Matcap3Strength * _MatcapMask_var.b);
|
|
c.albedo = applyMatcap(_Matcap4, matcapUV, c.albedo, _Matcap4Tint, _Matcap4Blend, _Matcap4Strength * _MatcapMask_var.a);
|
|
}
|
|
|
|
|
|
float3 finalRimLight = 0;
|
|
if (_UseFresnel)
|
|
{
|
|
float fresnelLightMaskBase = LerpOneTo((d.NdotH), _UseFresnelLightMask);
|
|
float fresnelLightMask =
|
|
saturate(pow(saturate( fresnelLightMaskBase), _FresnelLightMask));
|
|
float fresnelLightMaskInv =
|
|
saturate(pow(saturate(-fresnelLightMaskBase), _FresnelLightMask));
|
|
|
|
// Refactored to use more ifs because the compiler is smarter than me.
|
|
float rimBase = sharpenLighting(d.rlPow4.y * c.rim.width * fresnelLightMask, c.rim.power) * c.rim.alpha;
|
|
float3 rimCol = rimBase * c.rim.tint;
|
|
|
|
float rimInv = sharpenLighting(d.rlPow4.y * c.rim.invWidth * fresnelLightMaskInv,
|
|
c.rim.invPower) * _FresnelLightMask * c.rim.invAlpha;
|
|
float3 rimInvCol = rimInv * c.rim.invTint;
|
|
|
|
float3 rimFinal = rimCol + rimInvCol;
|
|
|
|
float applyToAlbedo = (_UseFresnel == 1) + (_UseFresnel == 4);
|
|
float applyToFinal = (_UseFresnel == 2);
|
|
float applyToLightBias = (_UseFresnel == 3) + (_UseFresnel == 4);
|
|
// Lit
|
|
if (applyToAlbedo) c.albedo += c.albedo * rimFinal * (1-isOutline);
|
|
// AmbientAlt
|
|
if (applyToLightBias) c.occlusion += saturate(rimBase) * (1-isOutline);
|
|
// Ambient
|
|
// If applied to the final output, it can only be applied later.
|
|
if (applyToFinal) finalRimLight = rimFinal * (1-isOutline);
|
|
|
|
}
|
|
|
|
// Workaround for scenes with HDR off blowing out in VRchat.
|
|
if (getLightClampActive()) l.color = saturate(l.color);
|
|
|
|
#if defined(UNITY_PASS_FORWARDBASE)
|
|
finalColor = SCSS_ShadeBase(c, i, l, attenuation);
|
|
#endif
|
|
|
|
#if defined(UNITY_PASS_FORWARDADD)
|
|
finalColor = SCSS_ShadeLight(c, i, l, attenuation);
|
|
#endif
|
|
|
|
// Proper cheap vertex lights.
|
|
#if defined(VERTEXLIGHT_ON) && !defined(SCSS_UNIMPORTANT_LIGHTS_FRAGMENT)
|
|
finalColor += c.albedo * calcVertexLight(i.vertexLight, c.occlusion, c.tone, c.softness);
|
|
#endif
|
|
|
|
// Apply Ambient rim lighting
|
|
if (_UseFresnel == 2)
|
|
{
|
|
finalColor += effectLighting*finalRimLight;
|
|
}
|
|
|
|
// Apply full lighting to unimportant lights. This is cheaper than you might expect.
|
|
#if defined(UNITY_PASS_FORWARDBASE) && defined(VERTEXLIGHT_ON) && defined(SCSS_UNIMPORTANT_LIGHTS_FRAGMENT)
|
|
for (int num = 0; num < 4; num++) {
|
|
UNITY_BRANCH if ((unity_LightColor[num].r + unity_LightColor[num].g + unity_LightColor[num].b + i.vertexLight[num]) != 0.0)
|
|
{
|
|
l.color = unity_LightColor[num].rgb;
|
|
l.dir = normalize(float3(unity_4LightPosX0[num], unity_4LightPosY0[num], unity_4LightPosZ0[num]) - i.posWorld.xyz);
|
|
|
|
if (getLightClampActive()) l.color = saturate(l.color);
|
|
|
|
float3 addColor = SCSS_ShadeLight(c, i, l, 1) * i.vertexLight[num];
|
|
|
|
if (getLightClampActive()) addColor = saturate(addColor);
|
|
|
|
finalColor += addColor;
|
|
}
|
|
};
|
|
#endif
|
|
|
|
#if defined(UNITY_PASS_FORWARDADD)
|
|
finalColor *= attenuation;
|
|
#endif
|
|
|
|
finalColor *= _LightWrappingCompensationFactor;
|
|
|
|
// Apply the light scaling if the light clamp is active. When the light clamp is active,
|
|
// the final colour is divided by the light intensity
|
|
if (getLightClampActive()) finalColor = finalColor / max(max3(effectLighting), 1);
|
|
|
|
finalColor *= _LightMultiplyAnimated;
|
|
|
|
#if defined(UNITY_PASS_FORWARDBASE)
|
|
addEmissive(c, i, effectLightShadow, finalColor);
|
|
|
|
// Emissive rim.
|
|
finalColor += _CustomFresnelColor.xyz * (pow(d.rlPow4.y, rcp(_CustomFresnelColor.w+0.0001)));
|
|
#endif
|
|
|
|
return finalColor;
|
|
}
|
|
|
|
#endif // SCSS_CORE_INCLUDED |