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

news2025/2/12 21:50:02

项目里有一个其他同事实现的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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645661.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么决定了我们的命运?

一、什么决定了我们的命运&#xff1f; 一个学生时代看起来特别优秀的人&#xff0c; 后来成了特别平凡的人&#xff1b; 而那时候看起来平淡无奇的一些人&#xff0c; 后来做出了一些似乎超越了他水平的事情。 不禁想问&#xff0c;到底是什么决定了我们的命运&#xff1f; 关…

Transformer在CV领域有可能替代CNN吗?

目前已经有基于Transformer在三大图像问题上的应用&#xff1a;分类&#xff08;ViT&#xff09;&#xff0c;检测&#xff08;DETR&#xff09;和分割&#xff08;SETR&#xff09;&#xff0c;并且都取得了不错的效果。那么未来&#xff0c;Transformer有可能替换CNN吗&#…

uniapp创建vue3项目(持续更新)

一.项目全局配置 1. 创建项目 使用Hbuilderx工具创建项目, 使用Hbuilderx工具创建项目&#xff0c;选择uni-ui项目模版&#xff0c;VUE3 2.底部菜单栏配置tabBar uniapp官网&#xff1a; 全局文件--pages.json页面路由 -- tabBar 项目文件&#xff1a;pages.json--文件底部…

MM32F3273G8P火龙果开发板MindSDK开发教程18 -sfud库的移植

MM32F3273G8P火龙果开发板MindSDK开发教程18 -sfud库的移植 1、sfud简介 SFUD (Serial Flash Universal Driver) 串行 Flash 通用驱动库 推荐查看官方文档&#xff1a;一款使用 JEDEC SFDP 标准的串行 (SPI) Flash 通用驱动库 2、实验设备 主控&#xff1a;MM32F3273G8P火龙…

Matlab与ROS---深度学习(九)

0. 简介 在了解完上面8讲内容后&#xff0c;基本上ROS和Matlab最关键的部分已经介绍完毕。我们最后一讲就来简单的讲述一下如何在Matlab中结合ROS来完成障碍物的识别与检测。 1. 在Matlab中使用CUDA 配置SimulinkCoder以从Simulink模型生成和构建的CUDA的ROS节点是我们这一小…

如何选择接口测试工具?

目录 前言&#xff1a; 一、易用性 二、灵活性 三、可靠性 四、成本 如何正确选择接口测试工具 测试用例 接口测试数据 自动化测试 测试报告 总结 前言&#xff1a; 接口测试是一种重要的测试类型&#xff0c;常用于Web应用程序和服务的测试。选择一个合适的接口测…

一分钟了解物联存储柜的特点和功能

物联存储柜是一种智能存储柜&#xff0c;具有多种传感器和通信技术&#xff0c;集成物联网和云计算技术&#xff0c;通过自动化、智能化、网络化将传统存储柜与智能设备相结合&#xff0c;自动识别、存取物品&#xff0c;通过网络进行数据交互&#xff0c;实现远程监测和管理功…

矿业变革进行中,北斗技术赋能智慧矿山

近年来&#xff0c;在国家政策支持和技术创新驱动下&#xff0c;国内矿山一直致力于向智能化、数字化方向发展&#xff0c;智慧矿山建设正在加速推进中。 我国自主研发的北斗导航系统&#xff0c;不断与千行百业融合&#xff0c;广泛应用于生产生活的各个领域。“北斗矿业”也成…

怎么自学网安?过程中遇到问题怎么解决

趁着今天下班&#xff0c;我花了几个小时整理了下&#xff0c;非常不易&#xff0c;希望大家可以点赞收藏支持一波&#xff0c;谢谢。 我的经历&#xff1a; 我 19 年毕业&#xff0c;大学专业是物联网工程&#xff0c;我相信很多人在象牙塔里都很迷茫&#xff0c;到了大三大…

JavaScript数学对象-数字进制转换

关注“大前端私房菜”微信公众号&#xff0c;输入暗号【面试宝典】即可免费领取107页前端面试题。 什么是进制 进制就是达到指定位置时候进一位 常见的进制 十进制: 0 1 2 3 4 5 6 7 8 9 10 11 12 ... 99 100 101 二进制: 0 1 10 11 100 101 110 111 1000 八进制: 0 1 2 3 4 …

在thinkBook16的win11基础上安装ubuntu22.04

简介 背景&#xff1a;联想ThinkBook16 pro 32G/512G&#xff0c;系统盘已安装Win11&#xff0c;加装一条M.2-2T固态&#xff0c;想在2T上分一个300GB的EXT4分区&#xff0c;然后把ubuntu22.04安装在该分区上&#xff0c;并实现Win11Linux多系统共存。 目标&#xff1a;实现多…

【C++】STL基本介绍

目录 1、什么是STL 2、STL六大组件 2.1容器 2.2算法 2.3迭代器 1、什么是STL 概念: STL (standard template libaray - 标准模板库)&#xff1a;是 C 标准库的重要组成部分&#xff0c;不仅是一个可复用的组件库&#xff0c;而且是一个包罗数据结构与算法的软件框架。 …

重回游戏公平,向游戏打金工作室宣战!

什么是打金工作室&#xff1f; 近几年中国游戏市场持续发展&#xff0c;国产自研精品游戏层出不穷&#xff0c;游戏产业精品化、高质量健康发展趋势稳定。根据中国音数协游戏工委&#xff08;GPC&#xff09;与中国游戏产业研究院发布的《2022年中国游戏产业报告》显示&#xf…

总结2023开放原子全球开源峰会,中兴国产操作系统的开源贡献

近年来&#xff0c;随着信息技术的不断发展&#xff0c;催生出越来越多的智能场景&#xff0c;作为信息产业的底层技术&#xff0c;国产操作系统所面临的问题也变得越来越复杂。面对新技术和新场景对国产操作系统提出的新挑战、新要求&#xff0c;如何凝聚生态合作伙伴的优势力…

看了这几个C语言例子,你一定和我一样连说5个卧槽,声音一次比一次大

曾经我一直以为自己C语言学的还挺好的&#xff0c;直到看到这几个例子。 例1 首先来看一下&#xff0c;大师是如何求圆周率的&#xff0c;一口君实在词穷&#xff0c;first卧槽。 #include <stdio.h>long a10000,b0,c10000,d,e,f[10001],g;void main(){for(;b ! c; f[…

Rust 基础语法

Rust 基础语法 变量&#xff0c;基本类型&#xff0c;函数&#xff0c;注释和控制流&#xff0c;这些几乎是每种编程语言都具有的编程概念。 这些基础概念将存在于每个 Rust 程序中&#xff0c;及早学习它们将使你以最快的速度学习 Rust 的使用。 变量 首先必须说明&#x…

AIAgent来了!AutoGPT的新对手

AutoGPT 和 AgentGPT遇到了更强的对手&#xff0c;我来介绍一下 http://AIAgent.app 这个网站。 http://AIAgent.app 是一个可以让你使用人工智能代理来完成各种任务的网站。你只需要设置一个目标&#xff0c;然后选择一个合适的代理&#xff0c;它就会自动为你执行任务&#…

LED显示屏薄膜类型有哪些

LED显示屏薄膜是指应用在LED显示屏的薄膜材料&#xff0c;用于保护LED模块和增强显示效果。以下是LED显示屏薄膜知识的详细说明&#xff1a; 防护膜&#xff08;Protective Film&#xff09;&#xff1a;防护膜是一层透明的薄膜材料&#xff0c;常用于覆盖在LED显示屏的正面&am…

Android 手机自动化测试工具有哪几种?

文章大纲&#xff1a; 1、Android手机自动化测试工具&#xff0c;常用的有这7种 2、化繁为简&#xff0c;补充移动端自动化测试工具 3、这么多工具&#xff0c;工作中该如何选择 4、掌握自动化测试工具的学习建议 一、Android手机自动化测试工具&#xff0c;常用的有这7中&…

退休大厂软件测试面试官给大家的一些建议

最近因为又要增加用人&#xff0c;就又开始忙于招聘&#xff0c;一段时间下来遇到不少有趣的事情&#xff0c;结合之前的面试经验&#xff0c;就简单记录一下。 火眼金睛&#xff1a;识别真假 为什么一开始要说这个&#xff0c;因为最近确实遇到很多编造的简历&#xff0c;给…