项目里有一个其他同事实现的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函数里 要加代码
文章来源:https://www.toymoban.com/news/detail-501598.html
#ifdef _ADDITIONAL_LIGHTS_VERTEX
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalInput.normalWS);
#endif
和fogFactor拼在一起
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
传给 片元着色器, 片元着色器计算时 再分别取 它们的值
另一个例子(不和 fogFactor, 单独传递):文章来源地址https://www.toymoban.com/news/detail-501598.html
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
//Pass to fragment shader to apply in Lighting function
output.vertexLight.rgb = VertexLighting(vertexData.positionWS, vertexData.normalWS);
#endif
到了这里,关于【unity】URP的shader开发中支持多光源,_ADDITIONAL_LIGHTS_VERTEX 和 _ADDITIONAL_LIGHTS 区别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!