Render additional lights in a shader in URP
Create a light loop and iterate over additional lights in the Forward+ and Forward rendering paths.
읽는 시간 4분최근 업데이트: 24일 전
Write a rendering loop that iterates over additional lights in the Forward+ and Forward rendering paths in a URP shader.
The shader example on this page is compatible with both the Forward+ and Forward rendering paths. Unity handles additional lights and non-main directional lights differently in the Forward+ and Forward rendering paths. The Forward+ rendering path doesn't have a limit on the real-time lights per object. For a comparison of rendering paths, refer to Choose a rendering path in URP.
Add include directives to the shader file
Add the following include directives in the block in the shader file:
HLSLPROGRAM#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl"
Implement the light loop
-
In a shader pass, add the followingdirective to make the shader compatible with the Forward+ rendering path:
multi_compile#pragma multi_compile _ _CLUSTER_LIGHT_LOOP -
In the Forward+ rendering path, themacro requires the
LIGHT_LOOP_BEGINstruct. Declare the struct in the fragment shader.InputDataInputData inputData = (InputData)0;inputData.positionWS = input.positionWS;inputData.normalWS = input.normalWS;inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS);inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS); -
Use themacro to implement the additional light loop for non-main directional lights in the Forward+ rendering path:
UNITY_LOOP#if USE_CLUSTER_LIGHT_LOOPUNITY_LOOP for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++){ Light additionalLight = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1,1,1,1)); lighting += MyLightingFunction(inputData.normalWS, additionalLight);}#endif -
Use themacro to iterate over lights:
LIGHT_LOOP_BEGIN// Additional light loop.uint pixelLightCount = GetAdditionalLightsCount();LIGHT_LOOP_BEGIN(pixelLightCount) Light additionalLight = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1,1,1,1)); lighting += MyLightingFunction(inputData.normalWS, additionalLight);LIGHT_LOOP_END
Example
The following URP shader iterates over the additional lights, including non-main directional lights, and uses them in a custom lighting function.
The shader is compatible with both the Forward+ and Forward rendering paths.
Shader "Custom/AdditionalLights"{ Properties { } SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" } Cull Off ZWrite On Pass { // The LightMode tag matches the ShaderPassName set in UniversalRenderPipeline.cs. // The SRPDefaultUnlit pass and passes without the LightMode tag are also rendered by URP Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } HLSLPROGRAM #pragma vertex vert #pragma fragment frag // This multi_compile declaration is required for the Forward rendering path #pragma multi_compile _ _ADDITIONAL_LIGHTS // This multi_compile declaration is required for the Forward+ rendering path #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RealtimeLights.hlsl" struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD1; float3 normalWS : TEXCOORD2; }; Varyings vert(Attributes IN) { Varyings OUT; OUT.positionWS = TransformObjectToWorld(IN.positionOS.xyz); OUT.positionCS = TransformWorldToHClip(OUT.positionWS); OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS); return OUT; } float3 MyLightingFunction(float3 normalWS, Light light) { float NdotL = dot(normalWS, normalize(light.direction)); NdotL = (NdotL + 1) * 0.5; return saturate(NdotL) * light.color * light.distanceAttenuation * light.shadowAttenuation; } // This function loops through the lights in the scene float3 MyLightLoop(float3 color, InputData inputData) { float3 lighting = 0; // Get the main light Light mainLight = GetMainLight(); lighting += MyLightingFunction(inputData.normalWS, mainLight); // Get additional lights #if defined(_ADDITIONAL_LIGHTS) // Additional light loop for non-main directional lights. This block is specific to Forward+. #if USE_CLUSTER_LIGHT_LOOP UNITY_LOOP for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++) { Light additionalLight = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1,1,1,1)); lighting += MyLightingFunction(inputData.normalWS, additionalLight); } #endif // Additional light loop. uint pixelLightCount = GetAdditionalLightsCount(); LIGHT_LOOP_BEGIN(pixelLightCount) Light additionalLight = GetAdditionalLight(lightIndex, inputData.positionWS, half4(1,1,1,1)); lighting += MyLightingFunction(inputData.normalWS, additionalLight); LIGHT_LOOP_END #endif return color * lighting; } half4 frag(Varyings input) : SV_Target0 { // The Forward+ light loop (LIGHT_LOOP_BEGIN) requires the InputData struct to be in its scope. InputData inputData = (InputData)0; inputData.positionWS = input.positionWS; inputData.normalWS = input.normalWS; inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS); inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS); float3 surfaceColor = float3(1, 1, 1); float3 lighting = MyLightLoop(surfaceColor, inputData); half4 finalColor = half4(lighting, 1); return finalColor; } ENDHLSL } }}