#ifndef __LIGHTINGTEMPLATE_H__ #define __LIGHTINGTEMPLATE_H__ #include "UnityCG.cginc" #include "UnityStandardBRDF.cginc" #include "UnityStandardUtils.cginc" #include "UnityPBSLighting.cginc" uniform uint g_nNumDirLights; //--------------------------------------------------------------------------------------------------------------------------------------------------------- // TODO: clean up.. -va #define MAX_SHADOW_LIGHTS 10 #define MAX_SHADOWMAP_PER_LIGHT 6 #define MAX_DIRECTIONAL_SPLIT 4 #define CUBEMAPFACE_POSITIVE_X 0 #define CUBEMAPFACE_NEGATIVE_X 1 #define CUBEMAPFACE_POSITIVE_Y 2 #define CUBEMAPFACE_NEGATIVE_Y 3 #define CUBEMAPFACE_POSITIVE_Z 4 #define CUBEMAPFACE_NEGATIVE_Z 5 CBUFFER_START(ShadowLightData) float4 g_vShadow3x3PCFTerms0; float4 g_vShadow3x3PCFTerms1; float4 g_vShadow3x3PCFTerms2; float4 g_vShadow3x3PCFTerms3; float4 g_vDirShadowSplitSpheres[MAX_DIRECTIONAL_SPLIT]; float4x4 g_matWorldToShadow[MAX_SHADOW_LIGHTS * MAX_SHADOWMAP_PER_LIGHT]; CBUFFER_END //--------------------------------------------------------------------------------------------------------------------------------------------------------- //UNITY_DECLARE_TEX2D(_LightTextureB0); sampler2D _LightTextureB0; UNITY_DECLARE_TEX2DARRAY(_spotCookieTextures); UNITY_DECLARE_TEXCUBEARRAY(_pointCookieTextures); StructuredBuffer g_dirLightData; #define VALVE_DECLARE_SHADOWMAP( tex ) Texture2D tex; SamplerComparisonState sampler##tex #define VALVE_SAMPLE_SHADOW( tex, coord ) tex.SampleCmpLevelZero( sampler##tex, (coord).xy, (coord).z ) VALVE_DECLARE_SHADOWMAP(g_tShadowBuffer); float ComputeShadow_PCF_3x3_Gaussian(float3 vPositionWs, float4x4 matWorldToShadow) { float4 vPositionTextureSpace = mul(float4(vPositionWs.xyz, 1.0), matWorldToShadow); vPositionTextureSpace.xyz /= vPositionTextureSpace.w; float2 shadowMapCenter = vPositionTextureSpace.xy; if ((shadowMapCenter.x < 0.0f) || (shadowMapCenter.x > 1.0f) || (shadowMapCenter.y < 0.0f) || (shadowMapCenter.y > 1.0f)) return 1.0f; float objDepth = saturate(257.0 / 256.0 - vPositionTextureSpace.z); float4 v20Taps; v20Taps.x = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.xy, objDepth)).x; // 1 1 v20Taps.y = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.zy, objDepth)).x; // -1 1 v20Taps.z = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.xw, objDepth)).x; // 1 -1 v20Taps.w = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms1.zw, objDepth)).x; // -1 -1 float flSum = dot(v20Taps.xyzw, float4(0.25, 0.25, 0.25, 0.25)); if ((flSum == 0.0) || (flSum == 1.0)) return flSum; flSum *= g_vShadow3x3PCFTerms0.x * 4.0; float4 v33Taps; v33Taps.x = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms2.xz, objDepth)).x; // 1 0 v33Taps.y = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms3.xz, objDepth)).x; // -1 0 v33Taps.z = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms3.zy, objDepth)).x; // 0 -1 v33Taps.w = VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy + g_vShadow3x3PCFTerms2.zy, objDepth)).x; // 0 1 flSum += dot(v33Taps.xyzw, g_vShadow3x3PCFTerms0.yyyy); flSum += VALVE_SAMPLE_SHADOW(g_tShadowBuffer, float3(shadowMapCenter.xy, objDepth)).x * g_vShadow3x3PCFTerms0.z; return flSum; } //--------------------------------------------------------------------------------------------------------------------------------------------------------- /** * Gets the cascade weights based on the world position of the fragment and the positions of the split spheres for each cascade. * Returns an invalid split index if past shadowDistance (ie 4 is invalid for cascade) */ float GetSplitSphereIndexForDirshadows(float3 wpos) { float3 fromCenter0 = wpos.xyz - g_vDirShadowSplitSpheres[0].xyz; float3 fromCenter1 = wpos.xyz - g_vDirShadowSplitSpheres[1].xyz; float3 fromCenter2 = wpos.xyz - g_vDirShadowSplitSpheres[2].xyz; float3 fromCenter3 = wpos.xyz - g_vDirShadowSplitSpheres[3].xyz; float4 distances2 = float4(dot(fromCenter0, fromCenter0), dot(fromCenter1, fromCenter1), dot(fromCenter2, fromCenter2), dot(fromCenter3, fromCenter3)); float4 vDirShadowSplitSphereSqRadii; vDirShadowSplitSphereSqRadii.x = g_vDirShadowSplitSpheres[0].w; vDirShadowSplitSphereSqRadii.y = g_vDirShadowSplitSpheres[1].w; vDirShadowSplitSphereSqRadii.z = g_vDirShadowSplitSpheres[2].w; vDirShadowSplitSphereSqRadii.w = g_vDirShadowSplitSpheres[3].w; fixed4 weights = float4(distances2 < vDirShadowSplitSphereSqRadii); weights.yzw = saturate(weights.yzw - weights.xyz); return 4 - dot(weights, float4(4, 3, 2, 1)); } float SampleShadow(uint type, float3 vPositionWs, float3 vPositionToLightDirWs, uint lightIndex) { float flShadowScalar = 1.0; int shadowSplitIndex = 0; if (type == DIRECTIONAL_LIGHT) { shadowSplitIndex = GetSplitSphereIndexForDirshadows(vPositionWs); } else if (type == SPHERE_LIGHT) { float3 absPos = abs(vPositionToLightDirWs); shadowSplitIndex = (vPositionToLightDirWs.z > 0) ? CUBEMAPFACE_NEGATIVE_Z : CUBEMAPFACE_POSITIVE_Z; if (absPos.x > absPos.y) { if (absPos.x > absPos.z) { shadowSplitIndex = (vPositionToLightDirWs.x > 0) ? CUBEMAPFACE_NEGATIVE_X : CUBEMAPFACE_POSITIVE_X; } } else { if (absPos.y > absPos.z) { shadowSplitIndex = (vPositionToLightDirWs.y > 0) ? CUBEMAPFACE_NEGATIVE_Y : CUBEMAPFACE_POSITIVE_Y; } } } flShadowScalar = ComputeShadow_PCF_3x3_Gaussian(vPositionWs.xyz, g_matWorldToShadow[lightIndex * MAX_SHADOWMAP_PER_LIGHT + shadowSplitIndex]); return flShadowScalar; } float3 ExecuteLightList(uint start, uint numLights, float3 vP, float3 vPw, float3 Vworld) { UnityIndirect ind; UNITY_INITIALIZE_OUTPUT(UnityIndirect, ind); ind.diffuse = 0; ind.specular = 0; float3 ints = 0; uint l=0; for (int i = 0; i < g_nNumDirLights; i++) { DirectionalLight lightData = g_dirLightData[i]; float atten = 1; [branch] if (lightData.uShadowLightIndex != 0xffffffff) { float shadowScalar = SampleShadow(DIRECTIONAL_LIGHT, vPw, 0, lightData.uShadowLightIndex); atten *= shadowScalar; } UnityLight light; light.color.xyz = lightData.vCol.xyz * atten; light.dir.xyz = mul((float3x3) g_mViewToWorld, -lightData.vLaxisZ).xyz; ints += EvalMaterial(light, ind); } // we need this outer loop for when we cannot assume a wavefront is 64 wide // since in this case we cannot assume the lights will remain sorted by type // during processing in lightlist_cs.hlsl #if !defined(XBONE) && !defined(PLAYSTATION4) while(l0.0); // finally apply this to the dist att. const bool bHasShadow = (lgtDat.flags&HAS_SHADOW)!=0; [branch]if(bHasShadow) { float shadowScalar = SampleShadow(SPOT_LIGHT, vPw, 0, lgtDat.uShadowLightIndex); atten *= shadowScalar; } UnityLight light; light.color.xyz = lgtDat.vCol.xyz*atten*angularAtt.xyz; light.dir.xyz = mul((float3x3) g_mViewToWorld, vL).xyz; //unity_CameraToWorld ints += EvalMaterial(light, ind); ++l; uIndex = l=MAX_TYPES) ++l; if(uLgtType!=SPOT_LIGHT && uLgtType!=SPHERE_LIGHT) ++l; #endif } return ints; } #endif