下文是URP的Light.hlsl文件中用于计算Forward Add光的代码片段,和build-in不同的是,前向渲染的额外光照不再需要写进第二个Pass里了。
概括来说的话,代码中首先使用GetAdditionalLightsCount获取片元额外光的数量,LIGHT_LOOP_BEGIN……LIGHT_LOOP_END遍历每个额外光,GetAdditionalLight获取额外光对象,并将光加进片元中。
#if defined(_ADDITIONAL_LIGHTS)
uint pixelLightCount = GetAdditionalLightsCount();
#if USE_FORWARD_PLUS
for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
{
FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
}
#endif
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
#ifdef _LIGHT_LAYERS
if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
#endif
{
lightingData.additionalLightsColor += LightingPhysicallyBased(brdfData, brdfDataClearCoat, light,
inputData.normalWS, inputData.viewDirectionWS,
surfaceData.clearCoatMask, specularHighlightsOff);
}
LIGHT_LOOP_END
#endif
我预想按照这个大致流程给GPU实例化草地加入额外光照即可,但实际因为GPU实例化的特殊性,渲染管线无法知道具体的每棵草是否受到额外的光,即USE_FORWARD_PLUS这个宏是否开启,可以看到下文中如果USE_FORWARD_PLUS未开启,那GetAdditionalLight和GetAdditionalLightsCount就无法输出正确的数据:
int GetAdditionalLightsCount()
{
#if USE_FORWARD_PLUS
return 0;
#else
return int(min(_AdditionalLightsCount.x, unity_LightData.y));
#endif
}
Light GetAdditionalLight(uint i, float3 positionWS)
{
#if USE_FORWARD_PLUS
int lightIndex = i;
#else
int lightIndex = GetPerObjectLightIndex(i);
#endif
return GetAdditionalPerObjectLight(lightIndex, positionWS);
}
还有一个不能忽略的点是GetAdditionalLightsCount中的unity_LightData.y也不是正确的数字,gpu实例草中的位置信息管线本身是不知道的,因此这个值可以替换成我们自定的一个数字或者让脚本计算额外光数量_AddLightCount,避免额外光数量一直识别为0,也避免shader中额外光的遍历循环产生过多空转,这样一来,GPU实例化中的多光源支持就可以正常工作了:
#if _ADDITIONAL_LIGHTS
uint pixelLightCount = int(min(_AdditionalLightsCount.x, _AddLightCount));
half3 viewDir = normalize(_WorldSpaceCameraPos - input.positionWS);
LIGHT_LOOP_BEGIN(pixelLightCount)
Light light = GetAdditionalPerObjectLight(lightIndex, input.positionWS);
color.rgb += ApplySingleDirectLight(light, normalWS, viewDir, diffuseColor.xyz, input.positionOS.y);
LIGHT_LOOP_END
#endif