////////////////////////////////////////////////////// // MK Toon Lighting // // // // Created by Michael Kremmel // // www.michaelkremmel.de // // Copyright © 2020 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" #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" // ------------------------------------------------------------------------------------------ // 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 half3 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 }; #if UNITY_VERSION < 60000000 && defined(MK_URP) || defined(MK_LEGACY_RP) 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 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 } 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; } #endif #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_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, lerp(0, _ArtisticShadowFilter, shadowMask)) * step(HALF_MIN, compare) #elif defined(MK_ARTISTIC_SKETCH) #define ARTISTIC_RAW_SHADOW_MASKED(value, shadowMask, compare) value = Sketch(surface.artistic0, 1, lerp(value, _ArtisticShadowFilter, shadowMask)) * 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(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); light.distanceAttenuation = saturate(saturate((1 - (distanceSqr / lightRangeSqr)) * 5.0) * Rcp(1.0 + 25.0 * distanceSqr / lightRangeSqr)) * AngleAttenuation(spotDirection.xyz, LightDir, distanceAndSpotAttenuation.zw); } #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 = 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 #endif 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, 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 int lightIndex = index; #else int lightIndex = GetPerObjectLightIndex(index); #endif LinearDistanceAttenuation(light, surfaceData, lightIndex); #endif mkLight.distanceAttenuation = light.distanceAttenuation; mkLight.color = light.color; mkLight.radiometricColor = mkLight.color * PI; mkLight.attenuation = light.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 = FastPow2(ToLHV) / FastPow2(anisoScale.x); half ax = FastPow2(BoLHV) / FastPow2(anisoScale.y); return INV_PI * p * Rcp(FastPow2(ay + ax + FastPow2(NoLHV))); } inline half DistributionGGX(half NoLHV, half roughnessP4) { return SafeDivide(roughnessP4, (PI * FastPow2(FastPow2(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 = FastPow2(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 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_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 //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_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, in MKLight light, in MKLightData lightData) { #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 #if defined(MK_WRAPPED_DIFFUSE) diffuse = HalfWrap(diffuse, 0.5); #endif #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, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); diffuse *= light.shadowAttenuation; #endif LIGHT_STYLE_RAW_2D(diffuse, light.distanceAttenuation, _LightThreshold, _DiffuseSmoothness * 0.5, _DiffuseSmoothness * 0.5, _DiffuseRamp, SAMPLER_CLAMPED_MAIN); #ifndef MK_STYLIZE_SYSTEM_SHADOWS diffuse *= light.attenuation; #else 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 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.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, in MKLight light, in MKLightData lightData, out half4 finalLightColor) { #ifdef MK_LIT half diffuseRaw = MKLightingDiffuse(surface, surfaceData, pbsData, light, lightData); half4 diffuse = half4(0, 0, 0, diffuseRaw); #ifdef MK_STYLIZE_SYSTEM_SHADOWS ARTISTIC_RAW_SHADOW_MASKED(diffuse.a, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #else ARTISTIC_RAW_MASKED(diffuse.a, 0, 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, in MKLightData lightData, out half4 finalLightColor) { #ifdef MK_LIT half diffuseRaw = MKLightingDiffuse(surface, surfaceData, pbsData, light, lightData); half4 diffuse = half4(0, 0, 0, diffuseRaw); #ifdef MK_STYLIZE_SYSTEM_SHADOWS ARTISTIC_RAW_SHADOW_MASKED(diffuse.a, saturate(1.0 - light.shadowAttenuation), light.distanceAttenuation); #else ARTISTIC_RAW_MASKED(diffuse.a, 0, 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