////////////////////////////////////////////////////// // MK Toon Lighting // // // // Created by Michael Kremmel // // www.michaelkremmel.de // // Copyright © 2020 All rights reserved. // ////////////////////////////////////////////////////// #ifndef MK_TOON_LIGHTING #define MK_TOON_LIGHTING #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" #ifdef POINT #define UNITY_LIGHT_ATTENUATION_DISTANCE(destName, input, worldPos) \ unityShadowCoord3 lightDistanceCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xyz; \ fixed destName = tex2D(_LightTexture0, dot(lightDistanceCoord, lightDistanceCoord).rr).r; #endif #ifdef SPOT #if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS) #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord4 lightDistanceCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)) #else #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord4 lightDistanceCoord = input._LightCoord #endif #define UNITY_LIGHT_ATTENUATION_DISTANCE(destName, input, worldPos) \ DECLARE_LIGHT_DISTANCE_COORD(input, worldPos); \ fixed destName = (lightDistanceCoord.z > 0) * UnitySpotCookie(lightDistanceCoord) * UnitySpotAttenuate(lightDistanceCoord.xyz); #endif #ifdef DIRECTIONAL #define UNITY_LIGHT_ATTENUATION_DISTANCE(destName, input, worldPos) fixed destName = UNITY_SHADOW_ATTENUATION(input, worldPos); #endif #ifdef POINT_COOKIE #if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS) #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord3 lightDistanceCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xyz #else #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord3 lightDistanceCoord = input._LightCoord #endif #define UNITY_LIGHT_ATTENUATION_DISTANCE(destName, input, worldPos) \ DECLARE_LIGHT_DISTANCE_COORD(input, worldPos); \ fixed destName = tex2D(_LightTextureB0, dot(lightDistanceCoord, lightDistanceCoord).rr).r * texCUBE(_LightTexture0, lightDistanceCoord).w; #endif #ifdef DIRECTIONAL_COOKIE #if !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS) #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord2 lightDistanceCoord = mul(unity_WorldToLight, unityShadowCoord4(worldPos, 1)).xy #else #define DECLARE_LIGHT_DISTANCE_COORD(input, worldPos) unityShadowCoord2 lightDistanceCoord = input._LightCoord #endif #define UNITY_LIGHT_ATTENUATION_DISTANCE(destName, input, worldPos) \ DECLARE_LIGHT_DISTANCE_COORD(input, worldPos); \ fixed destName = tex2D(_LightTexture0, lightDistanceCoord).w; #endif #endif #include "Surface.hlsl" #ifndef MK_GOOCH_REMAP_MAX_INPUT #define MK_GOOCH_REMAP_MAX_INPUT half3(1,1,1) #endif // ------------------------------------------------------------------------------------------ // 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 { half3 color; half3 radiometricColor; half3 dirWorld; half attenuation; half distanceAttenuation; half shadowAttenuation; #if defined(MK_URP) && UNITY_VERSION >= 202120 uint layerMask; #endif #ifdef MK_LEGACY_RP #ifdef MK_HANDLE_SHADOWS_BLENDING_IN_GI half giBlend; #endif #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_LHV float3 LHV; #endif #ifdef MK_L_DOT_LHV half LoLHV; #endif #ifdef MK_V_DOT_LHV half VoLHV; half oneMinusVoHV; #endif #ifdef MK_T_DOT_LHV half ToLHV; #endif #ifdef MK_B_DOT_LHV half BoLHV; #endif #ifdef MK_N_DOT_LHV half NoLHV; #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) float4 MKGetShadowCoord(VertexPositionInputs vertexInput) { #if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(MK_SURFACE_TYPE_TRANSPARENT) return ComputeScreenPos(vertexInput.positionCS); #else return TransformWorldToShadowCoord(vertexInput.positionWS); #endif } #define DECLARE_DYNAMIC_LIGHTMAP_INPUT(i) float2 dynamicLightmapUV : TEXCOORD##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(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) #if defined(_MAIN_LIGHT_SHADOWS_SCREEN) && !defined(MK_SURFACE_TYPE_TRANSPARENT) #define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) l._ShadowCoord = ComputeScreenPos(l.SV_CLIP_POS); #else #define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) l._ShadowCoord = TransformWorldToShadowCoord(o.positionWorld.xyz); #endif #else #define TRANSFORM_WORLD_TO_SHADOW_COORDS(o, i, l) l._ShadowCoord = float4(0, 0, 0, 0); #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 half3 vertexLighting : COLOR1; #endif #ifdef MK_LIGHTMAP_UV 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 } #if UNITY_VERSION < 60000000 && defined(MK_URP) || defined(MK_LEGACY_RP) inline half3 ComputeSHVertex(float3 positionWorld, half3 normalWorld, half3 viewDirectionWorld) { #if defined(MK_URP) && UNITY_VERSION >= 202310 && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) return SampleProbeVolumeVertex(positionWorld, normalWorld, viewDirectionWorld); #else return ComputeSHVertex(normalWorld); #endif } #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)).r #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_LIGHT_BANDED) #define LIGHT_STYLE_RAW_2D_ADDITIONAL(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = BandingAdditional(value, _LightBands, smoothnessMin, smoothnessMax, threshold, _LightBandsScale) #elif defined(MK_LIGHT_CEL) #define LIGHT_STYLE_RAW_2D_ADDITIONAL(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = CelAdditional(threshold, smoothnessMin, smoothnessMax, value) #elif defined(MK_LIGHT_RAMP) #define LIGHT_STYLE_RAW_2D_ADDITIONAL(value, atten, threshold, smoothnessMin, smoothnessMax, ramp, samplerRamp) value = SampleRamp2D(PASS_TEXTURE_2D(ramp, samplerRamp), half2(value, atten)).r #else //MK_LIGHT_BUILTIN #define LIGHT_STYLE_RAW_2D_ADDITIONAL(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) * step(HALF_MIN, value) #elif defined(MK_ARTISTIC_HATCHING) #define ARTISTIC_RAW_ADDITIVE(value) value = Hatching(surface.artistic0, surface.artistic1, value, 0) * step(HALF_MIN, value) #elif defined(MK_ARTISTIC_SKETCH) #define ARTISTIC_RAW_ADDITIVE(value) value = Sketch(surface.artistic0, value) * step(HALF_MIN, value) #else #define ARTISTIC_RAW_ADDITIVE(value) #endif #if defined(MK_ARTISTIC_DRAWN) #define ARTISTIC_RAW_MASKED(value, shadowMask, compare) value = Drawn(value, surface.artistic0, _DrawnClampMin, _DrawnClampMax) * step(HALF_MIN, compare) #elif defined(MK_ARTISTIC_HATCHING) #define ARTISTIC_RAW_MASKED(value, shadowMask, compare) value = Hatching(surface.artistic0, surface.artistic1, value, lerp(0, 0.166667h, shadowMask)) * step(HALF_MIN, compare) #elif defined(MK_ARTISTIC_SKETCH) #define ARTISTIC_RAW_MASKED(value, shadowMask, compare) value = Sketch(surface.artistic0, 1, value) * step(HALF_MIN, compare) #else #define ARTISTIC_RAW_MASKED(value, shadowMask, compare) #endif #if defined(MK_ARTISTIC_DRAWN) #define ARTISTIC_RAW_SHADOW_MASKED(value, shadowMask, compare) value = Drawn(value, surface.artistic0, lerp(_DrawnClampMin, _ArtisticShadowFilter, shadowMask), _DrawnClampMax) * step(HALF_MIN, compare) #elif defined(MK_ARTISTIC_HATCHING) #define ARTISTIC_RAW_SHADOW_MASKED(value, shadowMask, compare) value = Hatching(surface.artistic0, surface.artistic1, value, _ArtisticShadowFilter) * step(HALF_MIN, compare) #elif defined(MK_ARTISTIC_SKETCH) #define ARTISTIC_RAW_SHADOW_MASKED(value, shadowMask, compare) value = Sketch(surface.artistic0, 1, lerp(_ArtisticShadowFilter, value, value)) * step(HALF_MIN, compare) #else #define ARTISTIC_RAW_SHADOW_MASKED(value, shadowMask, compare) #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 = MKSafeNormalize(lerp(normalWorld, cross(cross(reflectDirection, stretchDir), stretchDir), abs(anisotropy) * 0.5)); data.reflectDirection = reflectDirection - 2.0 * dot(reflectNormal, reflectDirection) * reflectNormal; return data; } //GI functions should match input inline MKGI MKGlobalIllumination(in MKGlossyEnvironmentData glossyED, half occlusion, MKLight mkLight, in MKSurfaceData surfaceData) { MKGI gi; INITIALIZE_STRUCT(MKGI, gi); gi.diffuse = half3(0,0,0); gi.specular = half3(0,0,0); #ifdef MK_INDIRECT #if defined(MK_URP) || defined(MK_LWRP) #if UNITY_VERSION >= 60000009 #if defined(_SCREEN_SPACE_IRRADIANCE) gi.diffuse = SAMPLE_GI(_ScreenSpaceIrradiance, surfaceData.svPositionClip.xy); #elif defined(DYNAMICLIGHTMAP_ON) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.zw, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.rgb, GetAbsolutePositionWS(surfaceData.positionWorld), surfaceData.normalWorld, surfaceData.viewWorld, surfaceData.svPositionClip.xy, surfaceData.probeOcclusion, surfaceData.shadowMask); #else gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #endif #elif UNITY_VERSION >= 60000000 #if defined(DYNAMICLIGHTMAP_ON) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.zw, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.rgb, GetAbsolutePositionWS(surfaceData.positionWorld), surfaceData.normalWorld, surfaceData.viewWorld, surfaceData.svPositionClip.xy); #else gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #endif #elif UNITY_VERSION >= 202310 #if defined(DYNAMICLIGHTMAP_ON) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.zw, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #elif !defined(LIGHTMAP_ON) && (defined(PROBE_VOLUMES_L1) || defined(PROBE_VOLUMES_L2)) gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.rgb, GetAbsolutePositionWS(surfaceData.positionWorld), surfaceData.normalWorld, surfaceData.viewWorld, surfaceData.svPositionClip.xy); #else gi.diffuse = SAMPLE_GI(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #endif #else #if defined(LIGHTMAP_ON) && defined(DYNAMICLIGHTMAP_ON) gi.diffuse = SampleLightmap(surfaceData.lightmapUV.xy, surfaceData.lightmapUV.zw, surfaceData.normalWorld); #elif defined(DYNAMICLIGHTMAP_ON) gi.diffuse = SampleLightmap(0, surfaceData.lightmapUV.zw, surfaceData.normalWorld); #elif defined(LIGHTMAP_ON) #if UNITY_VERSION >= 202120 gi.diffuse = SampleLightmap(surfaceData.lightmapUV.xy, 0, surfaceData.normalWorld); #else gi.diffuse = SampleLightmap(surfaceData.lightmapUV.xy, surfaceData.normalWorld); #endif #else gi.diffuse = SampleSHPixel(surfaceData.lightmapUV.rgb, surfaceData.normalWorld); #endif #endif gi.diffuse *= occlusion; #if UNITY_VERSION >= 202220 gi.specular = GlossyEnvironmentReflection(glossyED.reflectDirection, surfaceData.positionWorld, glossyED.roughness, occlusion, surfaceData.screenUV.xy); #else gi.specular = GlossyEnvironmentReflection(glossyED.reflectDirection, glossyED.roughness, occlusion); #endif return gi; #else UnityGIInput giInput; UnityLight unityLight; unityLight.color = mkLight.color; unityLight.dir = mkLight.dirWorld; giInput.light = unityLight; giInput.worldPos = surfaceData.positionWorld; #ifdef MK_ENVIRONMENT_REFLECTIONS_AMBIENT giInput.worldViewDir = -surfaceData.viewWorld; #else giInput.worldViewDir = 0; #endif giInput.atten = mkLight.attenuation; #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON) giInput.ambient = 0; giInput.lightmapUV = surfaceData.lightmapUV; #endif #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL giInput.ambient = surfaceData.lightmapUV.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, surfaceData.normalWorld, uge); gi.diffuse = unityGI.indirect.diffuse; gi.specular = unityGI.indirect.specular; #endif return gi; #endif return gi; } 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_LHV lightData.LHV = MKSafeNormalize(light.dirWorld + surfaceData.viewWorld); #endif #ifdef MK_V_DOT_LHV lightData.VoLHV = saturate(dot(lightData.LHV, surfaceData.viewWorld)); lightData.oneMinusVoHV = 1.0 - lightData.VoLHV; #endif //tohv and bohv should not be clamped because of the aniso usage #ifdef MK_T_DOT_LHV lightData.ToLHV = dot(surfaceData.tangentWorld, lightData.LHV); #endif #ifdef MK_B_DOT_LHV lightData.BoLHV = dot(surfaceData.bitangentWorld, lightData.LHV); #endif #ifdef MK_N_DOT_LHV lightData.NoLHV = saturate(dot(surfaceData.normalWorld, lightData.LHV)); #endif #ifdef MK_L_DOT_LHV lightData.LoLHV = dot(light.dirWorld, lightData.LHV); #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.shadowAttenuation * light.distanceAttenuation; mkLight.dirWorld = light.direction; mkLight.distanceAttenuation = light.distanceAttenuation; mkLight.shadowAttenuation = light.shadowAttenuation; #if UNITY_VERSION >= 202120 mkLight.layerMask = light.layerMask; #endif return mkLight; } Light ConvertMKLightToURPLight(MKLight mkLight) { Light light; INITIALIZE_STRUCT(Light, light); light.color = mkLight.color; light.direction = mkLight.dirWorld; light.distanceAttenuation = mkLight.distanceAttenuation; light.shadowAttenuation = mkLight.shadowAttenuation; #if UNITY_VERSION >= 202120 light.layerMask = mkLight.layerMask; #endif return light; } #endif #if defined(MK_URP) || defined(MK_LWRP) #ifdef MK_LINEAR_lIGHT_DISTANCE_ATTENUATION inline void LinearDistanceAttenuation(inout Light light, in MKSurfaceData surfaceData, in int index) { //color & light layers should be untouched #if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA float4 lightPositionWS = _AdditionalLightsBuffer[index].position; half4 distanceAndSpotAttenuation = _AdditionalLightsBuffer[index].attenuation; half4 spotDirection = _AdditionalLightsBuffer[index].spotDirection; half4 lightOcclusionProbeInfo = _AdditionalLightsBuffer[index].occlusionProbeChannels; #else float4 lightPositionWS = _AdditionalLightsPosition[index]; half4 distanceAndSpotAttenuation = _AdditionalLightsAttenuation[index]; half4 spotDirection = _AdditionalLightsSpotDir[index]; half4 lightOcclusionProbeInfo = _AdditionalLightsOcclusionProbes[index]; #endif float3 LightToWorld = lightPositionWS.xyz - surfaceData.positionWorld * lightPositionWS.w; float distanceSqr = max(dot(LightToWorld, LightToWorld), HALF_MIN); half3 LightDir = half3(LightToWorld * rsqrt(distanceSqr)); float lightRangeSqr = Rcp(distanceAndSpotAttenuation.x); #if(META_QUEST_NO_SPOTLIGHTS_LIGHT_LOOP) light.distanceAttenuation = DistanceAttenuation(distanceSqr, distanceAndSpotAttenuation.xy); #else light.distanceAttenuation = saturate(saturate((1 - (distanceSqr / lightRangeSqr)) * 5.0) * Rcp(1.0 + 25.0 * distanceSqr / lightRangeSqr)) * AngleAttenuation(spotDirection.xyz, LightDir, distanceAndSpotAttenuation.zw); #endif } #endif #endif MKLight ComputeMainLight(in MKSurfaceData surfaceData, inout VertexOutputLight vertexOutputLight) { MKLight mkLight; INITIALIZE_STRUCT(MKLight, mkLight); #if defined(MK_URP) || defined(MK_LWRP) #if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) //skip interpolator... #elif defined(MAIN_LIGHT_CALCULATE_SHADOWS) vertexOutputLight._ShadowCoord = TransformWorldToShadowCoord(surfaceData.positionWorld); #else vertexOutputLight._ShadowCoord = float4(0, 0, 0, 0); #endif #endif #if defined(MK_URP) || defined(MK_LWRP) Light light; INITIALIZE_STRUCT(Light, light); #if defined(MK_URP_2020_2_Or_Newer) light = GetMainLight(vertexOutputLight._ShadowCoord, surfaceData.positionWorld, surfaceData.shadowMask); #else light = GetMainLight(vertexOutputLight._ShadowCoord); #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; #if UNITY_VERSION >= 202120 mkLight.layerMask = light.layerMask; #endif #else //lightdirection and attenuation #ifdef USING_DIRECTIONAL_LIGHT mkLight.dirWorld = MKSafeNormalize(_WorldSpaceLightPos0.xyz); mkLight.distanceAttenuation = 1; #else mkLight.dirWorld = MKSafeNormalize(_WorldSpaceLightPos0.xyz - surfaceData.positionWorld); //Atten Old //mkLight.distanceAttenuation = saturate(1.0 - ((distance(_WorldSpaceLightPos0.xyz, surfaceData.positionWorld)) / Rcp(_LightPositionRange.w))); UNITY_LIGHT_ATTENUATION_DISTANCE(distanceAtten, vertexOutputLight, surfaceData.positionWorld); mkLight.distanceAttenuation = lerp(CelFalloff(0.0h, _AdditionalLightsFalloff, distanceAtten) * 0.5h, distanceAtten, _AdditionalLightsFalloff); //mkLight.distanceAttenuation = distanceAtten; #endif UNITY_LIGHT_ATTENUATION(atten, vertexOutputLight, surfaceData.positionWorld); mkLight.shadowAttenuation = UNITY_SHADOW_ATTENUATION(vertexOutputLight, surfaceData.positionWorld); #ifdef MK_HANDLE_SHADOWS_BLENDING_IN_GI mkLight.giBlend = 0; atten = mkLight.shadowAttenuation; float shadowFade = UnityComputeShadowFade(UnityComputeShadowFadeDistance(surfaceData.positionWorld, dot(_WorldSpaceCameraPos - surfaceData.positionWorld, UNITY_MATRIX_V[2].xyz))); float bakedOcclusion = UnitySampleBakedOcclusion(surfaceData.lightmapUV.xy, surfaceData.positionWorld); atten = UnityMixRealtimeAndBakedShadows(atten, bakedOcclusion, shadowFade); #if defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) mkLight.giBlend = atten; #endif mkLight.shadowAttenuation = atten; #endif mkLight.attenuation = mkLight.shadowAttenuation * mkLight.distanceAttenuation; 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, mkLight); Light light; INITIALIZE_STRUCT(Light, light); #if defined(MK_URP_2020_2_Or_Newer) light = GetAdditionalLight(index, surfaceData.positionWorld, surfaceData.shadowMask); #else light = GetAdditionalLight(index, surfaceData.positionWorld); #endif #ifdef MK_LINEAR_lIGHT_DISTANCE_ATTENUATION #if USE_FORWARD_PLUS || USE_CLUSTER_LIGHT_LOOP int lightIndex = index; #else int lightIndex = GetPerObjectLightIndex(index); #endif LinearDistanceAttenuation(light, surfaceData, lightIndex); #endif mkLight.distanceAttenuation = lerp(CelFalloff(0, _AdditionalLightsFalloff, light.distanceAttenuation) * 0.5h, light.distanceAttenuation, _AdditionalLightsFalloff); //mkLight.distanceAttenuation = light.distanceAttenuation > 0.01; //mkLight.distanceAttenuation = smoothstep(0.001, 1, light.distanceAttenuation); //mkLight.distanceAttenuation = light.distanceAttenuation; mkLight.color = light.color; mkLight.radiometricColor = mkLight.color * PI; mkLight.attenuation = mkLight.distanceAttenuation * light.shadowAttenuation; mkLight.dirWorld = light.direction; mkLight.shadowAttenuation = light.shadowAttenuation; #if UNITY_VERSION >= 202120 mkLight.layerMask = light.layerMask; #endif 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) { //original ggx roughness //half aspect = sqrt(1.0 - 0.9 * anisotropy); //return half2(roughness / aspect, roughness * aspect); //approximated return half2(roughness * (1 + 0.9 * anisotropy), roughness * (1 - 0.9 * anisotropy)); } inline half DistributionGGX(half NoLHV, half ToLHV, half BoLHV, half roughness, half anisoropy) { half2 anisoScale = AnisoScale(roughness, anisoropy); half anisoStretch = anisoScale.x * anisoScale.y; half p = Rcp(anisoStretch); half ay = FastPow2HP(ToLHV) / FastPow2HP(anisoScale.x); half ax = FastPow2HP(BoLHV) / FastPow2HP(anisoScale.y); return INV_PI * p * Rcp(FastPow2HP(ay + ax + FastPow2HP(NoLHV))); } inline half DistributionGGX(half NoLHV, half roughnessP4) { return SafeDivide(roughnessP4, (PI * FastPow2HP(FastPow2HP(NoLHV) * (roughnessP4 - 1.0) + 1))); } inline half GeometricSchlickGGX(half VoN, half roughness) { return SafeDivide(VoN, (VoN * (1.0 - roughness) + roughness)); } inline half GeometricSmithGGX(half VoN, half NoL, half roughness) { half directRoughness = FastPow2HP(roughness + 1.0) * 0.125; return GeometricSchlickGGX(VoN, directRoughness) * GeometricSchlickGGX(NoL, directRoughness); } inline half3 FresnelCSch(half LoLHV, half3 f0) { //limit, because C.Sch goes to infinity instead of 1. return saturate(SafeDivide(f0, LoLHV)); } //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(MKSafeNormalize(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 float BlinnSpecular(float 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_SCREEN_SPACE_OCCLUSION) && defined(MK_URP_2020_2_Or_Newer) surface.occlusion.r *= surfaceData.ambientOcclusion.indirectAmbientOcclusion; #endif #if defined(MK_ENVIRONMENT_REFLECTIONS_ADVANCED) gi = MKGlobalIllumination(ged, surface.occlusion.r, light, surfaceData); #elif defined(MK_ENVIRONMENT_REFLECTIONS_AMBIENT) gi = MKGlobalIllumination(ged, surface.occlusion.r, light, surfaceData); #endif #if defined(MK_URP) || defined(MK_LWRP) Light urpLight; INITIALIZE_STRUCT(Light, urpLight); urpLight = ConvertMKLightToURPLight(light); MixRealtimeAndBakedGI(urpLight, surfaceData.normalWorld, gi.diffuse); #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 UNITY_BRANCH if(_FresnelHighlights > 0) //lerp(indirectReflectRadiance, saturate(pbsData.smoothness + pbsData.reflectivity), pbsData.fresnel); // lerp(0.33, 1, pbsData.smoothness) iL = pbsData.fresnel; else iL = indirectReflectRadiance; #else iL = indirectReflectRadiance; #endif #if defined(MK_ENVIRONMENT_REFLECTIONS) surface.indirect += Rcp(pbsData.roughnessPow4 + 1.0) * gi.specular * iL; #endif #ifdef MK_PBS surface.indirectFade = lerp(surface.indirectFade, pbsData.reflectivity, pbsData.reflectivity); #endif #ifdef MK_EMISSION //Emission handled as indirect surface.indirect += surface.emission; #endif #endif } ///////////////////////////////////////////////////////////////////////////////////////////// // Lighting Direct ///////////////////////////////////////////////////////////////////////////////////////////// inline half MKLightingDiffuse(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, inout MKLight light, in MKLightData lightData, in half threshold) { //mkLight.distanceAttenuation = lerp(smoothstep(0, _AdditionalLightsFalloff, light.distanceAttenuation), light.distanceAttenuation, _AdditionalLightsFalloff); #ifdef MK_LIT half diffuse; #if defined(MK_DIFFUSE_MINNAERT) diffuse = Minnaert(lightData.NoLRaw, surfaceData.VoN, pbsData.roughnessPow2); #elif defined(MK_DIFFUSE_OREN_NAYAR) diffuse = OrenNayar(lightData.NoLRaw, surfaceData.VoN, lightData.VoL, pbsData.roughnessPow2); #else //MK_SIMPLE diffuse = lightData.NoLRaw; #endif diffuse = lerp(diffuse, HalfWrap(diffuse, 0.5), step(HALF_MIN, _WrappedLighting)); #ifdef MK_THRESHOLD_MAP diffuse -= _DiffuseThresholdOffset * surface.thresholdOffset; diffuse += _DiffuseThresholdOffset * THRESHOLD_OFFSET_NORMALIZER; #endif //Lighting could be optimized by combining every component (diffuse, specular, lightTransmission, indirect/direct), may break gooch #ifdef MK_STYLIZE_SYSTEM_SHADOWS LIGHT_STYLE_RAW_2D(light.shadowAttenuation, 1, threshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); ARTISTIC_RAW_SHADOW_MASKED(light.shadowAttenuation, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #endif LIGHT_STYLE_RAW_2D(diffuse, light.distanceAttenuation, threshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); #ifndef MK_STYLIZE_SYSTEM_SHADOWS diffuse *= light.attenuation; #else diffuse *= light.shadowAttenuation; diffuse *= light.distanceAttenuation; #endif #ifdef MK_LEGACY_RP #ifdef MK_HANDLE_SHADOWS_BLENDING_IN_GI diffuse *= saturate(1.0 - light.giBlend); #endif #endif return diffuse; #else return 1; #endif } //URP lights has limitations regarding AA, different sampling is required when AA is enabled inline half MKLightingDiffuseAdditional(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, inout MKLight light, in MKLightData lightData, in half threshold) { //mkLight.distanceAttenuation = lerp(smoothstep(0, _AdditionalLightsFalloff, light.distanceAttenuation), light.distanceAttenuation, _AdditionalLightsFalloff); #ifdef MK_LIT half diffuse; #if defined(MK_DIFFUSE_MINNAERT) diffuse = Minnaert(lightData.NoLRaw, surfaceData.VoN, pbsData.roughnessPow2); #elif defined(MK_DIFFUSE_OREN_NAYAR) diffuse = OrenNayar(lightData.NoLRaw, surfaceData.VoN, lightData.VoL, pbsData.roughnessPow2); #else //MK_SIMPLE diffuse = lightData.NoLRaw; #endif diffuse = lerp(diffuse, HalfWrap(diffuse, 0.5), step(HALF_MIN, _WrappedLighting)); #ifdef MK_THRESHOLD_MAP diffuse -= _DiffuseThresholdOffset * surface.thresholdOffset; diffuse += _DiffuseThresholdOffset * THRESHOLD_OFFSET_NORMALIZER; #endif //Lighting could be optimized by combining every component (diffuse, specular, lightTransmission, indirect/direct), may break gooch #ifdef MK_STYLIZE_SYSTEM_SHADOWS LIGHT_STYLE_RAW_2D_ADDITIONAL(light.shadowAttenuation, 1, threshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); ARTISTIC_RAW_SHADOW_MASKED(light.shadowAttenuation, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #endif LIGHT_STYLE_RAW_2D_ADDITIONAL(diffuse, light.distanceAttenuation, threshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); #ifndef MK_STYLIZE_SYSTEM_SHADOWS diffuse *= light.attenuation; #else diffuse *= light.shadowAttenuation; diffuse *= light.distanceAttenuation; #endif #ifdef MK_LEGACY_RP #ifdef MK_HANDLE_SHADOWS_BLENDING_IN_GI diffuse *= saturate(1.0 - light.giBlend); #endif #endif return diffuse; #else return 1; #endif } inline void MKLightingSFX(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, in MKLight light, in MKLightData lightData, in half4 diffuse, inout half4 finalLightColor) { #ifdef MK_LIT half3 goochRamp; #ifdef MK_GOOCH_RAMP goochRamp = lerp(HALF3_ONE, 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 half diffuseContrib = max(diffuse.r, max(diffuse.g, diffuse.b)); half3 goochDarkRemapped = lerp(half3(0,0,0), surface.goochDark.rgb, Remap3(surface.indirect.rgb, half3(0, 0, 0), MK_GOOCH_REMAP_MAX_INPUT, _GoochDarkRemapMin.xxx, _GoochDarkRemapMax.xxx)); gooch = goochRamp * lerp(lerp(surface.goochDark.rgb, goochDarkRemapped, _GoochDarkRemapFadeWithIndirect), surface.goochBright.rgb, diffuseContrib); //#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.NoLHV, lightData.ToLHV, lightData.BoLHV, pbsData.roughnessPow2, _Anisotropy); //Isotropic Geo term is producing more pleasant results so its used for now geometric = GeometricSmithGGX(surfaceData.VoN, lightData.NoL, pbsData.roughness); sFresnel = FresnelCSch(lightData.LoLHV, pbsData.specularRadiance); #else distribution = DistributionGGX(lightData.NoLHV, pbsData.roughnessPow4); geometric = GeometricSmithGGX(surfaceData.VoN, lightData.NoL, pbsData.roughness); sFresnel = FresnelCSch(lightData.LoLHV, pbsData.specularRadiance); #endif specular.a = SafeDivide(distribution * geometric, 4.0 * surfaceData.VoN * lightData.NoL + HALF_MIN); #else //MK_SIMPLE Iso Only specular.a = BlinnSpecular(lightData.NoLHV, pbsData.smoothness); #endif #ifdef MK_SHADER_API_MOBILE specular.a = clamp(specular.a, 0.0, 100.0); #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 #ifdef MK_LIGHTING_ALPHA #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 #else finalLightColor.a = 1; #endif #endif } inline void LightingDirectAdditional(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, inout MKLight light, in MKLightData lightData, out half4 finalLightColor) { #ifdef MK_LIT half diffuseRaw = MKLightingDiffuseAdditional(surface, surfaceData, pbsData, light, lightData, _AdditionalLightsThreshold); half4 diffuse = half4(0, 0, 0, diffuseRaw); #ifndef MK_STYLIZE_SYSTEM_SHADOWS ARTISTIC_RAW_MASKED(diffuse.a, 0, light.distanceAttenuation); #else ARTISTIC_RAW_SHADOW_MASKED(diffuse.a, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #endif //diffuse.a *= diffuseRaw; TRANSFER_SCALAR_TO_VECTOR(diffuse); MKLightingSFX(surface, surfaceData, pbsData, light, lightData, diffuse, finalLightColor); #endif } inline void LightingDirect(inout Surface surface, in MKSurfaceData surfaceData, in MKPBSData pbsData, in MKLight light, inout MKLightData lightData, out half4 finalLightColor) { #ifdef MK_LIT #ifdef MK_LEGACY_RP #ifdef USING_DIRECTIONAL_LIGHT half diffuseRaw = MKLightingDiffuse(surface, surfaceData, pbsData, light, lightData, _LightThreshold); #else half diffuseRaw = MKLightingDiffuseAdditional(surface, surfaceData, pbsData, light, lightData, _AdditionalLightsThreshold); #endif #else half diffuseRaw = MKLightingDiffuse(surface, surfaceData, pbsData, light, lightData, _LightThreshold); #endif half4 diffuse = half4(0, 0, 0, diffuseRaw); #ifndef MK_STYLIZE_SYSTEM_SHADOWS ARTISTIC_RAW_MASKED(diffuse.a, 0, light.distanceAttenuation); #else ARTISTIC_RAW_SHADOW_MASKED(diffuse.a, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #endif TRANSFER_SCALAR_TO_VECTOR(diffuse); MKLightingSFX(surface, surfaceData, pbsData, light, lightData, diffuse, finalLightColor); #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 #endif } #endif