// Return camera relative probe volume world to object transformation float4x4 GetProbeVolumeWorldToObject() { return ApplyCameraTranslationToInverseMatrix(unity_ProbeVolumeWorldToObject); } // In unity we can have a mix of fully baked lightmap (static lightmap) + enlighten realtime lightmap (dynamic lightmap) // for each case we can have directional lightmap or not. // Else we have lightprobe for dynamic/moving entity. Either SH9 per object lightprobe or SH4 per pixel per object volume probe float3 SampleBakedGI(float3 positionRWS, float3 normalWS, float2 uvStaticLightmap, float2 uvDynamicLightmap) { // If there is no lightmap, it assume lightprobe #if !defined(LIGHTMAP_ON) && !defined(DYNAMICLIGHTMAP_ON) // TODO: Confirm with Ionut but it seems that UNITY_LIGHT_PROBE_PROXY_VOLUME is always define for high end and // unity_ProbeVolumeParams always bind. if (unity_ProbeVolumeParams.x == 0.0) { // TODO: pass a tab of coefficient instead! real4 SHCoefficients[7]; SHCoefficients[0] = unity_SHAr; SHCoefficients[1] = unity_SHAg; SHCoefficients[2] = unity_SHAb; SHCoefficients[3] = unity_SHBr; SHCoefficients[4] = unity_SHBg; SHCoefficients[5] = unity_SHBb; SHCoefficients[6] = unity_SHC; return SampleSH9(SHCoefficients, normalWS); } else { return SampleProbeVolumeSH4(TEXTURE3D_PARAM(unity_ProbeVolumeSH, samplerunity_ProbeVolumeSH), positionRWS, normalWS, GetProbeVolumeWorldToObject(), unity_ProbeVolumeParams.y, unity_ProbeVolumeParams.z, unity_ProbeVolumeMin, unity_ProbeVolumeSizeInv); } #else float3 bakeDiffuseLighting = float3(0.0, 0.0, 0.0); #ifdef UNITY_LIGHTMAP_FULL_HDR bool useRGBMLightmap = false; float4 decodeInstructions = float4(0.0, 0.0, 0.0, 0.0); // Never used but needed for the interface since it supports gamma lightmaps #else bool useRGBMLightmap = true; #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 LIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED bakeDiffuseLighting += SampleDirectionalLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), TEXTURE2D_PARAM(unity_LightmapInd, samplerunity_Lightmap), uvStaticLightmap, unity_LightmapST, normalWS, useRGBMLightmap, decodeInstructions); #else bakeDiffuseLighting += SampleSingleLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uvStaticLightmap, unity_LightmapST, useRGBMLightmap, decodeInstructions); #endif #endif #ifdef DYNAMICLIGHTMAP_ON #ifdef DIRLIGHTMAP_COMBINED bakeDiffuseLighting += SampleDirectionalLightmap(TEXTURE2D_PARAM(unity_DynamicLightmap, samplerunity_DynamicLightmap), TEXTURE2D_PARAM(unity_DynamicDirectionality, samplerunity_DynamicLightmap), uvDynamicLightmap, unity_DynamicLightmapST, normalWS, false, decodeInstructions); #else bakeDiffuseLighting += SampleSingleLightmap(TEXTURE2D_PARAM(unity_DynamicLightmap, samplerunity_DynamicLightmap), uvDynamicLightmap, unity_DynamicLightmapST, false, decodeInstructions); #endif #endif return bakeDiffuseLighting; #endif } float4 SampleShadowMask(float3 positionRWS, float2 uvStaticLightmap) // normalWS not use for now { #if defined(LIGHTMAP_ON) float2 uv = uvStaticLightmap * unity_LightmapST.xy + unity_LightmapST.zw; float4 rawOcclusionMask = SAMPLE_TEXTURE2D(unity_ShadowMask, samplerunity_Lightmap, uv); // Reuse sampler from Lightmap #else float4 rawOcclusionMask; if (unity_ProbeVolumeParams.x == 1.0) { rawOcclusionMask = SampleProbeOcclusion(TEXTURE3D_PARAM(unity_ProbeVolumeSH, samplerunity_ProbeVolumeSH), positionRWS, GetProbeVolumeWorldToObject(), unity_ProbeVolumeParams.y, unity_ProbeVolumeParams.z, unity_ProbeVolumeMin, unity_ProbeVolumeSizeInv); } else { // Note: Default value when the feature is not enabled is float(1.0, 1.0, 1.0, 1.0) in C++ rawOcclusionMask = unity_ProbesOcclusion; } #endif return rawOcclusionMask; } // Calculate velocity in Clip space [-1..1] float2 CalculateVelocity(float4 positionCS, float4 previousPositionCS) { // This test on define is required to remove warning of divide by 0 when initializing empty struct // TODO: Add forward opaque MRT case... #if (SHADERPASS == SHADERPASS_VELOCITY) // Encode velocity positionCS.xy = positionCS.xy / positionCS.w; previousPositionCS.xy = previousPositionCS.xy / previousPositionCS.w; float2 velocity = (positionCS.xy - previousPositionCS.xy); #if UNITY_UV_STARTS_AT_TOP velocity.y = -velocity.y; #endif return velocity; #else return float2(0.0, 0.0); #endif } // For builtinData we want to allow the user to overwrite default GI in the surface shader / shader graph. // So we perform the following order of operation: // 1. InitBuiltinData - Init bakeDiffuseLighting and backBakeDiffuseLighting // 2. User can overwrite these value in the surface shader / shader graph // 3. PostInitBuiltinData - Handle debug mode + allow the current lighting model to update the data with ModifyBakedDiffuseLighting // This method initialize BuiltinData usual values and after update of builtinData by the caller must be follow by PostInitBuiltinData void InitBuiltinData( float alpha, float3 normalWS, float3 backNormalWS, float3 positionRWS, float2 texCoord1, float2 texCoord2, out BuiltinData builtinData) { ZERO_INITIALIZE(BuiltinData, builtinData); builtinData.opacity = alpha; // Sample lightmap/lightprobe/volume proxy builtinData.bakeDiffuseLighting = SampleBakedGI(positionRWS, normalWS, texCoord1, texCoord2); // We also sample the back lighting in case we have transmission. If not use this will be optimize out by the compiler // For now simply recall the function with inverted normal, the compiler should be able to optimize the lightmap case to not resample the directional lightmap // however it may not optimize the lightprobe case due to the proxy volume relying on dynamic if (to verify), not a problem for SH9, but a problem for proxy volume. // TODO: optimize more this code. builtinData.backBakeDiffuseLighting = SampleBakedGI(positionRWS, backNormalWS, texCoord1, texCoord2); #ifdef SHADOWS_SHADOWMASK float4 shadowMask = SampleShadowMask(positionRWS, texCoord1); builtinData.shadowMask0 = shadowMask.x; builtinData.shadowMask1 = shadowMask.y; builtinData.shadowMask2 = shadowMask.z; builtinData.shadowMask3 = shadowMask.w; #endif // Use uniform directly - The float need to be cast to uint (as unity don't support to set a uint as uniform) builtinData.renderingLayers = _EnableLightLayers ? asuint(unity_RenderingLayer.x) : DEFAULT_LIGHT_LAYERS; } // InitBuiltinData must be call before calling PostInitBuiltinData void PostInitBuiltinData( float3 V, PositionInputs posInput, SurfaceData surfaceData, inout BuiltinData builtinData) { #ifdef DEBUG_DISPLAY if (_DebugLightingMode == DEBUGLIGHTINGMODE_LUX_METER) { // The lighting in SH or lightmap is assume to contain bounced light only (i.e no direct lighting), // and is divide by PI (i.e Lambert is apply), so multiply by PI here to get back the illuminance builtinData.bakeDiffuseLighting *= PI; // don't take into account backBakeDiffuseLighting } else #endif { // Apply control from the indirect lighting volume settings - This is apply here so we don't affect emissive // color in case of lit deferred for example and avoid material to have to deal with it builtinData.bakeDiffuseLighting *= _IndirectLightingMultiplier.x; builtinData.backBakeDiffuseLighting *= _IndirectLightingMultiplier.x; #ifdef MODIFY_BAKED_DIFFUSE_LIGHTING ModifyBakedDiffuseLighting(V, posInput, surfaceData, builtinData); #endif } }