【unity】URP的shader开发中支持多光源,_ADDITIONAL_LIGHTS_VERTEX 和 _ADDITIONAL_LIGHTS 区别

项目里有一个其他同事实现的shader,美术那边希望能支持多个光源, 我一看代码里面, frag 函数里已经实现了
 


				#ifdef _ADDITIONAL_LIGHTS
					uint pixelLightCount = GetAdditionalLightsCount();
					for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
					{
						Light light = GetAdditionalLight(lightIndex, i.posWorld.xyz);
						half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
						lightColor += LightingLambert(attenuatedLightColor, light.direction, normalDirection);
					}
				#endif

代码也加了:

            #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS

材质里加了这个keyword还是没起作用,   若宏控制注了有效。  一开始没搞明白……
想到很可能这个关键字是系统本身控制的, 搜索了一下"_ADDITIONAL_LIGHTS",找到ForwardLights.cs里 确实控制了 _ADDITIONAL_LIGHTS 和 _ADDITIONAL_LIGHTS_VERTEX的开关,且2个只能存在一个(所以 multi_compile 是配置在一起的)

最后发现是 UniversalRenderPipelineAsset 这个自定义配置里Additional Lights的配置:

 perVertex (开 _ADDITIONAL_LIGHTS_VERTEX宏)表示 在顶点着色器 计算时,就取其他光源数据计算 光照值, 这样顶点数少 但像素绘制比较多(片元着色器执行次数更多)的情况,就能节省不少计算。

perPixel (开 _ADDITIONAL_LIGHTS) 表示  在片元着色器计算时,对其他光源做采样计算每个光照值 然后叠加。 (光源越多循环次数越多 性能影响也越大。)


一般来说使用 _ADDITIONAL_LIGHTS_VERTEX,可能要优化一些,但是有局限性,平行光还好,针对点光源,面片比较大的模型就很奇怪了,    这次就遇到这个问题,尝试地表接受一个点光源试试发现没作用,还以为代码没写好…………, 最后发现是它面太大了,顶点离点光源太远了 没计算进去……,如果点光跑到顶点附近 则还是有效果的, 但这个面片就整体发亮很奇怪。  

所以一般点光源 直接用lightmap烘培,运行时不要开,除非有特殊需求, 要么就是放到片元着色器里计算其他光。



自己写shader时如何让其支持  _ADDITIONAL_LIGHTS_VERTEX 或 _ADDITIONAL_LIGHTS呢?
可以参考simplelit.shader等 示例
_ADDITIONAL_LIGHTS比较简单
Fragment初始函数里在主光源后加

SimpleLitForwardPass.hlsl 调用的Lighting.hlsl 里的函数, 看 #ifdef _ADDITIONAL_LIGHTS 部分

half4 UniversalFragmentBlinnPhong(InputData inputData, half3 diffuse, half4 specularGloss, half smoothness, half3 emission, half alpha)
{
    Light mainLight = GetMainLight(inputData.shadowCoord);
    MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));

    half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation);
    half3 diffuseColor = inputData.bakedGI + LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS);
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
    half3 specularColor = LightingSpecular(attenuatedLightColor, mainLight.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, smoothness);
#else 
    half3 specularColor = 0;
#endif

#ifdef _ADDITIONAL_LIGHTS
    uint pixelLightCount = GetAdditionalLightsCount();
    for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
    {
        Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
        half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
        diffuseColor += LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS);
#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
        specularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, smoothness);
#endif
    }
#endif

#ifdef _ADDITIONAL_LIGHTS_VERTEX
    diffuseColor += inputData.vertexLighting;
#endif

    half3 finalColor = diffuseColor * diffuse + emission;

#if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR)
    finalColor += specularColor;
#endif

    return half4(finalColor, alpha);
}

如果shader用的 BRDFData 数据结构的 类似这样加
 


#ifdef _ADDITIONAL_LIGHTS
    uint pixelLightCount = GetAdditionalLightsCount();
    for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
    {
        Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
        color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
    }
#endif

#ifdef _ADDITIONAL_LIGHTS_VERTEX
    color += inputData.vertexLighting * brdfData.diffuse;
#endif

我们自己实现的一个shader:
 


				float4 shadowCoord = TransformWorldToShadowCoord(i.posWorld.xyz);
				Light mainLight = GetMainLight(shadowCoord);
				float NDotL = dot(normalDirection, mainLight.direction);
				//float halfLambert = saturate(NDotL * 0.5 + 0.5);
				float Lambert = saturate(NDotL);

				//half3 gi = SampleSH(normalDirection);
				half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation);
				half3 lightColor = Lambert * attenuatedLightColor;

                half3 bakedGI = SAMPLE_GI(i.lightmapUV, i.vertexSH, normalDirection);
                MixRealtimeAndBakedGI(mainLight, normalDirection, bakedGI, half4(0, 0, 0, 0));
                lightColor += bakedGI;

				#ifdef _ADDITIONAL_LIGHTS
					uint pixelLightCount = GetAdditionalLightsCount();
					for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
					{
						Light light = GetAdditionalLight(lightIndex, i.posWorld.xyz);
						half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
						lightColor += LightingLambert(attenuatedLightColor, light.direction, normalDirection);
					}
				#endif


#ifdef _ADDITIONAL_LIGHTS_VERTEX
                half3 vertexLighting = i.fogFactorAndVertexLight.yzw;
                lightColor += vertexLighting;
#endif

要支持 _ADDITIONAL_LIGHTS_VERTEX , (片元着色器代码 上面有示例了,不贴了)
主要是顶点着色器里的计算 和 怎么传递给片元着色器:
一般需要 结构体 v2f (或者叫 VertexOutput  或者叫 Varyings,反正是自己定义的)

定义一个 
    half4 fogFactorAndVertexLight   : TEXCOORD6; // x: fogFactor, yzw: vertex light
这个是把 unity自带fog的值一起传递了
当然单独加一个也是可以的

#if defined(_ADDITIONAL_LIGHTS_VERTEX) 
    float3 vertexLight                : TEXCOORD7;
#endif

顶点着色器 vert函数里 要加代码
 


#ifdef _ADDITIONAL_LIGHTS_VERTEX
    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
#endif

和fogFactor拼在一起
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
传给 片元着色器,   片元着色器计算时 再分别取 它们的值

另一个例子(不和 fogFactor, 单独传递):

#if defined(_ADDITIONAL_LIGHTS_VERTEX) 
    //Pass to fragment shader to apply in Lighting function
    output.vertexLight.rgb = VertexLighting(vertexData.positionWS, vertexData.normalWS);
#endif

### 关于Unity中的EvaluateSHVertex函数及其与_additionalLights_mainLightShadows的关系 在Unity引擎中,`EvaluateSHVertex` 函数用于计算球谐光(Spherical Harmonics, SH),这是一种高效表示环境光照的方法。该方法能够快速近似复杂场景中的间接照明效果[^1]。 当涉及到多光源处理时,Unity提供了 `_additionalLights` 变量来控制额外动态灯光的数量支持方式。通过设置此变量,开发者可以优化性能并调整视觉质量。对于主要方向光(即太阳或其他强光源),则有专门的宏定义如 `_MAIN_LIGHT_SHADOWS` 来启用阴影投射功能[^2]。 下面是一个简单的着色器片段代码示例,展示了如何结合这些特性: ```hlsl // HLSL Shader Code Example #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; }; float4 frag(Attributes input) : SV_Target { // 计算顶点位置的世界空间坐标 float3 posWS = mul((float3x3)_ObjectToWorld, input.positionOS.xyz); // 使用 EvaluateSHVertex 进行球谐光照评估 half3 shEvaluatedColor = EvaluateSHVertex(posWS); // 处理附加灯光影响 (如果启用了_ADDITIONAL_LIGHTS 宏) #ifdef _ADDITIONAL_LIGHTS for(int i = 0; i < _AdditionalLightsCount; ++i){ Light light = GetAdditionalLight(i, posWS); shEvaluatedColor += LightingFunction(light, normalWS); } #endif // 应用主光源阴影贴图采样 (如果启用了_MAIN_LIGHT_SHADOWS 宏) #if defined(_MAIN_LIGHT_SHADOWS) ShadowCoord shadowCoord = TransformWorldToShadowCoord(posWS); float mainLightAtten = SampleMainLightShadowMap(shadowCoord); shEvaluatedColor *= mainLightAtten; #endif return float4(shEvaluatedCode.rgb, 1.0); } ``` 上述代码段说明了在一个自定义HLSL着色器内集成 `EvaluateSHVertex` 其他高级光照特性的基本流程。请注意实际项目可能需要更复杂的逻辑以适应特定需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值