//#define NOURP //#define SURFACE #if defined(SURFACE) && defined(SHADER_TARGET_SURFACE_ANALYSIS) #define SURFACEANALYSIS #endif // Declare URP stuff if not in URP #ifdef NOURP #define LIGHTMAP_RGBM_MAX_GAMMA 5.0f // NB: Must match value in RGBMRanges.h #define LIGHTMAP_RGBM_MAX_LINEAR 34.493242f // LIGHTMAP_RGBM_MAX_GAMMA ^ 2.2 #ifdef UNITY_LIGHTMAP_RGBM_ENCODING #ifdef UNITY_COLORSPACE_GAMMA #define LIGHTMAP_HDR_MULTIPLIER LIGHTMAP_RGBM_MAX_GAMMA #define LIGHTMAP_HDR_EXPONENT 1.0f // Not used in gamma color space #else #define LIGHTMAP_HDR_MULTIPLIER LIGHTMAP_RGBM_MAX_LINEAR #define LIGHTMAP_HDR_EXPONENT 2.2f #endif #elif defined(UNITY_LIGHTMAP_DLDR_ENCODING) #ifdef UNITY_COLORSPACE_GAMMA #define LIGHTMAP_HDR_MULTIPLIER 2.0f #else #define LIGHTMAP_HDR_MULTIPLIER 4.59f // 2.0 ^ 2.2 #endif #define LIGHTMAP_HDR_EXPONENT 0.0f #else // (UNITY_LIGHTMAP_FULL_HDR) #define LIGHTMAP_HDR_MULTIPLIER 1.0f #define LIGHTMAP_HDR_EXPONENT 1.0f #endif #endif #define BAKERY_INV_PI 0.31830988618f #ifdef SURFACE sampler2D _RNM0, _RNM1, _RNM2; float4 SAMPLERNM(sampler2D t, float2 uv) { return tex2D(t, uv); } #else Texture2D _RNM0, _RNM1, _RNM2; SamplerState sampler_RNM1; float4 SAMPLERNM(Texture2D t, float2 uv) { return t.Sample(sampler_RNM1, uv); } #endif #ifndef SURFACEANALYSIS Texture3D _Volume0, _Volume1, _Volume2, _VolumeMask; #ifdef BAKERY_COMPRESSED_VOLUME Texture3D _Volume3; #endif SamplerState sampler_Volume0; SamplerState sampler_VolumeMask; #endif float4x4 _VolumeMatrix, _GlobalVolumeMatrix; float3 _VolumeMin, _VolumeInvSize; float3 _GlobalVolumeMin, _GlobalVolumeInvSize; //#ifdef BAKERY_VOLROTATIONY float2 _GlobalVolumeRY, _VolumeRY; //#endif #if defined(BAKERY_MODE_NONE) || defined(BAKERY_MODE_RNM) || defined(BAKERY_MODE_SH) || defined(BAKERY_MODE_MONOSH) || defined(BAKERY_MODE_VERTEXBAKED) || defined(BAKERY_MODE_VOLUME) || defined(BAKERY_MODE_NONLINEARLIGHTPROBE) #define BAKERY_NOSPECULARWEIGHTING #endif void LightmapUV_float(float2 uv, out float2 lightmapUV) { lightmapUV = uv * unity_LightmapST.xy + unity_LightmapST.zw; } #ifdef NOURP float3 DecodeHDREnvironment(float4 encodedIrradiance, float4 decodeInstructions) { // Take into account texture alpha if decodeInstructions.w is true(the alpha value affects the RGB channels) float alpha = max(decodeInstructions.w * (encodedIrradiance.a - 1.0) + 1.0, 0.0); // If Linear mode is not supported we can skip exponent part return (decodeInstructions.x * pow(abs(alpha), decodeInstructions.y)) * encodedIrradiance.rgb; } #endif void DecodeLightmap2(float4 lightmap, out float3 result) { #ifdef UNITY_LIGHTMAP_FULL_HDR float4 decodeInstructions = float4(0.0, 0.0, 0.0, 0.0); // Never used but needed for the interface since it supports gamma lightmaps #else #if defined(UNITY_LIGHTMAP_RGBM_ENCODING) float4 decodeInstructions = float4(34.493242, 2.2, 0.0, 0.0); // range^2.2 = 5^2.2, gamma = 2.2 #else float4 decodeInstructions = float4(2.0, 2.2, 0.0, 0.0); // range = 2.0^2.2 = 4.59 #endif #endif #ifdef NOURP result = DecodeLightmap(lightmap); #else result = DecodeLightmap(lightmap, decodeInstructions); #endif } void SampleRNM0_float(float2 lightmapUV, out float3 result) { DecodeLightmap2(SAMPLERNM(_RNM0, lightmapUV), result); } void SampleRNM1_float(float2 lightmapUV, out float3 result) { DecodeLightmap2(SAMPLERNM(_RNM1, lightmapUV), result); } void SampleRNM2_float(float2 lightmapUV, out float3 result) { DecodeLightmap2(SAMPLERNM(_RNM2, lightmapUV), result); } void SampleL1x_float(float2 lightmapUV, out float3 result) { result = SAMPLERNM(_RNM0, lightmapUV); } void SampleL1y_float(float2 lightmapUV, out float3 result) { result = SAMPLERNM(_RNM1, lightmapUV); } void SampleL1z_float(float2 lightmapUV, out float3 result) { result = SAMPLERNM(_RNM2, lightmapUV); } // Following two functions are copied from the original Unity standard shader for compatibility // ----- #ifndef SURFACE float SmoothnessToPerceptualRoughness(float smoothness) { return (1 - smoothness); } #endif float BakeryPerceptualRoughnessToRoughness(float perceptualRoughness) { return perceptualRoughness * perceptualRoughness; } #ifndef SURFACE float GGXTerm (half NdotH, half roughness) { half a2 = roughness * roughness; half d = (NdotH * a2 - NdotH) * NdotH + 1.0f; // 2 mad return BAKERY_INV_PI * a2 / (d * d + 1e-7f); // This function is not intended to be running on Mobile, // therefore epsilon is smaller than what can be represented by half } #endif #ifndef NOURP inline half3 DecodeDirectionalLightmap (half3 color, half4 dirTex, half3 normalWorld) { // In directional (non-specular) mode Enlighten bakes dominant light direction // in a way, that using it for half Lambert and then dividing by a "rebalancing coefficient" // gives a result close to plain diffuse response lightmaps, but normalmapped. // Note that dir is not unit length on purpose. Its length is "directionality", like // for the directional specular lightmaps. half halfLambert = dot(normalWorld, dirTex.xyz - 0.5) + 0.5; return color * halfLambert / max(1e-4h, dirTex.w); } #endif #define UNITY_SPECCUBE_LOD_STEPS 6 float BakeryPerceptualRoughnessToMipmapLevel(float perceptualRoughness, uint mipMapCount) { perceptualRoughness = perceptualRoughness * (1.7 - 0.7 * perceptualRoughness); return perceptualRoughness * mipMapCount; } float BakeryPerceptualRoughnessToMipmapLevel(float perceptualRoughness) { return BakeryPerceptualRoughnessToMipmapLevel(perceptualRoughness, UNITY_SPECCUBE_LOD_STEPS); } #define unity_ColorSpaceDielectricSpec half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%) // ----- void DirectionalSpecular_float(float2 lightmapUV, float3 normalWorld, float3 viewDir, float smoothness, out float3 color) { #ifdef LIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED #ifdef NOURP float3 lmColor = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV)); #else float3 lmColor = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV), half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h)); #endif float3 lmDir = unity_LightmapInd.Sample(samplerunity_Lightmap, lightmapUV) * 2 - 1; float3 halfDir = normalize(normalize(lmDir) + viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); float spec = GGXTerm(nh, roughness); color = lmColor * spec * 0.99999; return; #endif #endif color = 0; } void DirectionalDiffuse_float(float2 lightmapUV, float3 normalWorld, out float3 color) { #ifdef LIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED #ifdef NOURP float3 lmColor = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV)); #else float3 lmColor = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV), half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h)); #endif float4 lmDir = unity_LightmapInd.Sample(samplerunity_Lightmap, lightmapUV); color = DecodeDirectionalLightmap(lmColor, lmDir, normalWorld); #endif #endif color = 0; } void Specular_float(float3 lightDir, float3 normalWorld, float3 viewDir, float smoothness, out float specular) { float3 halfDir = normalize(lightDir + viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); specular = GGXTerm(nh, roughness); } float shEvaluateDiffuseL1Geomerics(float L0, float3 L1, float3 n) { // average energy float R0 = L0; // avg direction of incoming light float3 R1 = 0.5f * L1; // directional brightness float lenR1 = length(R1); // linear angle between normal and direction 0-1 //float q = 0.5f * (1.0f + dot(R1 / lenR1, n)); //float q = dot(R1 / lenR1, n) * 0.5 + 0.5; float q = dot(normalize(R1), n) * 0.5 + 0.5; // power for q // lerps from 1 (linear) to 3 (cubic) based on directionality float p = 1.0f + 2.0f * lenR1 / R0; // dynamic range constant // should vary between 4 (highly directional) and 0 (ambient) float a = (1.0f - lenR1 / R0) / (1.0f + lenR1 / R0); return R0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p)); } void NonLinearLightProbe_float(float3 normalWorld, out float3 color) { float3 L0 = float3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w); color.r = shEvaluateDiffuseL1Geomerics(L0.r, unity_SHAr.xyz, normalWorld); color.g = shEvaluateDiffuseL1Geomerics(L0.g, unity_SHAg.xyz, normalWorld); color.b = shEvaluateDiffuseL1Geomerics(L0.b, unity_SHAb.xyz, normalWorld); } void BakerySH_float(float3 L0, float3 normalWorld, float2 lightmapUV, out float3 sh) { #ifdef LIGHTMAP_ON float3 nL1x = SAMPLERNM(_RNM0, lightmapUV) * 2 - 1; float3 nL1y = SAMPLERNM(_RNM1, lightmapUV) * 2 - 1; float3 nL1z = SAMPLERNM(_RNM2, lightmapUV) * 2 - 1; float3 L1x = nL1x * L0 * 2; float3 L1y = nL1y * L0 * 2; float3 L1z = nL1z * L0 * 2; float lumaL0 = dot(L0, 1); float lumaL1x = dot(L1x, 1); float lumaL1y = dot(L1y, 1); float lumaL1z = dot(L1z, 1); float lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, float3(lumaL1x, lumaL1y, lumaL1z), normalWorld); sh = L0 + normalWorld.x * L1x + normalWorld.y * L1y + normalWorld.z * L1z; float regularLumaSH = dot(sh, 1); sh *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16)); sh = max(sh, 0); return; #endif NonLinearLightProbe_float(normalWorld, sh); } void BakeryMonoSH_float(float3 normalWorld, float2 lightmapUV, out float3 sh) { #ifdef LIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED #ifdef NOURP float3 L0 = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV)); #else float3 L0 = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV), half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h)); #endif float3 dominantDir = unity_LightmapInd.Sample(samplerunity_Lightmap, lightmapUV); float3 nL1 = dominantDir * 2 - 1; float3 L1x = nL1.x * L0 * 2; float3 L1y = nL1.y * L0 * 2; float3 L1z = nL1.z * L0 * 2; float lumaL0 = dot(L0, 1); float lumaL1x = dot(L1x, 1); float lumaL1y = dot(L1y, 1); float lumaL1z = dot(L1z, 1); float lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, float3(lumaL1x, lumaL1y, lumaL1z), normalWorld); sh = L0 + normalWorld.x * L1x + normalWorld.y * L1y + normalWorld.z * L1z; float regularLumaSH = dot(sh, 1); sh *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16)); sh = max(sh, 0); return; #endif #endif //sh = 0; NonLinearLightProbe_float(normalWorld, sh); } void BakerySpecSHFull_float(float3 L0, float3 normalWorld, float2 lightmapUV, float3 viewDir, float smoothness, float3 albedo, float metalness, out float3 diffuseSH, out float3 specularSH) { #ifdef LIGHTMAP_ON float3 nL1x = SAMPLERNM(_RNM0, lightmapUV) * 2 - 1; float3 nL1y = SAMPLERNM(_RNM1, lightmapUV) * 2 - 1; float3 nL1z = SAMPLERNM(_RNM2, lightmapUV) * 2 - 1; float3 L1x = nL1x * L0 * 2; float3 L1y = nL1y * L0 * 2; float3 L1z = nL1z * L0 * 2; float lumaL0 = dot(L0, 1); float lumaL1x = dot(L1x, 1); float lumaL1y = dot(L1y, 1); float lumaL1z = dot(L1z, 1); float lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, float3(lumaL1x, lumaL1y, lumaL1z), normalWorld); diffuseSH = L0 + normalWorld.x * L1x + normalWorld.y * L1y + normalWorld.z * L1z; float regularLumaSH = dot(diffuseSH, 1); diffuseSH *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16)); diffuseSH = max(diffuseSH, 0.0); const float3 lumaConv = float3(0.2125f, 0.7154f, 0.0721f); float3 dominantDir = float3(dot(nL1x, lumaConv), dot(nL1y, lumaConv), dot(nL1z, lumaConv)); float focus = saturate(length(dominantDir)); float3 halfDir = normalize(normalize(dominantDir) - -viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); float spec = GGXTerm(nh, roughness); specularSH = L0 + dominantDir.x * L1x + dominantDir.y * L1y + dominantDir.z * L1z; specularSH = max(spec * specularSH, 0.0); #ifndef BAKERY_NOSPECULARWEIGHTING // Convert metalness to specular and "oneMinusReflectivity" float3 specularColor = lerp(float3(0.04, 0.04, 0.04), albedo, metalness); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metalness * oneMinusDielectricSpec; // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(normalWorld, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColor.r, specularColor.g), specularColor.b); // hack, but consistent with Unity code float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColor, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuseSH *= oneMinusReflectivity; // no baked GI override: modify diffuse specularSH *= fresnel3; diffuseSH = max(diffuseSH, 0); specularSH = max(specularSH, 0); #endif return; #endif specularSH = 0; NonLinearLightProbe_float(normalWorld, diffuseSH); } void BakerySpecMonoSHFull_float(float3 normalWorld, float2 lightmapUV, float3 viewDir, float smoothness, float3 albedo, float metalness, out float3 diffuseSH, out float3 specularSH) { #ifdef LIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED #ifdef NOURP float3 L0 = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV)); #else float3 L0 = DecodeLightmap(unity_Lightmap.Sample(samplerunity_Lightmap, lightmapUV), half4(LIGHTMAP_HDR_MULTIPLIER, LIGHTMAP_HDR_EXPONENT, 0.0h, 0.0h)); #endif float3 dominantDir = unity_LightmapInd.Sample(samplerunity_Lightmap, lightmapUV); float3 nL1 = dominantDir * 2 - 1; float3 L1x = nL1.x * L0 * 2; float3 L1y = nL1.y * L0 * 2; float3 L1z = nL1.z * L0 * 2; float lumaL0 = dot(L0, 1); float lumaL1x = dot(L1x, 1); float lumaL1y = dot(L1y, 1); float lumaL1z = dot(L1z, 1); float lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, float3(lumaL1x, lumaL1y, lumaL1z), normalWorld); diffuseSH = L0 + normalWorld.x * L1x + normalWorld.y * L1y + normalWorld.z * L1z; float regularLumaSH = dot(diffuseSH, 1); diffuseSH *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16)); diffuseSH = max(diffuseSH, 0.0); const float3 lumaConv = float3(0.2125f, 0.7154f, 0.0721f); dominantDir = nL1; float focus = saturate(length(dominantDir)); float3 halfDir = normalize(normalize(dominantDir) - -viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); float spec = GGXTerm(nh, roughness); specularSH = L0 + dominantDir.x * L1x + dominantDir.y * L1y + dominantDir.z * L1z; specularSH = max(spec * specularSH, 0.0); #ifndef BAKERY_NOSPECULARWEIGHTING // Convert metalness to specular and "oneMinusReflectivity" float3 specularColor = lerp(float3(0.04, 0.04, 0.04), albedo, metalness); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metalness * oneMinusDielectricSpec; // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(normalWorld, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColor.r, specularColor.g), specularColor.b); // hack, but consistent with Unity code float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColor, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuseSH *= oneMinusReflectivity; // no baked GI override: modify diffuse specularSH *= fresnel3; diffuseSH = max(diffuseSH, 0); specularSH = max(specularSH, 0); #endif return; #endif #endif diffuseSH = 0; specularSH = 0; NonLinearLightProbe_float(normalWorld, diffuseSH); } void BakerySpecMonoSHFullVertex_float(float3 normalWorld, float3 L0, float3 dominantDir, float3 viewDir, float smoothness, float3 albedo, float metalness, out float3 diffuseSH, out float3 specularSH) { float3 nL1 = dominantDir; float3 L1x = nL1.x * L0 * 2; float3 L1y = nL1.y * L0 * 2; float3 L1z = nL1.z * L0 * 2; float lumaL0 = dot(L0, 1); float lumaL1x = dot(L1x, 1); float lumaL1y = dot(L1y, 1); float lumaL1z = dot(L1z, 1); float lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, float3(lumaL1x, lumaL1y, lumaL1z), normalWorld); diffuseSH = L0 + normalWorld.x * L1x + normalWorld.y * L1y + normalWorld.z * L1z; float regularLumaSH = dot(diffuseSH, 1); diffuseSH *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16)); diffuseSH = max(diffuseSH, 0.0); const float3 lumaConv = float3(0.2125f, 0.7154f, 0.0721f); dominantDir = nL1; float focus = saturate(length(dominantDir)); float3 halfDir = normalize(normalize(dominantDir) - -viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); float spec = GGXTerm(nh, roughness); specularSH = L0 + dominantDir.x * L1x + dominantDir.y * L1y + dominantDir.z * L1z; specularSH = max(spec * specularSH, 0.0); #ifndef BAKERY_NOSPECULARWEIGHTING // Convert metalness to specular and "oneMinusReflectivity" float3 specularColor = lerp(float3(0.04, 0.04, 0.04), albedo, metalness); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metalness * oneMinusDielectricSpec; // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(normalWorld, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColor.r, specularColor.g), specularColor.b); // hack, but consistent with Unity code float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColor, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuseSH *= oneMinusReflectivity; // no baked GI override: modify diffuse specularSH *= fresnel3; diffuseSH = max(diffuseSH, 0); specularSH = max(specularSH, 0); #endif } void BakeryVolume(float3 lpUV, float3 normalWorld, out float3 sh) { #ifdef SURFACEANALYSIS sh = 0; #else #ifdef BAKERY_COMPRESSED_VOLUME float4 tex0, tex1, tex2, tex3; float3 L0, L1x, L1y, L1z; tex0 = _Volume0.Sample(sampler_Volume0, lpUV); tex1 = _Volume1.Sample(sampler_Volume0, lpUV) * 2 - 1; tex2 = _Volume2.Sample(sampler_Volume0, lpUV) * 2 - 1; tex3 = _Volume3.Sample(sampler_Volume0, lpUV) * 2 - 1; L0 = tex0.xyz; L1x = tex1.xyz * L0 * 2; L1y = tex2.xyz * L0 * 2; L1z = tex3.xyz * L0 * 2; #else float4 tex0, tex1, tex2; float3 L0, L1x, L1y, L1z; tex0 = _Volume0.Sample(sampler_Volume0, lpUV); tex1 = _Volume1.Sample(sampler_Volume0, lpUV); tex2 = _Volume2.Sample(sampler_Volume0, lpUV); L0 = tex0.xyz; L1x = tex1.xyz; L1y = tex2.xyz; L1z = float3(tex0.w, tex1.w, tex2.w); #endif sh.r = shEvaluateDiffuseL1Geomerics(L0.r, float3(L1x.r, L1y.r, L1z.r), normalWorld); sh.g = shEvaluateDiffuseL1Geomerics(L0.g, float3(L1x.g, L1y.g, L1z.g), normalWorld); sh.b = shEvaluateDiffuseL1Geomerics(L0.b, float3(L1x.b, L1y.b, L1z.b), normalWorld); sh = max(sh, 0); #endif } float3 VolumeCoords(float3 posWorld, float supportBakedVolumeRotation) { bool isGlobal = dot(abs(_VolumeInvSize),1) == 0; float3 lpUV = posWorld - (isGlobal ? _GlobalVolumeMin : _VolumeMin); //#ifdef BAKERY_VOLROTATIONY if (supportBakedVolumeRotation > 0) { float2 sc = (isGlobal ? _GlobalVolumeRY : _VolumeRY); lpUV.xz = mul(float2x2(sc.y, -sc.x, sc.x, sc.y), lpUV.xz); } //#endif lpUV *= (isGlobal ? _GlobalVolumeInvSize : _VolumeInvSize); return lpUV; } void BakeryVolume_float(float3 posWorld, float3 normalWorld, float supportBakedVolumeRotation, out float3 sh) { float3 lpUV = VolumeCoords(posWorld, supportBakedVolumeRotation); BakeryVolume(lpUV, normalWorld, sh); } void BakeryVolumeRotatable_float(float3 posWorld, float3 normalWorld, out float3 sh) { bool isGlobal = dot(abs(_VolumeInvSize),1) == 0; float4x4 volMatrix = (isGlobal ? _GlobalVolumeMatrix : _VolumeMatrix); float3 volInvSize = (isGlobal ? _GlobalVolumeInvSize : _VolumeInvSize); float3 lpUV = mul(volMatrix, float4(posWorld,1)).xyz * volInvSize + 0.5f; normalWorld = mul((float3x3)volMatrix, normalWorld); BakeryVolume(lpUV, normalWorld, sh); } void BakeryVolumeSpec_float(float3 posWorld, float3 normalWorld, float3 viewDir, float smoothness, float3 albedo, float metalness, float supportBakedVolumeRotation, out float3 diffuseSH, out float3 specularSH) { #ifdef SURFACEANALYSIS diffuseSH = specularSH = 0; #else float3 lpUV = VolumeCoords(posWorld, supportBakedVolumeRotation); #ifdef BAKERY_COMPRESSED_VOLUME float4 tex0, tex1, tex2, tex3; float3 L0, L1x, L1y, L1z; tex0 = _Volume0.Sample(sampler_Volume0, lpUV); tex1 = _Volume1.Sample(sampler_Volume0, lpUV) * 2 - 1; tex2 = _Volume2.Sample(sampler_Volume0, lpUV) * 2 - 1; tex3 = _Volume3.Sample(sampler_Volume0, lpUV) * 2 - 1; L0 = tex0.xyz; L1x = tex1.xyz * L0 * 2; L1y = tex2.xyz * L0 * 2; L1z = tex3.xyz * L0 * 2; #else float4 tex0, tex1, tex2; float3 L0, L1x, L1y, L1z; tex0 = _Volume0.Sample(sampler_Volume0, lpUV); tex1 = _Volume1.Sample(sampler_Volume0, lpUV); tex2 = _Volume2.Sample(sampler_Volume0, lpUV); L0 = tex0.xyz; L1x = tex1.xyz; L1y = tex2.xyz; L1z = float3(tex0.w, tex1.w, tex2.w); #endif diffuseSH.r = shEvaluateDiffuseL1Geomerics(L0.r, float3(L1x.r, L1y.r, L1z.r), normalWorld); diffuseSH.g = shEvaluateDiffuseL1Geomerics(L0.g, float3(L1x.g, L1y.g, L1z.g), normalWorld); diffuseSH.b = shEvaluateDiffuseL1Geomerics(L0.b, float3(L1x.b, L1y.b, L1z.b), normalWorld); diffuseSH = max(diffuseSH, 0); const float3 lumaConv = float3(0.2125f, 0.7154f, 0.0721f); float3 nL1x = L1x / L0; float3 nL1y = L1y / L0; float3 nL1z = L1z / L0; float3 dominantDir = float3(dot(nL1x, lumaConv), dot(nL1y, lumaConv), dot(nL1z, lumaConv)); float3 halfDir = normalize(normalize(dominantDir) - -viewDir); float nh = saturate(dot(normalWorld, halfDir)); float perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); float roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); float spec = GGXTerm(nh, roughness); specularSH = L0 + dominantDir.x * L1x + dominantDir.y * L1y + dominantDir.z * L1z; specularSH = max(spec * specularSH, 0.0); #ifndef BAKERY_NOSPECULARWEIGHTING // Convert metalness to specular and "oneMinusReflectivity" float3 specularColor = lerp(float3(0.04, 0.04, 0.04), albedo, metalness); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metalness * oneMinusDielectricSpec; // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(normalWorld, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColor.r, specularColor.g), specularColor.b); // hack, but consistent with Unity code float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColor, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuseSH *= oneMinusReflectivity; // no baked GI override: modify diffuse specularSH *= fresnel3; diffuseSH = max(diffuseSH, 0); specularSH = max(specularSH, 0); #endif #endif } void URPMainLightDiffuse_float(float3 normalWorld, float3 albedo, out float3 diffuseLight) { #ifndef UNIVERSAL_LIGHTING_INCLUDED float3 direction = float3(0.5, 0.5, 1); float3 color = 1; #else Light mainLight = GetMainLight(); float3 direction = mainLight.direction; float3 color = mainLight.color; #endif diffuseLight = saturate(dot(normalWorld, direction)) * albedo * color; } void VolumeShadowmaskA_float(float3 posWorld, float supportBakedVolumeRotation, out float shadowmask) { #ifdef SURFACEANALYSIS shadowmask = 0; #else float3 lpUV = VolumeCoords(posWorld, supportBakedVolumeRotation); shadowmask = _VolumeMask.Sample(sampler_VolumeMask, lpUV).a; #endif } void SmoothnessToMip_float(float smoothness, out float mip) { float pr = SmoothnessToPerceptualRoughness(smoothness); mip = BakeryPerceptualRoughnessToMipmapLevel(pr); } void WeightReflection_float(float smoothness, float metallic, float occlusion, float3 baseColor, float3 normal, float3 viewDir, float3 reflection, out float3 newReflection) { half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); half roughness = BakeryPerceptualRoughnessToRoughness(perceptualRoughness); half surfaceReduction = 1.0 / (roughness*roughness + 1.0); float3 pSpecular = lerp(unity_ColorSpaceDielectricSpec.rgb, baseColor, metallic); //baseColor = lerp(baseColor, 0, metallic); half reflectivity = max(max(pSpecular.r, pSpecular.g), pSpecular.b); half grazingTerm = saturate(smoothness + reflectivity); surfaceReduction *= occlusion; float3 pNdotV = saturate(dot(normal, viewDir)); float fresnel = 1 - pNdotV; float t2 = fresnel * fresnel; fresnel *= t2 * t2; newReflection = surfaceReduction * reflection * lerp(pSpecular, grazingTerm, fresnel); } void GetReflectionProjected_float(float3 worldPos, float3 viewDir, float3 normal, float lod, float smoothness, out float3 reflection) { float3 reflDir = normalize(reflect(-viewDir, normal)); float3 specCol = 0; #ifdef _REFLECTION_PROBE_BOX_PROJECTION #ifdef NOURP if (unity_SpecCube0_ProbePosition.w > 0.0f) { float3 factors = ((reflDir > 0 ? unity_SpecCube0_BoxMax.xyz : unity_SpecCube0_BoxMin.xyz) - worldPos) / reflDir; float scalar = min(min(factors.x, factors.y), factors.z); reflDir = reflDir * scalar + (worldPos - unity_SpecCube0_ProbePosition.xyz); } #endif #endif #ifdef NOURP #ifdef SURFACEANALYSIS float4 sampleRefl = 0; #else float4 sampleRefl = unity_SpecCube0.Sample(samplerunity_SpecCube0, reflDir, lod); #endif #else //float4 sampleRefl = SAMPLE_TEXTURECUBE_LOD(unity_SpecCube0, samplerunity_SpecCube0, reflDir, lod); half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness); #ifdef UNIVERSAL_GLOBAL_ILLUMINATION_INCLUDED #if UNITY_VERSION >= 202210 specCol = GlossyEnvironmentReflection(reflDir, worldPos, perceptualRoughness, 1, 0); // omit screenspace UVs for F+ for now #else specCol = GlossyEnvironmentReflection(reflDir, worldPos, perceptualRoughness, 1); #endif #endif #endif //float3 specCol = DecodeHDREnvironment(sampleRefl, unity_SpecCube0_HDR); reflection = specCol; } void GetURPShadow_float(float3 worldPos, float3 worldNormal, float3 bakedGI, out float3 modifiedGI, out float shadow) { #if defined(UNIVERSAL_SHADOWS_INCLUDED) && defined(LIGHTMAP_SHADOW_MIXING) && !defined(SHADOWS_SHADOWMASK) #ifdef _MAIN_LIGHT_SHADOWS_CASCADE half cascadeIndex = ComputeCascadeIndex(worldPos); #else half cascadeIndex = 0; #endif float4 shadowCoord = mul(_MainLightWorldToShadow[cascadeIndex], float4(worldPos, 1.0)); ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData(); half4 shadowParams = _MainLightShadowParams; shadow = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_LinearClampCompare), shadowCoord, shadowSamplingData, shadowParams, false); float3 lightDir = _MainLightPosition.xyz; float3 lightColor = _MainLightColor.rgb; half shadowStrength = GetMainLightShadowStrength(); half contributionTerm = saturate(dot(lightDir, worldNormal)); half3 lambert = lightColor * contributionTerm; half3 estimatedLightContributionMaskedByInverseOfShadow = lambert * (1.0 - shadow); half3 subtractedLightmap = bakedGI - estimatedLightContributionMaskedByInverseOfShadow; // Subtractive shadows are awful // But it is the only thing URP supports ¯\_(ツ)_/¯ half3 realtimeShadow = max(subtractedLightmap, _SubtractiveShadowColor.xyz); realtimeShadow = lerp(bakedGI, realtimeShadow, shadowStrength); modifiedGI = min(bakedGI, realtimeShadow); #else modifiedGI = bakedGI; // shadows are undefined in this scene! shadow = 1; #endif } void WeightReflection2_float(float smoothness, float metallic, float occlusion, float3 baseColor, float3 worldPos, float3 normal, float3 viewDir, float3 diffuse, float3 specular, float3 specularColor, out float3 newDiffuse, out float3 newSpecular) { // Convert metalness to specular and "oneMinusReflectivity" float3 specularColorFinal = lerp(float3(0.04, 0.04, 0.04), baseColor, metallic); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metallic * oneMinusDielectricSpec; #ifdef _SPECULAR_SETUP specularColorFinal = specularColor; #endif // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(normal, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColorFinal.r, specularColorFinal.g), specularColorFinal.b); // hack, but consistent with Unity code #ifdef _SPECULAR_SETUP oneMinusReflectivity = 1.0f - reflectivity; #endif float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColorFinal, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuse *= oneMinusReflectivity; // no baked GI override: modify diffuse specular *= fresnel3; diffuse = max(diffuse, 0); specular = max(specular, 0); // Subtract shadows if needed float shadow; GetURPShadow_float(worldPos, normal, diffuse, newDiffuse, shadow); newDiffuse = newDiffuse * baseColor * occlusion; newSpecular = specular * shadow; } void UnpackNormal_float(float4 tex, out float3 normal) { #if defined(UNITY_NO_DXT5nm) normal = tex.xyz * 2 - 1; normal.z = sqrt(1 - saturate(dot(normal.xy, normal.xy))); // do not trust normal map z #else normal = UnpackNormal(tex); #endif } void FullVolumeLighting_float(float3 objPos, float3 worldNormal, float3 viewDir, float ao, float supportBakedVolumeRotation, out float3 color) { #ifdef UNIVERSAL_LIGHTING_INCLUDED VertexPositionInputs vertexInput = GetVertexPositionInputs(objPos); float3 lpUV = VolumeCoords(vertexInput.positionWS, supportBakedVolumeRotation); float4 mask = _VolumeMask.Sample(sampler_VolumeMask, lpUV); Light light = GetMainLight(GetShadowCoord(vertexInput), vertexInput.positionWS, mask); //float specular = 1; //float smoothness = 1; float3 attenColor = light.color * light.distanceAttenuation * light.shadowAttenuation; color = LightingLambert(attenColor, light.direction, worldNormal); //color = LightingSpecular(attenColor, light.direction, worldNormal, viewDir, specular, smoothness); int pixelLightCount = GetAdditionalLightsCount(); uint layers = GetMeshRenderingLayer(); for (int i = 0; i < pixelLightCount; i++) { light = GetAdditionalLight(i, vertexInput.positionWS, mask); if (IsMatchingLightLayer(light.layerMask, layers)) { attenColor = light.color * light.distanceAttenuation * light.shadowAttenuation; color += LightingLambert(attenColor, light.direction, worldNormal); } } float3 sh; BakeryVolume_float(vertexInput.positionWS, worldNormal, supportBakedVolumeRotation, sh); color += sh * ao; #else color = 0; #endif } void FullVolumeLightingSpec_float(float3 objPos, float3 worldNormal, float3 viewDir, float3 albedo, float ao, float smoothness, float metalness, float supportBakedVolumeRotation, out float3 diffuse, out float3 specular, out float3 indirectSpecular) { #ifdef UNIVERSAL_LIGHTING_INCLUDED VertexPositionInputs vertexInput = GetVertexPositionInputs(objPos); float3 lpUV = VolumeCoords(vertexInput.positionWS, supportBakedVolumeRotation); float4 mask = _VolumeMask.Sample(sampler_VolumeMask, lpUV); Light light = GetMainLight(GetShadowCoord(vertexInput), vertexInput.positionWS, mask); float3 attenColor = light.color * light.distanceAttenuation * light.shadowAttenuation; diffuse = LightingLambert(attenColor, light.direction, worldNormal); float spec; Specular_float(light.direction, worldNormal, viewDir, smoothness, spec); specular = spec * attenColor; int pixelLightCount = GetAdditionalLightsCount(); uint layers = GetMeshRenderingLayer(); for (int i = 0; i < pixelLightCount; i++) { light = GetAdditionalLight(i, vertexInput.positionWS, mask); if (IsMatchingLightLayer(light.layerMask, layers)) { attenColor = light.color * light.distanceAttenuation * light.shadowAttenuation; diffuse += LightingLambert(attenColor, light.direction, worldNormal); Specular_float(light.direction, worldNormal, viewDir, smoothness, spec); specular += spec * attenColor; } } #ifdef BAKERY_LMSPEC float3 diffuseSH, specularSH; BakeryVolumeSpec_float(vertexInput.positionWS, worldNormal, viewDir, smoothness, albedo, metalness, supportBakedVolumeRotation, diffuseSH, specularSH); diffuse += diffuseSH * ao; indirectSpecular = specularSH * ao; #else float3 sh; BakeryVolume_float(vertexInput.positionWS, worldNormal, supportBakedVolumeRotation, sh); diffuse += sh * ao; indirectSpecular = 0; #endif #ifndef BAKERY_NOSPECULARWEIGHTING // Convert metalness to specular and "oneMinusReflectivity" float3 specularColor = lerp(float3(0.04, 0.04, 0.04), albedo, metalness); float oneMinusDielectricSpec = 1.0 - 0.04; float oneMinusReflectivity = oneMinusDielectricSpec - metalness * oneMinusDielectricSpec; // Directly apply fresnel and smoothness-dependent grazing term float nv = 1.0f - saturate(dot(worldNormal, viewDir)); float nv2 = nv * nv; float fresnel = nv * nv2 * nv2; float reflectivity = max(max(specularColor.r, specularColor.g), specularColor.b); // hack, but consistent with Unity code float grazingTerm = saturate(smoothness + reflectivity); float3 fresnel3 = lerp(specularColor, float3(grazingTerm, grazingTerm, grazingTerm), fresnel); diffuse *= oneMinusReflectivity; // no baked GI override: modify diffuse specular *= fresnel3; diffuse = max(diffuse, 0); specular = max(specular, 0); #endif #else diffuse = 0; specular = 0; #endif } void unpack3NFloats_float(float src, out float3 dest) { float r = frac(src); float g = frac(src * 256.0); float b = frac(src * 65536.0); dest = float3(r, g, b); }