// NOTE: Based on URP Lighting.hlsl which replaced some half3 with floats to avoid lighting artifacts on mobile #ifndef UNIVERSAL_TOONLIGHTING_INCLUDED #define UNIVERSAL_TOONLIGHTING_INCLUDED #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/EntityLighting.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/ImageBasedLighting.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" // Toon TEXTURE2D(_GradientMap); float4 _GradientMap_TexelSize; SAMPLER(s_point_clamp_sampler); SAMPLER(s_linear_clamp_sampler); // /////////////////////////////////////////////////////////// // // Custom functions half3 LightingSpecular_Toon (Light light, half lightingRemap, half3 normalWS, half3 viewDirectionWS, half3 specular, half specularSmoothness, half smoothness, half specularStep, half specularUpper, bool energyConservation){ float3 halfVec = SafeNormalize( float3(light.direction) + float3(viewDirectionWS)); half NdotH = saturate(dot(normalWS, halfVec)); half modifier = pow(NdotH /* lightingRemap*/, specularSmoothness); // Normalization? Na, we just multiply by smoothness in the return statement. // #define ONEOVERTWOPI 0.159155h // half normalization = (specularSmoothness + 1) * ONEOVERTWOPI; // Sharpen half modifierSharpened = smoothstep(specularStep, specularUpper, modifier); half toonNormalization = (energyConservation == 1.0h) ? smoothness : 1; return light.color * specular * modifierSharpened * toonNormalization; // * smoothness; } half3 LightingSpecularAniso_Toon (Light light, half NdotL, half3 normalWS, half3 viewDirectionWS, half3 tangentWS, half3 bitangentWS, half anisotropy, half3 specular, half specularSmoothness, half smoothness, half specularStep, half specularUpper, bool energyConservation){ // This does not let us fade from isotropic to anisotropic... // half3 H = SafeNormalize(light.direction + viewDirectionWS); // half3 T = cross(normalWS, tangent); // T = lerp(tangent, bitangent, (anisotropy + 1) * 0.5); // float TdotH = dot(T, H); // float sinTHSq = saturate(1.0 - TdotH * TdotH); // float exponent = RoughnessToBlinnPhongSpecularExponent_Lux(1 - smoothness); // float modifier = dirAttn * pow(sinTHSq, 0.5 * exponent); // float norm = smoothness; //(exponent + 2) * rcp(2 * PI); // // Sharpen // half modifierSharpened = smoothstep(specularStep, specularUpper, modifier); // half toonNormalization = (energyConservation == 1.0h) ? norm : 1; // return light.color * specular * modifierSharpened * toonNormalization; // /////////////////////////////// // // GGX "like" distribution in order to be able to fade from isotropic to anisotropic // We skip visbility here as it is toon lighting. // NOTE: Further normalization does not help here to fixe the final shape... float3 H = SafeNormalize(float3(light.direction) + float3(viewDirectionWS)); // TdotH and BdotH should be unclamped here float TdotH = dot(tangentWS, H); float BdotH = dot(bitangentWS, H); float NdotH = dot(normalWS, H); float roughness = 1.0f - smoothness; // roughness^2 would be correct here - but in order to get it a bit closer to our blinn phong isotropic specular we go with ^4 instead roughness *= roughness * roughness * roughness; float at = roughness * (1.0f + anisotropy); float ab = roughness * (1.0f - anisotropy); float a2 = at * ab; float3 v = float3(ab * TdotH, at * BdotH, a2 * NdotH); float v2 = dot(v, v); float w2 = a2 / v2; half res = half(a2 * w2 * w2 * (1.0f / PI)); // Sharpen half modifierSharpened = smoothstep(specularStep, specularUpper, res); half toonNormalization = (energyConservation == 1.0h) ? smoothness : 1.0h; return light.color * specular * modifierSharpened * toonNormalization; } // https://www.ronja-tutorials.com/2019/11/29/fwidth.html half aaStep(half compValue, half gradient, half softness){ half change = fwidth(gradient) * softness; // Base the range of the inverse lerp on the change over two pixels half lowerEdge = compValue - change; half upperEdge = compValue + change; // Do the inverse interpolation half stepped = (gradient - lowerEdge) / (upperEdge - lowerEdge); stepped = saturate(stepped); return stepped; } half4 LuxURPToonFragmentPBR(InputData inputData, #if defined(_ANISOTROPIC) && !defined(_SPECULARHIGHLIGHTS_OFF) half3 tangentWS, half anisotropy, #endif half3 albedo, half3 shadedAlbedo, half metallic, half3 specular, half steps, half diffuseStep, half diffuseFalloff, half energyConservation, half specularStep, half specularFalloff, half mainLightAttenContribution, half addLightAttenContribution, half lightColorContribution, half addLightFalloff, half shadowFalloff, half shadowBiasDirectional, half shadowBiasAdditional, half3 toonRimColor, half toonRimPower, half toonRimFallOff, half toonRimAttenuation, half smoothness, half occlusion, half3 emission, half alpha ) { BRDFData brdfData; // We can't use our specular here as it can be anything. So we simply use the default dielectric value here. InitializeBRDFData(albedo, metallic, kDieletricSpec.rgb, smoothness, alpha, brdfData); // ShadowMask: To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders #if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON) half4 shadowMask = inputData.shadowMask; #elif !defined (LIGHTMAP_ON) half4 shadowMask = unity_ProbesOcclusion; #else half4 shadowMask = half4(1, 1, 1, 1); #endif //Light mainLight = GetMainLight(inputData.shadowCoord); Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, shadowMask); // SSAO #if defined(_SCREEN_SPACE_OCCLUSION) #if defined(_SSAO_ENABLED) AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV); mainLight.color *= aoFactor.directAmbientOcclusion; occlusion = min(occlusion, aoFactor.indirectAmbientOcclusion); #endif #endif mainLight.shadowAttenuation = smoothstep( (1 - shadowFalloff) * shadowFalloff, shadowFalloff, mainLight.shadowAttenuation); MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0)); // We really do not want any reflections #if defined(_ENVIRONMENTREFLECTIONS_OFF) _GlossyEnvironmentColor.rgb = half3(0,0,0); #endif // Global Illumination half3 GI = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS); // Set up Lighting half lightIntensity = 0.0h; half3 specularLighting = 0.0h; half3 rimLighting = 0.0h; half3 lightColor = 0.0h; half luminance; // Adjust tangent and reconstruct bitangent in case anisotropic specular is active as otherwise normal mapping has no effect #if defined(_ANISOTROPIC) && !defined(_SPECULARHIGHLIGHTS_OFF) #if defined(_NORMALMAP) tangentWS = Orthonormalize(tangentWS, inputData.normalWS); #endif half3 bitangentWS = cross(inputData.normalWS, tangentWS); #endif // Main Light // Remap old diffuseStep and diffuseFalloff in order to match new function diffuseStep = diffuseStep + 1.0h; diffuseFalloff = diffuseFalloff * 4.0h + 1.0h; half NdotL = dot(inputData.normalWS, mainLight.direction); NdotL = saturate((NdotL + 1.0h) - diffuseStep); #if !defined(_RAMP_SMOOTHSAMPLING) && !defined(_RAMP_POINTSAMPLING) // We have to use steps - 1 here! half oneOverSteps = 1.0h / steps; half quantizedNdotL = floor(NdotL * steps); // IMPORTANT: no saturate on the 2nd param: NdotL - 0.01. 0.01 is eyballed. NdotL = (quantizedNdotL + aaStep(saturate(quantizedNdotL * oneOverSteps), NdotL - 0.01h, diffuseFalloff )) * oneOverSteps; #else #if defined(_RAMP_SMOOTHSAMPLING) NdotL = SAMPLE_TEXTURE2D(_GradientMap, s_linear_clamp_sampler, float2 (NdotL, 0.5f)).r; #else half NdotL0 = SAMPLE_TEXTURE2D(_GradientMap, s_point_clamp_sampler, float2 (NdotL, 0.5f)).r; half NdotL1 = SAMPLE_TEXTURE2D(_GradientMap, s_point_clamp_sampler, float2 (NdotL + fwidth(NdotL) * _GradientMap_TexelSize.x, 0.5f)).r; NdotL = (NdotL0 + NdotL1) * 0.5h; #endif #endif half atten = NdotL * mainLight.distanceAttenuation * saturate(shadowBiasDirectional + mainLight.shadowAttenuation); mainLight.color = lerp(Luminance(mainLight.color).xxx, mainLight.color, lightColorContribution.xxx); // #if defined(_COLORIZEMAIN) // lightColor = mainLight.color * mainLight.distanceAttenuation; // #else // lightColor = mainLight.color * atten; // #endif lightColor = mainLight.color * lerp(atten, mainLight.distanceAttenuation, mainLightAttenContribution); luminance = Luminance(mainLight.color); lightIntensity += luminance * atten; // Specular #if !defined(_SPECULARHIGHLIGHTS_OFF) half specularSmoothness; half3 spec; half specularUpper; specularSmoothness = exp2(10 * smoothness + 1); specularUpper = saturate(specularStep + specularFalloff * (1.0h + smoothness)); #if defined(_ANISOTROPIC) spec = LightingSpecularAniso_Toon (mainLight, NdotL, inputData.normalWS, inputData.viewDirectionWS, tangentWS, bitangentWS, anisotropy, specular, specularSmoothness, smoothness, specularStep, specularUpper, energyConservation); #else spec = LightingSpecular_Toon(mainLight, NdotL, inputData.normalWS, inputData.viewDirectionWS, specular, specularSmoothness, smoothness, specularStep, specularUpper, energyConservation); #endif specularLighting = spec * atten; #endif // Rim #if defined(_TOONRIM) half rim = saturate(1.0h - saturate( dot(inputData.normalWS, inputData.viewDirectionWS)) ); //rimLighting = smoothstep(rimPower, rimPower + rimFalloff, rim) * rimColor.rgb; // Stabilize rim float delta = fwidth(rim); rimLighting = smoothstep(toonRimPower - delta, toonRimPower + toonRimFallOff + delta, rim) * toonRimColor.rgb; #endif // Additional lights #ifdef _ADDITIONAL_LIGHTS uint pixelLightCount = GetAdditionalLightsCount(); for (uint i = 0u; i < pixelLightCount; ++i) { Light light = GetAdditionalLight(i, inputData.positionWS); #if defined(_SCREEN_SPACE_OCCLUSION) #if defined(_SSAO_ENABLED) light.color *= aoFactor.directAmbientOcclusion; #endif #endif //light.shadowAttenuation = smoothstep(0.0h, shadowFalloff, light.shadowAttenuation); light.shadowAttenuation = smoothstep( (1.0h - shadowFalloff) * shadowFalloff, shadowFalloff, light.shadowAttenuation); NdotL = dot(inputData.normalWS, light.direction); NdotL = saturate((NdotL + 1.0h) - diffuseStep); #if !defined(_RAMP_SMOOTHSAMPLING) && !defined(_RAMP_POINTSAMPLING) half quantizedNdotL = floor(NdotL * steps); // IMPORTANT: no saturate on the 2nd param: NdotL - 0.01. 0.01 is eyballed. NdotL = (quantizedNdotL + aaStep(saturate(quantizedNdotL * oneOverSteps), NdotL - 0.01h, diffuseFalloff )) * oneOverSteps; #else #if defined(_RAMP_SMOOTHSAMPLING) NdotL = SAMPLE_TEXTURE2D(_GradientMap, s_linear_clamp_sampler, float2 (NdotL, 0.5f)).r; #else half NdotL0 = SAMPLE_TEXTURE2D(_GradientMap, s_point_clamp_sampler, float2 (NdotL, 0.5f)).r; half NdotL1 = SAMPLE_TEXTURE2D(_GradientMap, s_point_clamp_sampler, float2 (NdotL + fwidth(NdotL) * _GradientMap_TexelSize.x, 0.5f)).r; NdotL = (NdotL0 + NdotL1) * 0.5h; #endif #endif // No smoothstep here! as is totally fucks up the distanceAttenuation: It is not linear! //half distanceAttenuation = smoothstep(0, addLightFalloff, light.distanceAttenuation); half distanceAttenuation = (addLightFalloff < 1.0h) ? saturate(light.distanceAttenuation / addLightFalloff) : light.distanceAttenuation; atten = NdotL * distanceAttenuation * saturate(shadowBiasAdditional + light.shadowAttenuation); light.color = lerp(Luminance(light.color).xxx, light.color, lightColorContribution.xxx); // #if defined(_COLORIZEADD) // lightColor += light.color * light.distanceAttenuation; // #else // lightColor += light.color * atten; // #endif lightColor += light.color * lerp(atten, distanceAttenuation, addLightAttenContribution); luminance = Luminance(light.color); lightIntensity += luminance * atten; #if !defined(_SPECULARHIGHLIGHTS_OFF) #if defined(_ANISOTROPIC) spec = LightingSpecularAniso_Toon (light, NdotL, inputData.normalWS, inputData.viewDirectionWS, tangentWS, bitangentWS, anisotropy, specular, specularSmoothness, smoothness, specularStep, specularUpper, energyConservation); #else spec = LightingSpecular_Toon(light, NdotL, inputData.normalWS, inputData.viewDirectionWS, specular, specularSmoothness, smoothness, specularStep, specularUpper, energyConservation); #endif specularLighting += spec * atten; #endif } #endif // Combine Lighting half3 litAlbedo = lerp(shadedAlbedo, albedo, saturate(lightIntensity.xxx) ); //lightColor = 1; half3 Lighting = // ambient diffuse lighting GI //inputData.bakedGI * albedo //litAlbedo // direct diffuse lighting + litAlbedo * lightColor // spec lighting #if !defined(_SPECULARHIGHLIGHTS_OFF) + (specularLighting * lightIntensity * lightColor) #endif // rim lighting #if defined(_TOONRIM) + rimLighting * lerp(1.0h, lightIntensity, toonRimAttenuation) #endif // old version multiplied rim with lightColor ... //#if !defined(_SPECULARHIGHLIGHTS_OFF) // ) //#endif // * lightColor ; #ifdef _ADDITIONAL_LIGHTS_VERTEX Lighting += inputData.vertexLighting * albedo; #endif Lighting += emission; return half4(Lighting, alpha); } #endif