2022-01-17 11:26:35 +03:00

1007 lines
39 KiB
HLSL

//////////////////////////////////////////////////////
// MK Toon Lighting //
// //
// Created by Michael Kremmel //
// www.michaelkremmel.de //
// Copyright © 2021 All rights reserved. //
//////////////////////////////////////////////////////
#ifndef MK_TOON_LIGHTING
#define MK_TOON_LIGHTING
#include "Core.hlsl"
#if defined(MK_URP)
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#elif defined(MK_LWRP)
#include "Packages/com.unity.render-pipelines.lightweight/ShaderLibrary/Lighting.hlsl"
#else
#include "AutoLight.cginc"
#include "UnityGlobalIllumination.cginc"
#endif
#include "Surface.hlsl"
// ------------------------------------------------------------------------------------------
// Note: The complete lighting is not entirely physically "correct"
// Distribution, Fresnel and Geometric terms are customized
// Because its a toon shader its required to mix colors up to a qualitative artistic look
// However most calculations follow a realistic input and create a toon output based on it
// Schlick Visibility is avoided by now to maintain a scalar pipeline. C.Sch. approximation is post multiplied instead
// Diffuse terms are expected to be a scalar instead of vec3, its later scaled to rgb
// The gooch is not implemented the way it was originally developed, spec / lightTransmission applied after gooch (should match the original implementation)
// Minnaert and Oren Nayar[0, PI / 2] roughness is not straightforward, in this implementation the GGX roughness is still used.
// Oren Nayar albedo is expected to be 1 to maintain scalar pipeline
// Lighting instructions are split on 4 component => (vector (RGB), scalar (A))
// not all operations can be done in a single cycle, therefore the goal is to compute the raw value and then try to fit into a MAD for the final output
// this should give best trade off in terms of readability and performance
// ------------------------------------------------------------------------------------------
struct MKLight
{
autoLP3 color;
autoLP3 radiometricColor;
half3 dirWorld;
half attenuation;
half distanceAttenuation;
#if defined(MK_URP) || defined(MK_LWRP)
half shadowAttenuation;
#endif
};
struct MKGI
{
half3 diffuse;
half3 specular;
};
struct MKGlossyEnvironmentData
{
half roughness;
half3 reflectDirection;
};
struct MKLightData
{
#ifdef MK_V_DOT_L
half VoL;
#endif
#ifdef MK_N_DOT_L
half NoLRaw;
half NoL;
#endif
#ifdef MK_LND
half3 LND;
#endif
#ifdef MK_V_DOT_LND
half VoLND;
#endif
#ifdef MK_HV
half3 HV;
#endif
#ifdef MK_L_DOT_HV
half LoHV;
#endif
#ifdef MK_V_DOT_HV
half VoHV;
half oneMinusVoHV;
#endif
#ifdef MK_T_DOT_HV
half ToHV;
#endif
#ifdef MK_B_DOT_HV
half BoHV;
#endif
#ifdef MK_N_DOT_HV
half NoHV;
#endif
#ifdef MK_ML_REF_N
half3 MLrN;
#endif
#ifdef MK_ML_DOT_V
half MLoV;
#endif
#ifdef MK_ML_REF_N_DOT_V
half MLrNoV;
#endif
};
/////////////////////////////////////////////////////////////////////////////////////////////
// Lighting Helpers
/////////////////////////////////////////////////////////////////////////////////////////////
#define DECLARE_LIGHTMAP_UV(i) float4 lightmapUV : TEXCOORD##i
#define DECLARE_STATIC_LIGHTMAP_INPUT(i) float2 staticLightmapUV : TEXCOORD##i
#if defined(MK_URP) || defined(MK_LWRP)
#define DECLARE_DYNAMIC_LIGHTMAP_INPUT(i)
/*
#if !defined(_MAIN_LIGHT_SHADOWS_CASCADE)
#define DECLARE_LIGHTING_COORDS(i, j) float4 _ShadowCoord : TEXCOORD##i;
#else
#define DECLARE_LIGHTING_COORDS(i, j)
#endif
*/
//lighting coords are currently also used for later PS usage when cascade is enabled, so always set shadow coord
#define DECLARE_LIGHTING_COORDS(i, j) float4 _ShadowCoord : TEXCOORD##i;
#if !defined(_MAIN_LIGHT_SHADOWS_CASCADE)
#define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) l._ShadowCoord = TransformWorldToShadowCoord(o.positionWorld.xyz);
#else
#define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) l._ShadowCoord = 1;
#endif
#else
#define DECLARE_DYNAMIC_LIGHTMAP_INPUT(i) float2 dynamicLightmapUV : TEXCOORD##i;
#if UNITY_VERSION >= 201810
#define DECLARE_LIGHTING_COORDS(i, j) UNITY_LIGHTING_COORDS(6,7)
#else
#define DECLARE_LIGHTING_COORDS(i, j) UNITY_SHADOW_COORDS(6)
#endif
#if UNITY_VERSION >= 201810
#define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) UNITY_TRANSFER_LIGHTING(l, i.staticLightmapUV);
#else
#define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) UNITY_TRANSFER_SHADOW(l, i.staticLightmapUV);
#endif
#endif
struct VertexOutputLight
{
float4 SV_CLIP_POS : SV_POSITION;
#ifdef MK_LIT
#ifdef MK_VERTEX_LIGHTING
//should be automatically clamped (0 - 1) at a 8bit precision, still enough for a simple vertex lighting
autoLP3 vertexLighting : COLOR1;
#endif
#ifdef MK_ENVIRONMENT_REFLECTIONS
DECLARE_LIGHTMAP_UV(5);
#endif
DECLARE_LIGHTING_COORDS(6, 7)
#endif
};
inline half3 ComputeSHVertex(half3 normalWorld)
{
#if defined(MK_URP) || defined(MK_LWRP)
return SampleSHVertex(normalWorld);
#else
return ShadeSHPerVertex(normalWorld, 0); //Base Ambient = 0 0 0
#endif
}
inline float2 ComputeStaticLightmapUV(float2 staticLightmapUV)
{
return staticLightmapUV * unity_LightmapST.xy + unity_LightmapST.zw;
}
inline float2 ComputeDynamicLightmapUV(float2 dynamicLightmapUV)
{
return dynamicLightmapUV * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
}
#if defined(MK_LIGHT_BANDED)
#define LIGHT_STYLE_RAW_1D(value, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = Banding(value, _LightBands, smoothnessMin, smoothnessMax, threshold, _LightBandsScale)
#elif defined(MK_LIGHT_CEL)
#define LIGHT_STYLE_RAW_1D(value, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = Cel(threshold, smoothnessMin, smoothnessMax, value)
#elif defined(MK_LIGHT_RAMP)
#define LIGHT_STYLE_RAW_1D(value, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = SampleRamp1D(PASS_TEXTURE_2D(ramp, samplerRamp), value).r
#else //MK_LIGHT_BUILTIN
#define LIGHT_STYLE_RAW_1D(value, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = max(0.0, value)
#endif
#if defined(MK_LIGHT_BANDED)
#define LIGHT_STYLE_RAW_2D(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = Banding(value, _LightBands, smoothnessMin, smoothnessMax, threshold, _LightBandsScale)
#elif defined(MK_LIGHT_CEL)
#define LIGHT_STYLE_RAW_2D(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = Cel(threshold, smoothnessMin, smoothnessMax, value)
#elif defined(MK_LIGHT_RAMP)
#define LIGHT_STYLE_RAW_2D(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = SampleRamp2D(PASS_TEXTURE_2D(ramp, samplerRamp), half2(value, atten)).rgb
#else //MK_LIGHT_BUILTIN
#define LIGHT_STYLE_RAW_2D(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = max(0.0, value)
#endif
#if defined(MK_ARTISTIC_DRAWN)
#define ARTISTIC_RAW(value) value = Drawn(value, surface.artistic0, _DrawnClampMin, _DrawnClampMax)
#elif defined(MK_ARTISTIC_HATCHING)
#define ARTISTIC_RAW(value) value = Hatching(surface.artistic0, surface.artistic1, value, 0.166667h)
#elif defined(MK_ARTISTIC_SKETCH)
#define ARTISTIC_RAW(value) value = Sketch(surface.artistic0, 1, value)
#else
#define ARTISTIC_RAW(value)
#endif
#if defined(MK_ARTISTIC_DRAWN)
#define ARTISTIC_RAW_ADDITIVE(value) value = Drawn(value, surface.artistic0, _DrawnClampMax)
#elif defined(MK_ARTISTIC_HATCHING)
#define ARTISTIC_RAW_ADDITIVE(value) value = Hatching(surface.artistic0, surface.artistic1, value, 0)
#elif defined(MK_ARTISTIC_SKETCH)
#define ARTISTIC_RAW_ADDITIVE(value) value = Sketch(surface.artistic0, value)
#else
#define ARTISTIC_RAW_ADDITIVE(value)
#endif
#define TRANSFER_SCALAR_TO_VECTOR(value) value.rgb = value.a
//Isotropic Reflection
inline MKGlossyEnvironmentData SetupGlossyEnvironmentData(half3 reflectDirection, half roughness)
{
MKGlossyEnvironmentData data;
data.roughness = roughness;
data.reflectDirection = reflectDirection;
return data;
}
//Anisotropic Reflection
inline MKGlossyEnvironmentData SetupGlossyEnvironmentData(half3 reflectDirection, half3 bitangentWorld, half3 tangentWorld, half3 normalWorld, half anisotropy, half roughness)
{
MKGlossyEnvironmentData data;
data.roughness = roughness;
//based on Rendering the World of Far Cry 4, McAuley Stephen
//streching could be optimized to align the aniso direction in a more correct way
half3 stretchDir;
#if SHADER_TARGET >= 30
stretchDir = anisotropy > 0 ? bitangentWorld : tangentWorld;
#else
stretchDir = bitangentWorld;
#endif
half3 reflectNormal = SafeNormalize(lerp(normalWorld, cross(cross(reflectDirection, stretchDir), stretchDir), abs(anisotropy) * 0.5));
data.reflectDirection = reflectDirection - 3.0 * dot(reflectNormal, reflectDirection) * reflectNormal;
return data;
}
//GI functions should match input
inline MKGI GlobalIllumination(MKGlossyEnvironmentData glossyED, half occlusion, MKLight mkLight, float3 positionWorld, half3 viewWorld, half3 normalWorld, float4 lightmapUVAndSH)
{
MKGI gi;
INITIALIZE_STRUCT(MKGI, gi);
#if defined(MK_URP) || defined(MK_LWRP)
gi.diffuse = SAMPLE_GI(lightmapUVAndSH.xy, lightmapUVAndSH.rgb, normalWorld);
#ifdef MK_ENVIRONMENT_REFLECTIONS_ADVANCED
gi.specular = GlossyEnvironmentReflection(glossyED.reflectDirection, glossyED.roughness, occlusion);
#else
gi.specular = gi.diffuse;
#endif
return gi;
#else
UnityGIInput giInput;
UnityLight unityLight;
unityLight.color = mkLight.color;
unityLight.dir = mkLight.dirWorld;
giInput.light = unityLight;
giInput.worldPos = positionWorld;
giInput.worldViewDir = -viewWorld;
giInput.atten = mkLight.attenuation;
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
giInput.ambient = 0;
giInput.lightmapUV = lightmapUVAndSH;
#else
giInput.ambient = lightmapUVAndSH.rgb;
giInput.lightmapUV = 0;
#endif
giInput.probeHDR[0] = unity_SpecCube0_HDR;
giInput.probeHDR[1] = unity_SpecCube1_HDR;
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
#endif
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
giInput.boxMax[0] = unity_SpecCube0_BoxMax;
giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
giInput.boxMax[1] = unity_SpecCube1_BoxMax;
giInput.boxMin[1] = unity_SpecCube1_BoxMin;
giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
#endif
//indirect specular depends on the _GLOSSYREFLECTIONS_OFF keyword by default
//however its not defined in the non glossy MKGI function and we get no indirect specular via the unity_IndirectSpecColor RGB
//so we always create the glossy environment
Unity_GlossyEnvironmentData uge;
uge.roughness = glossyED.roughness;
uge.reflUVW = glossyED.reflectDirection;
UnityGI unityGI = UnityGlobalIllumination(giInput, occlusion, normalWorld, uge);
gi.diffuse = unityGI.indirect.diffuse;
gi.specular = unityGI.indirect.specular;
return gi;
#endif
}
inline half3 ComputeVertexLighting(float3 positionWorld, half3 normalWorld)
{
#if defined(MK_URP) || defined(MK_LWRP)
return VertexLighting(positionWorld, normalWorld);
#else
return Shade4PointLights
(
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, positionWorld, normalWorld
);
#endif
}
// Most clamping is using saturate instead of max due to alu/performance reasons
MKLightData ComputeLightData(in MKLight light, in MKSurfaceData surfaceData)
{
MKLightData lightData;
INITIALIZE_STRUCT(MKLightData, lightData);
#ifdef MK_V_DOT_L
lightData.VoL = saturate(dot(light.dirWorld, surfaceData.viewWorld));
#endif
#ifdef MK_N_DOT_L
lightData.NoLRaw = dot(surfaceData.normalWorld, light.dirWorld);
lightData.NoL = saturate(dot(surfaceData.normalWorld, light.dirWorld));
#endif
#ifdef MK_LND
//instead of the surface normal, normalized pos in object space could be used
#ifdef MK_LIGHT_TRANSMISSION_SUB_SURFACE_SCATTERING
lightData.LND = light.dirWorld + surfaceData.normalWorld * dot(_LightTransmissionDistortion, REL_LUMA);
#else //Translucent
lightData.LND = light.dirWorld + surfaceData.normalWorld * _LightTransmissionDistortion;
#endif
#endif
#ifdef MK_V_DOT_LND
lightData.VoLND = saturate(dot(surfaceData.viewWorld, -lightData.LND));
#endif
#ifdef MK_HV
lightData.HV = SafeNormalize(light.dirWorld + surfaceData.viewWorld);
#endif
#ifdef MK_V_DOT_HV
lightData.VoHV = saturate(dot(lightData.HV, surfaceData.viewWorld));
lightData.oneMinusVoHV = 1.0 - lightData.VoHV;
#endif
//tohv and bohv should not be clamped because of the aniso usage
#ifdef MK_T_DOT_HV
lightData.ToHV = dot(surfaceData.tangentWorld, lightData.HV);
#endif
#ifdef MK_B_DOT_HV
lightData.BoHV = dot(surfaceData.bitangentWorld, lightData.HV);
#endif
#ifdef MK_N_DOT_HV
lightData.NoHV = saturate(dot(surfaceData.normalWorld, lightData.HV));
#endif
#ifdef MK_L_DOT_HV
lightData.LoHV = saturate(dot(light.dirWorld, lightData.HV));
#endif
#ifdef MK_ML_REF_N
lightData.MLrN = reflect(-light.dirWorld, surfaceData.normalWorld);
#endif
#ifdef MK_ML_DOT_V
lightData.MLoV = saturate(dot(-light.dirWorld, surfaceData.viewWorld));
#endif
#ifdef MK_ML_REF_N_DOT_V
lightData.MLrNoV = saturate(dot(lightData.MLrN, surfaceData.viewWorld));
#endif
return lightData;
}
#ifdef MK_LIT
//not lit variants should result in compile issue if a light is accidentally used
#if defined(MK_URP) || defined(MK_LWRP)
MKLight ConvertURPLightToMKLight(Light light)
{
MKLight mkLight;
INITIALIZE_STRUCT(MKLight, mkLight);
mkLight.color = light.color;
mkLight.radiometricColor = mkLight.color * PI;
mkLight.attenuation = light.distanceAttenuation;
mkLight.dirWorld = light.direction;
mkLight.distanceAttenuation = light.distanceAttenuation;
mkLight.shadowAttenuation = light.shadowAttenuation;
return mkLight;
}
Light ConvertMKLightToURPLight(MKLight mkLight)
{
Light light;
INITIALIZE_STRUCT(Light, light);
light.color = mkLight.color;
light.distanceAttenuation = mkLight.attenuation;
light.direction = mkLight.dirWorld;
light.distanceAttenuation = mkLight.distanceAttenuation;
light.shadowAttenuation = mkLight.shadowAttenuation;
return light;
}
#endif
MKLight ComputeMainLight(in MKSurfaceData surfaceData, inout VertexOutputLight vertexOutputLight)
{
MKLight mkLight;
INITIALIZE_STRUCT(MKLight, mkLight);
#if defined(MK_URP) || defined(MK_LWRP)
#if !defined(_MAIN_LIGHT_SHADOWS_CASCADE)
//
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
vertexOutputLight._ShadowCoord = TransformWorldToShadowCoord(surfaceData.positionWorld);
#else
//vertexOutputLight.shadowCoords = 0;
#endif
#endif
#if defined(MK_URP) || defined(MK_LWRP)
Light light;
INITIALIZE_STRUCT(Light, light);
#ifdef _MAIN_LIGHT_SHADOWS
light = GetMainLight(vertexOutputLight._ShadowCoord);
#else
light = GetMainLight();
#endif
mkLight.color = light.color;
mkLight.radiometricColor = mkLight.color * PI;
mkLight.attenuation = light.distanceAttenuation * light.shadowAttenuation;
mkLight.dirWorld = light.direction;
mkLight.distanceAttenuation = light.distanceAttenuation;
mkLight.shadowAttenuation = light.shadowAttenuation;
#else
//lightdirection and attenuation
#ifdef USING_DIRECTIONAL_LIGHT
mkLight.dirWorld = SafeNormalize(_WorldSpaceLightPos0.xyz);
mkLight.distanceAttenuation = 1;
#else
mkLight.dirWorld = SafeNormalize(_WorldSpaceLightPos0.xyz - surfaceData.positionWorld);
mkLight.distanceAttenuation = saturate(1.0 - ((distance(_WorldSpaceLightPos0.xyz, surfaceData.positionWorld)) / Rcp(_LightPositionRange.w)));
#endif
UNITY_LIGHT_ATTENUATION(atten, vertexOutputLight, surfaceData.positionWorld);
mkLight.attenuation = atten;
mkLight.color = _LightColor0.rgb;
mkLight.radiometricColor = mkLight.color * PI;
#endif
return mkLight;
}
MKLight ComputeAdditionalLight(int index, in MKSurfaceData surfaceData, inout VertexOutputLight vertexOutputLight)
{
#if defined(MK_URP) || defined(MK_LWRP)
MKLight mkLight;
INITIALIZE_STRUCT(MKLight, light);
Light light;
INITIALIZE_STRUCT(Light, light);
light = GetAdditionalLight(index, surfaceData.positionWorld);
mkLight.color = light.color;
mkLight.radiometricColor = mkLight.color * PI;
mkLight.attenuation = light.distanceAttenuation * light.shadowAttenuation;
mkLight.dirWorld = light.direction;
mkLight.distanceAttenuation = light.distanceAttenuation;
mkLight.shadowAttenuation = light.shadowAttenuation;
return mkLight;
#else
//On Legacy RP additional lights are computed per pass
return ComputeMainLight(surfaceData, vertexOutputLight);
#endif
}
#endif
inline half4 RimRawBright(half ndl, half size, half oneMinusVoN, half smoothness, Surface surface, MKLight light)
{
//to get a smoother rim ndl is multiplied into the interpolation
//to get a harder rim ndl could be multiplied afterwards
half4 rim;
rim.a = pow(oneMinusVoN, size);
#ifdef MK_THRESHOLD_MAP
rim.a -= _RimThresholdOffset * surface.thresholdOffset;
rim.a += _RimThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
LIGHT_STYLE_RAW_2D(rim.a, light.distanceAttenuation, T_V, smoothness, smoothness, _RimRamp, SAMPLER_CLAMPED_MAIN);
rim.a *= ndl;
ARTISTIC_RAW(rim.a);
TRANSFER_SCALAR_TO_VECTOR(rim);
return rim;
}
inline half4 RimRawDark(half ndl, half size, half oneMinusVoN, half smoothness, Surface surface, MKLight light)
{
half4 rim;
rim.a = pow(oneMinusVoN, size);
#ifdef MK_THRESHOLD_MAP
rim.a -= _RimThresholdOffset * surface.thresholdOffset;
rim.a += _RimThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
LIGHT_STYLE_RAW_2D(rim.a, light.distanceAttenuation, T_V, smoothness, smoothness, _RimRamp, SAMPLER_CLAMPED_MAIN);
rim.a *= ndl;
ARTISTIC_RAW(rim.a);
TRANSFER_SCALAR_TO_VECTOR(rim);
return rim;
}
//Rim with smooth interpolation
inline half4 RimRawEverything(half size, half oneMinusVoN, half smoothness, Surface surface)
{
half4 rim;
rim.a = pow(oneMinusVoN, size);
#ifdef MK_THRESHOLD_MAP
rim.a -= _RimThresholdOffset * surface.thresholdOffset;
rim.a += _RimThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
LIGHT_STYLE_RAW_1D(rim.a, T_V, smoothness, smoothness, _RimRamp, SAMPLER_CLAMPED_MAIN);
ARTISTIC_RAW(rim.a);
TRANSFER_SCALAR_TO_VECTOR(rim);
return rim;
}
inline half4 Iridescence(half size, half oneMinusVoN, half smoothness, Surface surface)
{
half4 iridescence;
iridescence.a = pow(oneMinusVoN, size);
#ifdef MK_THRESHOLD_MAP
iridescence.a -= _IridescenceThresholdOffset * surface.thresholdOffset;
iridescence.a += _IridescenceThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
#if !defined(MK_LIGHT_RAMP)
//only style scalar iridescence if lighting is not set to ramp
LIGHT_STYLE_RAW_1D(iridescence.a, T_V, smoothness, smoothness, _DiffuseRamp, SAMPLER_CLAMPED_MAIN);
#endif
ARTISTIC_RAW(iridescence.a);
TRANSFER_SCALAR_TO_VECTOR(iridescence);
iridescence.rgb *= (SampleRamp1D(PASS_TEXTURE_2D(_IridescenceRamp, SAMPLER_CLAMPED_MAIN), iridescence.a).rgb * _IridescenceColor.rgb);
return iridescence;
}
inline half Minnaert(half ndl, half vdn, half roughness)
{
return ndl * pow(saturate(ndl) * vdn, roughness);
}
half OrenNayar(half ndl, half ndv, half vdl, half roughness)
{
half3 rough = SafeDivide(roughness, roughness + half3(0.33, 0.13, 0.09));
half3 coeff = half3(1, 0, 0) + half3(-0.5, 0.17, 0.45) * rough;
half diff = vdl - ndl * ndv;
diff = SafeDivide(diff, lerp(max(ndl, ndv), 1, step(diff, 0)));
return ndl * (coeff.z * diff + coeff.y + coeff.x);
}
//Scale of the aniso
inline half2 AnisoScale(half roughness, half anisotropy)
{
return half2(roughness * (1 + anisotropy), roughness * (1 - anisotropy));
}
inline half DistributionGGX(half NoHV, half ToHV, half BoHV, half roughness, half anisoropy)
{
half2 anisoScale = AnisoScale(roughness, anisoropy);
half anisoStretch = anisoScale.x * anisoScale.y;
half3 anisoView = half3(anisoScale.x * ToHV, anisoScale.y * BoHV, anisoStretch * NoHV);
half p = Rcp(anisoStretch);
half ay = FastPow2(ToHV) / FastPow2(anisoScale.x);
half ax = FastPow2(BoHV) / FastPow2(anisoScale.y);
return p * Rcp(PI * FastPow2(ay + ax + FastPow2(NoHV)));
}
inline half DistributionGGX(half NoHV, half roughnessP4)
{
return roughnessP4 / (PI * FastPow2(FastPow2(NoHV) * (roughnessP4 - 1.0) + 1));
}
inline half GeometricSchlickGGX(half VoN, half roughness)
{
return VoN / (VoN * (1.0 - roughness) + roughness);
}
inline half GeometricSmithGGX(half VoN, half NoL, half roughness)
{
half directRoughness = FastPow2(roughness + 1.0) * THRESHOLD_OFFSET_NORMALIZER;
return GeometricSchlickGGX(VoN, directRoughness) * GeometricSchlickGGX(NoL, directRoughness);
}
half GeometricSmithkGGX(half anisoropy, half ToHV, half BoHV, half ToL, half BoL, half VoN, half NoL, half roughness)
{
half2 anisoScale = AnisoScale(roughness, anisoropy);
half V = NoL * length(half3(anisoScale.x * ToHV, anisoScale.y * BoHV, VoN));
half L = VoN * length(half3(anisoScale.x * ToL, anisoScale.y * BoL, NoL));
return SafeDivide(0.5, V + L);
}
half3 FresnelSchlickGGX(half oneMinusVoHV, half3 f0, half smoothness)
{
return FastPow5(oneMinusVoHV) * (max(smoothness, f0) - f0) + f0;
}
half3 FresnelCSch(half LoHV, half3 f0, half roughness)
{
return SafeDivide(f0, LoHV);
}
//Aniso specular blinn phong
inline half BlinnSpecularAniso(half3 normal, half3 halfV, half ndhv, half shine, half offset, half4 aDir, half ndl)
{
half term = pow(lerp(ndhv, max(0.0, sin(radians((dot(SafeNormalize(normal + aDir.rgb), halfV) + offset) * 180.0))), aDir.a), shine);
#if SHADER_TARGET >= 30
return (ndl > 0.0) ? term : 0.0;
#else
return term;
#endif
}
//specular blinn phong
inline half BlinnSpecular(half ndhv, half shine)
{
//exp2 instead of linear SHINE_MULT to match URP behavior
return pow(ndhv, exp2(10 * shine + 1));
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Lighting Indirect
/////////////////////////////////////////////////////////////////////////////////////////////
inline void LightingIndirect(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, in MKLight light, in MKLightData lightData)
{
#ifdef MK_INDIRECT
#if defined(MK_ENVIRONMENT_REFLECTIONS_ADVANCED)
#ifdef MK_SPECULAR_ANISOTROPIC
MKGlossyEnvironmentData ged = SetupGlossyEnvironmentData(-surfaceData.viewWorld, surfaceData.bitangentWorld, surfaceData.tangentWorld, surfaceData.normalWorld, _Anisotropy, pbsData.roughness);
#else
MKGlossyEnvironmentData ged = SetupGlossyEnvironmentData(surfaceData.MVrN, pbsData.roughness);
#endif
#elif defined(MK_ENVIRONMENT_REFLECTIONS_AMBIENT)
MKGlossyEnvironmentData ged = SetupGlossyEnvironmentData(0, pbsData.roughness);
#endif
#if defined(MK_ENVIRONMENT_REFLECTIONS)
MKGI gi;
#if defined(MK_ENVIRONMENT_REFLECTIONS_ADVANCED)
gi = GlobalIllumination(ged, surface.occlusion.r, light, surfaceData.positionWorld, surfaceData.viewWorld, surfaceData.normalWorld, surfaceData.lightmapUV);
#elif defined(MK_ENVIRONMENT_REFLECTIONS_AMBIENT)
gi = GlobalIllumination(ged, surface.occlusion.r, light, surfaceData.positionWorld, 0, surfaceData.normalWorld, surfaceData.lightmapUV);
#endif
#if defined(MK_URP) || defined(MK_LWRP)
Light urpLight;
INITIALIZE_STRUCT(Light, urpLight);
urpLight = ConvertMKLightToURPLight(light);
MixRealtimeAndBakedGI(urpLight, surfaceData.normalWorld, gi.diffuse, half4(0, 0, 0, 0));
#endif
/*
#ifdef MK_ARTISTIC
surface.indirect = lerp(0, gi.diffuse * pbsData.diffuseRadiance, surface.direct);
#else
surface.indirect = gi.diffuse * pbsData.diffuseRadiance;
#endif
*/
surface.indirect = gi.diffuse * pbsData.diffuseRadiance;
#else
surface.indirect = pbsData.specularRadiance * pbsData.reflectivity;
#endif
half3 iL;
half3 indirectReflectRadiance;
#if defined(MK_PBS)
indirectReflectRadiance = pbsData.specularRadiance;
#else //Simple
indirectReflectRadiance = 0;
#endif
#ifdef MK_FRESNEL_HIGHLIGHTS
//lerp(indirectReflectRadiance, saturate(pbsData.smoothness + pbsData.reflectivity), pbsData.fresnel); // lerp(0.33, 1, pbsData.smoothness)
iL = pbsData.fresnel;
#else
iL = indirectReflectRadiance;
#endif
#if defined(MK_ENVIRONMENT_REFLECTIONS)
surface.indirect += Rcp(pbsData.roughnessPow4 + 1.0) * gi.specular * iL;
#endif
#ifdef MK_EMISSION
//Emission handled as indirect
surface.indirect += surface.emission;
#endif
#endif
}
/////////////////////////////////////////////////////////////////////////////////////////////
// Lighting Direct
/////////////////////////////////////////////////////////////////////////////////////////////
inline void LightingDirect(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, in MKLight light, in MKLightData lightData, out half4 finalLightColor)
{
#ifdef MK_LIT
half4 diffuse;
#if defined(MK_DIFFUSE_MINNAERT)
diffuse.a = Minnaert(lightData.NoL, surfaceData.VoN, pbsData.roughnessPow2);
#elif defined(MK_DIFFUSE_OREN_NAYAR)
diffuse.a = OrenNayar(lightData.NoL, surfaceData.VoN, lightData.VoL, pbsData.roughnessPow2);
#else
//MK_SIMPLE
diffuse.a = lightData.NoL;
#endif
//Customize light atten
//LIGHT_STYLE_RAW_1D(light.attenuation, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp);
//ARTISTIC_RAW(light.attenuation);
//diffuse.a = lerp(diffuse.a, diffuse.a * light.attenuation, step(0, diffuse.a));
#if defined(MK_WRAPPED_DIFFUSE)
diffuse.a = HalfWrap(diffuse.a, 0.5);
#endif
#ifdef MK_THRESHOLD_MAP
diffuse.a -= _DiffuseThresholdOffset * surface.thresholdOffset;
diffuse.a += _DiffuseThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
//Lighting could be optimized by combining every component (diffuse, specular, lightTransmission, indirect/direct), may break gooch
LIGHT_STYLE_RAW_2D(diffuse.a, light.distanceAttenuation, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN);
diffuse.a *= light.attenuation;
ARTISTIC_RAW(diffuse.a);
TRANSFER_SCALAR_TO_VECTOR(diffuse);
half3 goochRamp;
#ifdef MK_GOOCH_RAMP
goochRamp = lerp(1.0, SampleRamp2D(PASS_TEXTURE_2D(_GoochRamp, SAMPLER_CLAMPED_MAIN), half2(diffuse.a, light.distanceAttenuation)).rgb, _GoochRampIntensity);
#else
goochRamp = 1.0;
#endif
half3 gooch;
//Gooch needs to be applied on diffuse only to not distract other light styles such as indirect, spec, lightTransmission
gooch = goochRamp * lerp(surface.goochDark.rgb, surface.goochBright.rgb, max(diffuse.r, max(diffuse.g, diffuse.b)));
//#ifdef MK_GOOCH_RAMP
// gooch.rgb = lerp(gooch.rgb, SampleRamp2D(PASS_TEXTURE_2D(_GoochRamp, SAMPLER_CLAMPED_MAIN), half2(diffuse.a, light.distanceAttenuation)).rgb, _GoochRampIntensity);
//#endif
//Surface Direct + Gooch
#ifdef MK_SPECULAR
half4 specular;
#if defined(MK_PBS)
//Distribution - Geometric - Fresnel
half distribution, geometric;
half3 sFresnel;
#ifdef MK_SPECULAR_ANISOTROPIC
//BRDF Aniso Specular
distribution = DistributionGGX(lightData.NoHV, lightData.ToHV, lightData.BoHV, pbsData.roughnessPow2, _Anisotropy);
//Isotropic Geo term is producing more pleasant results so its used for now
geometric = GeometricSmithGGX(surfaceData.VoN, diffuse.a, pbsData.roughness);
//geometric = GeometricSmithkGGX(_Anisotropy, lightData.ToHV, lightData.BoHV, dot(surfaceData.tangentWorld, light.dirWorld), dot(surfaceData.bitangentWorld, light.dirWorld), surfaceData.VoN, diffuse.a, pbsData.roughnessPow2);
//sFresnel = FresnelSchlickGGX(lightData.oneMinusVoHV, pbsData.specularRadiance, pbsData.smoothness);
sFresnel = FresnelCSch(lightData.LoHV, pbsData.specularRadiance, pbsData.roughness);
#else
distribution = DistributionGGX(lightData.NoHV, pbsData.roughnessPow4);
geometric = GeometricSmithGGX(surfaceData.VoN, diffuse.a, pbsData.roughness);
//sFresnel = FresnelSchlickGGX(lightData.oneMinusVoHV, pbsData.specularRadiance, pbsData.smoothness);
sFresnel = FresnelCSch(lightData.LoHV, pbsData.specularRadiance, pbsData.roughness);
#endif
specular.a = distribution * geometric;
specular.a = SafeDivide(specular.a, 4 * surfaceData.VoN * diffuse.a);
#else //MK_SIMPLE Iso Only
specular.a = BlinnSpecular(lightData.NoHV, pbsData.smoothness);
#endif
#ifdef MK_THRESHOLD_MAP
specular.a -= _SpecularThresholdOffset * surface.thresholdOffset;
specular.a += _SpecularThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
// specular could be thresholded using: lerp(T_Q, 1, _LightThreshold) but confuses the user because specular is influenced by smoothness and threshold then
LIGHT_STYLE_RAW_2D(specular.a, light.distanceAttenuation, T_V, _SpecularSmoothness * 0.5, _SpecularSmoothness * 0.5, _SpecularRamp, SAMPLER_CLAMPED_MAIN);
ARTISTIC_RAW_ADDITIVE(specular.a);
TRANSFER_SCALAR_TO_VECTOR(specular);
#ifdef MK_PBS
finalLightColor.rgb = ((sFresnel * _SpecularIntensity) * specular.rgb + (pbsData.diffuseRadiance * INV_PI)) * light.radiometricColor * gooch;
#else
finalLightColor.rgb = (pbsData.specularRadiance * _SpecularIntensity * specular.rgb + (pbsData.diffuseRadiance * INV_PI)) * light.radiometricColor * gooch;
#endif
#else
finalLightColor.rgb = (pbsData.diffuseRadiance * INV_PI) * light.radiometricColor * gooch;
#endif
#ifdef MK_LightTransmission
half4 lightTransmission;
//A scaling could be implemented here: dot(vohld, scale) then saturate
lightTransmission.a = FastPow4(lightData.VoLND);
//Based on Colin Barre-Brisebois - GDC 2011 - Approximating Translucency for a Fast, Cheap and Convincing Subsurface-Scattering
#ifdef MK_THICKNESS_MAP
lightTransmission.a *= surface.thickness;
#endif
half sssAtten = light.distanceAttenuation;
//Custom atten stylize not required, because shadows dont affect it
#ifdef MK_LIGHT_TRANSMISSION_TRANSLUCENT
sssAtten = lerp(0, sssAtten, pbsData.oneMinusReflectivity);
#endif
lightTransmission.a *= sssAtten;
#ifdef MK_THRESHOLD_MAP
lightTransmission.a -= _LightTransmissionThresholdOffset * surface.thresholdOffset;
lightTransmission.a += _LightTransmissionThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
LIGHT_STYLE_RAW_2D(lightTransmission.a, light.distanceAttenuation, T_V, _LightTransmissionSmoothness * 0.5, _LightTransmissionSmoothness * 0.5, _LightTransmissionRamp, SAMPLER_CLAMPED_MAIN);
ARTISTIC_RAW_ADDITIVE(lightTransmission.a);
TRANSFER_SCALAR_TO_VECTOR(lightTransmission);
#ifdef MK_LIGHT_TRANSMISSION_SUB_SURFACE_SCATTERING
finalLightColor.rgb += lightTransmission.rgb * _LightTransmissionColor.rgb * light.color * pbsData.diffuseRadiance * _LightTransmissionIntensity;
#else //Translucent
finalLightColor.rgb += lightTransmission.rgb * _LightTransmissionColor.rgb * light.color * _LightTransmissionIntensity;
#endif
#endif
#if defined(MK_RIM_SPLIT)
#ifndef MK_ADDITIONAL_LIGHTS
surface.rim += RimRawBright(diffuse.a, _RimSize, surfaceData.OneMinusVoN, _RimSmoothness * 0.5, surface, light);
#endif
//surface.rimDark = RimRawDark(1.0 - saturate(diffuse.a), _RimSize, surfaceData.OneMinusVoN, _RimSmoothness * 0.5, surface, light);
#endif
/*
#if defined(MK_ALPHA_LOOKUP)
finalLightColor.a = dot(finalLightColor.rgb, REL_LUMA);
//finalLightColor.a = (finalLightColor.r + finalLightColor.g + finalLightColor.b) * 0.33;
#endif
#if defined(MK_SURFACE_TYPE_OPAQUE)
finalLightColor.a = 1;
#endif
*/
finalLightColor.a = 1;
#endif
}
inline void LightingDirectAdditional(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, in MKLight light, in MKLightData lightData, out half4 finalLightColor)
{
#ifdef MK_LIT
half4 diffuse;
#if defined(MK_DIFFUSE_MINNAERT)
diffuse.a = Minnaert(lightData.NoL, surfaceData.VoN, pbsData.roughnessPow2);
#elif defined(MK_DIFFUSE_OREN_NAYAR)
diffuse.a = OrenNayar(lightData.NoL, surfaceData.VoN, lightData.VoL, pbsData.roughnessPow2);
#else
//MK_SIMPLE
diffuse.a = lightData.NoL;
#endif
//Customize light atten
//LIGHT_STYLE_RAW_1D(light.attenuation, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp);
//ARTISTIC_RAW(light.attenuation);
//diffuse.a = lerp(diffuse.a, diffuse.a * light.attenuation, step(0, diffuse.a));
#if defined(MK_WRAPPED_DIFFUSE)
diffuse.a = HalfWrap(diffuse.a, 0.5);
#endif
#ifdef MK_THRESHOLD_MAP
diffuse.a -= _DiffuseThresholdOffset * surface.thresholdOffset;
diffuse.a += _DiffuseThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
//Lighting could be optimized by combining every component (diffuse, specular, lightTransmission, indirect/direct), may break gooch
LIGHT_STYLE_RAW_2D(diffuse.a, light.distanceAttenuation, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN);
diffuse.a *= light.attenuation;
ARTISTIC_RAW(diffuse.a);
TRANSFER_SCALAR_TO_VECTOR(diffuse);
half3 goochRamp;
#ifdef MK_GOOCH_RAMP
goochRamp = lerp(1.0, SampleRamp2D(PASS_TEXTURE_2D(_GoochRamp, SAMPLER_CLAMPED_MAIN), half2(diffuse.a, light.distanceAttenuation)).rgb, _GoochRampIntensity);
#else
goochRamp = 1.0;
#endif
half3 gooch;
//Gooch needs to be applied on diffuse only to not distract other light styles such as indirect, spec, lightTransmission
gooch = goochRamp * lerp(surface.goochDark.rgb, surface.goochBright.rgb, max(diffuse.r, max(diffuse.g, diffuse.b)));
//#ifdef MK_GOOCH_RAMP
// gooch.rgb = lerp(gooch.rgb, SampleRamp2D(PASS_TEXTURE_2D(_GoochRamp, SAMPLER_CLAMPED_MAIN), half2(diffuse.a, light.distanceAttenuation)).rgb, _GoochRampIntensity);
//#endif
//Surface Direct + Gooch
#ifdef MK_SPECULAR
half4 specular;
#if defined(MK_PBS)
//Distribution - Geometric - Fresnel
half distribution, geometric;
half3 sFresnel;
#ifdef MK_SPECULAR_ANISOTROPIC
//BRDF Aniso Specular
distribution = DistributionGGX(lightData.NoHV, lightData.ToHV, lightData.BoHV, pbsData.roughnessPow2, _Anisotropy);
//Isotropic Geo term is producing more pleasant results so its used for now
geometric = GeometricSmithGGX(surfaceData.VoN, diffuse.a, pbsData.roughness);
//geometric = GeometricSmithkGGX(_Anisotropy, lightData.ToHV, lightData.BoHV, dot(surfaceData.tangentWorld, light.dirWorld), dot(surfaceData.bitangentWorld, light.dirWorld), surfaceData.VoN, diffuse.a, pbsData.roughnessPow2);
//sFresnel = FresnelSchlickGGX(lightData.oneMinusVoHV, pbsData.specularRadiance, pbsData.smoothness);
sFresnel = FresnelCSch(lightData.LoHV, pbsData.specularRadiance, pbsData.roughness);
#else
distribution = DistributionGGX(lightData.NoHV, pbsData.roughnessPow4);
geometric = GeometricSmithGGX(surfaceData.VoN, diffuse.a, pbsData.roughness);
//sFresnel = FresnelSchlickGGX(lightData.oneMinusVoHV, pbsData.specularRadiance, pbsData.smoothness);
sFresnel = FresnelCSch(lightData.LoHV, pbsData.specularRadiance, pbsData.roughness);
#endif
specular.a = distribution * geometric;
specular.a = SafeDivide(specular.a, 4 * surfaceData.VoN * diffuse.a);
#else //MK_SIMPLE Iso Only
specular.a = BlinnSpecular(lightData.NoHV, pbsData.smoothness);
#endif
#ifdef MK_THRESHOLD_MAP
specular.a -= _SpecularThresholdOffset * surface.thresholdOffset;
specular.a += _SpecularThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
// specular could be thresholded using: lerp(T_Q, 1, _LightThreshold) but confuses the user because specular is influenced by smoothness and threshold then
LIGHT_STYLE_RAW_2D(specular.a, light.distanceAttenuation, T_V, _SpecularSmoothness * 0.5, _SpecularSmoothness * 0.5, _SpecularRamp, SAMPLER_CLAMPED_MAIN);
ARTISTIC_RAW_ADDITIVE(specular.a);
TRANSFER_SCALAR_TO_VECTOR(specular);
#ifdef MK_PBS
finalLightColor.rgb = ((sFresnel * _SpecularIntensity) * specular.rgb + (pbsData.diffuseRadiance * INV_PI)) * light.radiometricColor * gooch;
#else
finalLightColor.rgb = (pbsData.specularRadiance * _SpecularIntensity * specular.rgb + (pbsData.diffuseRadiance * INV_PI)) * light.radiometricColor * gooch;
#endif
#else
finalLightColor.rgb = (pbsData.diffuseRadiance * INV_PI) * light.radiometricColor * gooch;
#endif
#ifdef MK_LightTransmission
half4 lightTransmission;
//A scaling could be implemented here: dot(vohld, scale) then saturate
lightTransmission.a = FastPow4(lightData.VoLND);
//Based on Colin Barre-Brisebois - GDC 2011 - Approximating Translucency for a Fast, Cheap and Convincing Subsurface-Scattering
#ifdef MK_THICKNESS_MAP
lightTransmission.a *= surface.thickness;
#endif
half sssAtten = light.distanceAttenuation;
//Custom atten stylize not required, because shadows dont affect it
#ifdef MK_LIGHT_TRANSMISSION_TRANSLUCENT
sssAtten = lerp(0, sssAtten, pbsData.oneMinusReflectivity);
#endif
lightTransmission.a *= sssAtten;
#ifdef MK_THRESHOLD_MAP
lightTransmission.a -= _LightTransmissionThresholdOffset * surface.thresholdOffset;
lightTransmission.a += _LightTransmissionThresholdOffset * THRESHOLD_OFFSET_NORMALIZER;
#endif
LIGHT_STYLE_RAW_2D(lightTransmission.a, light.distanceAttenuation, T_V, _LightTransmissionSmoothness * 0.5, _LightTransmissionSmoothness * 0.5, _LightTransmissionRamp, SAMPLER_CLAMPED_MAIN);
ARTISTIC_RAW_ADDITIVE(lightTransmission.a);
TRANSFER_SCALAR_TO_VECTOR(lightTransmission);
#ifdef MK_LIGHT_TRANSMISSION_SUB_SURFACE_SCATTERING
finalLightColor.rgb += lightTransmission.rgb * _LightTransmissionColor.rgb * light.color * pbsData.diffuseRadiance * _LightTransmissionIntensity;
#else //Translucent
finalLightColor.rgb += lightTransmission.rgb * _LightTransmissionColor.rgb * light.color * _LightTransmissionIntensity;
#endif
#endif
/*
#if defined(MK_ALPHA_LOOKUP)
finalLightColor.a = dot(finalLightColor.rgb, REL_LUMA);
//finalLightColor.a = (finalLightColor.r + finalLightColor.g + finalLightColor.b) * 0.33;
#endif
#if defined(MK_SURFACE_TYPE_OPAQUE)
finalLightColor.a = 1;
#endif
*/
finalLightColor.a = 1;
#endif
}
#endif