UnityShader学习笔记——多种光源
——内容源自唐老狮的shader课程
目录
1.光源类型
2.判断光源类型
2.1.在哪判断
2.2.如何判断
3.光照衰减
3.1.基本概念
3.2.unity中的光照衰减
3.3.光源空间变换矩阵
4.点光源衰减计算
5.聚光灯衰减计算
5.2.聚光灯衰减计算
6.应用
7.如有疏漏,还请指出
1.光源类型
平行光:没有位置,没有衰减,只有方向,颜色,强度参与计算
点光源:五个属性都需要考虑
聚光灯:五个属性都需要考虑,并且因为他的范围(由Range和Spot Angle共同决定)特殊,所以需要进行更复杂的运算
面光源:在烘培时使用
2.判断光源类型
2.1.在哪判断
我们一般在Additional Pass中判断光源类型以分别处理各部分的逻辑,因为不同光源的处理不一样
2.2.如何判断
unity提供了三个宏:
- _DIRECTIONAL_LIGHT(平行光)
- _POINT_LIGHT(点光源)
- _SPOT_LIGHT(聚光灯)
//在CG中使用条件语句来分别处理#if defined(_DIRECTIONAL_LIGHT)
平行光逻辑
#elif defined(_POINT_LIGHT)
点光源逻辑
#elif defined(_SPOT_LIGHT)
聚光灯逻辑
#else
其他逻辑
#endif
unity底层会根据该条件编译指令,生成多个 Shader Variants(着色器变体),这些变体共享相同的核心代码,但会根据条件选择执行不同的代码块。
Shader Variants是在编写Shader时根据不同的配置(如条件编译指令下不同的条件)生成的多个版本的Shader
3.光照衰减
3.1.基本概念
通常是指渲染过程中考虑光线在传播过程中的减弱效应。
一般常见的光照衰减计算方式有:线性衰减和平方衰减,后者更符合现实
3.2.unity中的光照衰减
为了提高性能,我们一般不会直接通过数学公式计算衰减,而是使用一张纹理作为查找表,在片元着色器中计算逐像素光照的衰减。
为此,unity准备了一个内置的纹理类型的变量 _LightTexture0,unity会在内部计算好相关数据并存储到这个变量中。其上的纹理颜色值,表明了光源空间中不同位置的点对应的衰减值。
起点(0,0)位置,表明和光源重合的点的衰减值
终点(1,1)位置,表明光源空间中离光源最远的点的衰减值
一般我们会直接从_LightTexture0纹理中进行纹理采样,利用其中的UNITY_ATTEN_CHANNE宏来获得衰减值所在的分量,即:
tex2D(_LightTexture0,,对应纹理坐标).UNITY_ATTEN_CHANNEL
要注意的是:如果光源存在cookie(灯光遮罩),那么衰减查找纹理就变成_LightTextureB0
3.3.光源空间变换矩阵
由于我们要从光照纹理中取得对应的衰减数据,因此我们需要将顶点坐标从世界空间变换到光源空间,然后再去获取衰减数据,使用如下公式即可:
mul(unity_WorldToLight,世界空间下顶点坐标)
4.点光源衰减计算
1.将顶点从世界空间下转换到光源空间,并且只取xyz分量,也就是说lightCoord是一个float3的变量。需要了解的是,unity_WorldToLight计算所得的是一个规范在0~1之间的值,这个值可视为与光源的距离
2.利用该光源空间的坐标来计算离光源的距离,并利用距离参数,从衰减纹理中采样
fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).xx).UNITY_ATTEN_CHANNEL
纹理坐标使用distance ^ 2是为了符合现实。ligthCoord是光源空间下顶点位置
5.聚光灯衰减计算
5.1.聚光灯的cookie(灯光遮罩)
unity会默认为聚光灯弄个cookie,这玩意主要是用来模拟聚光灯的区域性,就像打手电一样
此时,_LightTexture0 存储的变为cookie纹理信息,_LightTextureB0 存储的才是光照纹理信息,也就是说衰减值要从这里面取
5.2.聚光灯衰减计算
1.将顶点从世界空间转换到光源空间,并且这里的lightCoord是一个float4变量,也就是取了w
2.利用光源空间下的坐标信息与一些数据获取聚光灯的衰减信息,公式为(好长):
fixed atten = (lightCoord.z > 0) *
tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w *
tex2D(_LightTextureB0, dot(lightCoord, lightCoord).xx).UNITY_ATTEN_CHANNEL
第一行:判断物体是不是在聚光灯的背面,如果在背面返回值是0,即不受聚光灯影响
第二行:后半部分就是把xy变到01区间内,xy/w能变到-0.5~0.5之间,加个0.5就正好,然后对遮罩纹理采样获取遮罩衰减值(就是那个w)。为什么要这么变换可以画个图根据相似三角形推导一下
第三行:正常取出基于距离的光照衰减值,需要了解的是,dot不会计算w
6.应用
1.为BasePass添加编译指令 #pragma multi_compile_fwdbase
为AdditionalPass添加编译指令 #pragma multi_compile_fwdadd
这些编译指令会帮我们编译对应Pass中所有变体,并且附加通道的编译指令还可以确保在附加渲染通道中能访问到正确的光照变量
2.在附加通道中加入混合命令 Blend One One,即线性简单效果
3.根据条件判断语句,基于不同的光照类型计算光的方向和衰减值。方向是因为点光源和聚光灯需要用光源位置减去顶点位置,平行光不用
4.计算最终颜色,附加通道中的最后颜色不需要再加环境光,只需要将满反射颜色和高光反射颜色相加然后乘以衰减值即可
//这是附加通道Pass{Tags { "LightMode" = "ForwardAdd" }Blend One OneCGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fwdadd#include "Lighting.cginc"#include "UnityCG.cginc"#include "AutoLight.cginc"fixed4 _DiffuseColor;fixed4 _SpecularColor;float _Gloss;struct v2f{float4 pos : SV_POSITION;float3 wNormal : NORMAL;float3 wPos : TEXCOORD0;};v2f vert(appdata_full v){v2f data;data.pos = UnityObjectToClipPos(v.vertex);data.wNormal = UnityObjectToWorldNormal(v.normal);float3 wPos = mul(unity_ObjectToWorld, v.vertex).xyz;data.wPos = wPos;return data;}fixed4 frag(v2f f) : SV_TARGET{float3 wNormal = normalize(f.wNormal);float3 wLightDir = float3(0, 0, 0);#if defined(_DIRECTIONAL_LIGHT)wLightDir = normalize(_WorldSpaceLightPos0);#else// 点光源和聚光灯的灯光方向需要用 位置 减去 顶点位置wLightDir = normalize(_WorldSpaceLightPos0.xyz - f.wPos);#endiffloat3 wViewDir = normalize(_WorldSpaceCameraPos - f.wPos).xyz;float3 halfAngle = normalize(wLightDir + wViewDir);fixed3 lambertColor = _LightColor0.rgb * _DiffuseColor.rgb * max(0, dot(wNormal, wLightDir));fixed3 blinn_PhongColor = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(wNormal, halfAngle)), _Gloss);//这个条件判断不止一种写法,还有别的写法#if defined(_DIRECTIONAL_LIGHT)float atten = 1;#else#if defined(_POINT_LIGHT)//注意由世界坐标向空间左边转换时,将wPos作为四维向量转换并只取xyz(因为w没用)float3 lightCoord = mul(unity_WorldToLight, float4(f.wPos, 1)).xyz;float atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).xx).UNITY_ATTEN_CHANNEL;#elif defined(_SPOT_LIGHT)//这里w就有用了float4 lightCoord = mul(unity_WorldToLight, float4(f.wPos, 1));//1.判断能否照到; 2.映射到最大平面; 3.距离的平方采样; float atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;#elsefloat atten = 1;#endif#endif//附加渲染通道中不需要加 环境光颜色,原因是再基础通道中就已经计算了//衰减值 乘以 兰伯特光照和布林芳高光的颜色之和fixed3 finalColor = (lambertColor + blinn_PhongColor) * atten;return fixed4(finalColor, 1);}ENDCG}
7.如有疏漏,还请指出
相关文章:
UnityShader学习笔记——多种光源
——内容源自唐老狮的shader课程 目录 1.光源类型 2.判断光源类型 2.1.在哪判断 2.2.如何判断 3.光照衰减 3.1.基本概念 3.2.unity中的光照衰减 3.3.光源空间变换矩阵 4.点光源衰减计算 5.聚光灯衰减计算 5.1.聚光灯的cookie(灯光遮罩) 5.2.聚…...
电脑右下角小喇叭没反应怎么回事,快速解决方案
当电脑右下角的小喇叭(音量图标)没有反应时,可以尝试以下快速解决方案: 一、基础检查与操作 检查键盘音量键: 按下键盘上的音量增加或减少键,或尝试Fn音量键(部分笔记本需组合键)&a…...
chrome-base 如何实现一个BindOnce
考虑一个问题: worker_thread.task_runner()->PostDelayedTask(FROM_HERE, base::BindOnce(&Ref::Foo, ref, 1), base::Milliseconds(1000)); BindOnce 是如何实现的呢? 翻看源码:base\functional\bind.h 写的 非常简洁 // Bind a…...
HTML学习之CSS三种引入方式
HTML学习之CSS三种引入方式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>…...
Mysql基于binlog主从同步配置
主配置: 修改配置文件:/etc/my.cnf 添加server-id1 重启MySQL服务:systemctl restart mysqld 创建用户并授权: mysql> create user rep192.168.79.% identified with mysql_native_password by 123456; Query OK, 0 rows aff…...
Docker Desktop安装到其他盘
Docker Desktop 默认安装到c盘,占用空间太大了,想给安装到其他盘,网上找了半天的都不对 正确安装命令: start /w "" "Docker Desktop Installer.exe" install --installation-dirF:\docker命令执行成功&am…...
NetCore Consul动态伸缩+Ocelot 网关 缓存 自定义缓存 + 限流、熔断、超时 等服务治理
网关 OcelotGeteway 网关 Ocelot配置文件 {//单地址多实例负载均衡Consul 实现动态伸缩"Routes": [{// 上游 》》 接受的请求//上游请求方法,可以设置特定的 HTTP 方法列表或设置空列表以允许其中任何方法"UpstreamHttpMethod": [ "Get", &quo…...
ubuntu 本地部署deepseek r1 蒸馏模型
本文中的文件路径或网络代理需要根据自身环境自行删改 一、交互式chat页面 1.1 open-webui 交互窗口部署:基于docker安装,且支持联网搜索 Open WebUI 是一个可扩展、功能丰富且用户友好的自托管 AI 平台,旨在完全离线操作。它支持各种 LLM…...
go语言中的反射
为什么会引入反射 有时我们需要写一个函数,这个函数有能力统一处理各种值类型,而这些类型可能无法共享同一个接口,也可能布局未知,也有可能这个类型在我们设计函数时还不存在,这个时候我们就可以用到反射。 空接口可…...
旅行社项目展示微信小程序功能模块和开发流程
旅行社当前旅游线路的程序(微信小程序),旨在帮助旅行社更高效地管理线下活动预订,同时为客户提供便捷的报名和查看功能。适用于短途游、团队建设等活动,支持在线预订、缴费及订单管理,可根据用户需求定制更多个性化服务,为公司提升品牌知名度与客户体验。通过简洁明了的…...
JUC学习笔记02
文章目录 JUC笔记2练习题:手写线程池代码解释:AdvancedThreadPool 类:WorkerThread 内部类:AdvancedThreadPoolExample 类: 线程池的思考CPU密集型IO密集型 练习题:手写自动重试机练习题:手写定…...
【论文翻译】DeepSeek-V3论文翻译——DeepSeek-V3 Technical Report——第一部分:引言与模型架构
论文原文链接:DeepSeek-V3/DeepSeek_V3.pdf at main deepseek-ai/DeepSeek-V3 GitHub 特别声明,本文不做任何商业用途,仅作为个人学习相关论文的翻译记录。本文对原文内容直译,一切以论文原文内容为准,对原文作者表示…...
python编程-类结构,lambda语法,原始字符串
一个类的基本结构包括以下部分: 类名:用来描述具有相同属性和方法的对象的集合。 属性:类变量或实例变量,用于处理类及其实例对象的相关数据。 方法:在类中定义的函数,用于执行特定操作。 构造器ÿ…...
C++:代码常见规范1
头文件包含 (1)先系统头文件,后用户头文件:这是一个良好的编程习惯。系统头文件通常包含标准库的定义,而用户头文件则包含项目特定的定义。将系统头文件放在前面可以避免因用户头文件中的定义与系统头文件冲突而导致的问题。 #include <…...
C++(进阶五)--STL--用一颗红黑树封装map和set
目录 1.红黑树源码(简略版) 2.模板参数的控制 3.红黑树的结点 4.迭代器的实现 正向迭代器 反向迭代器 5.set的模拟实现 6.map的模拟实现 7.封装完成后的代码 RBTree.h mymap.h myset.h 1.红黑树源码(简略版) 下面我们…...
DeepSeek服务器繁忙问题的原因分析与解决方案
一、引言 随着人工智能技术的飞速发展,DeepSeek 等语言模型在众多领域得到了广泛应用。然而,在春节这段时间的使用过程中,用户常常遭遇服务器繁忙的问题,这不仅影响了用户的使用体验,也在一定程度上限制了模型的推广和…...
《手札·开源篇》数字化转型助力永磁电机企业降本增效:快速设计软件如何让研发效率提升40%?
数字化转型助力永磁电机企业降本增效:快速设计软件如何让研发效率提升40%? 一、痛点:传统研发模式正在吃掉企业的利润 永磁电机行业面临两大挑战: 研发周期长:一款新电机从设计到量产需6-12个月,电磁计算…...
飞算JavaAI :AI + 时代下的行业趋势引领者与推动者
在科技飞速发展的当下,AI 时代正以前所未有的速度重塑着各个行业的格局,而软件开发领域更是这场变革的前沿阵地。在众多创新力量之中,飞算JavaAI 脱颖而出,宛如一颗璀璨的新星,凭借其独树一帜的特性与强大功能&#x…...
【重新认识C语言----结构体篇】
目录 -----------------------------------------begin------------------------------------- 引言 1. 结构体的基本概念 1.1 为什么需要结构体? 1.2 结构体的定义 2. 结构体变量的声明与初始化 2.1 声明结构体变量 2.2 初始化结构体变量 3. 结构体成员的访…...
解决错误:CondaHTTPError: HTTP 000 CONNECTION FAILED for url
解决错误:CondaHTTPError: HTTP 000 CONNECTION FAILED for url 查看channels:vim ~/.condarcshow_channel_urls: true channels:- http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/- http://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/…...
使用令牌桶算法通过redis实现限流
令牌桶算法是一种常用的限流算法,它可以平滑地控制请求的处理速率。在 Java 中结合 Redis 实现令牌桶算法,可以利用 Redis 的原子操作来保证多节点环境下的限流效果。 一 实现思路 初始化令牌桶:在 Redis 中存储令牌桶的相关信息࿰…...
Docker的进程和Cgroup概念
Docker的进程和Cgroup概念 容器里的进程组织或关系0号进程:containerd-shim1号进程:容器内的第一个进程进程收到信号后的三种反应两个特权信号在容器内执行 kill 命令的行为 Cgroup 介绍CPU Cgroup 中与 CFS 相关的参数Kubernetes 中的资源管理memory cg…...
Day68:类的多态
在面向对象编程(OOP)中,多态(Polymorphism)是指不同类的对象对同一消息作出响应的能力。换句话说,多态允许不同类的对象使用相同的方法名,但实现不同的行为。多态是通过继承和方法重写来实现的,通常可以分为方法重写和接口重载。 在 Python 中,多态常常通过方法重写来…...
一种解决SoC总线功能验证完备性的技术
1. 前言 通过总线将各个IP通过总线连接起来的SoC芯片是未来的大趋势,也是缩短芯片开发周期,抢先进入市场的常用方法。如何确保各个IP是否正确连接到总线上,而且各IP的地址空间分配是否正确,是一件很棘手的事情。本文提出了一种新…...
【Linux系统】线程:线程库 / 线程栈 / 线程库源码阅读学习
一、线程库 1、线程库介绍:命名与设计 命名:线程库通常根据其实现目的和平台特性进行命名。例如,POSIX标准定义了Pthreads(POSIX Threads),这是一个广泛使用的线程库规范,适用于多种操作系统。此…...
深度剖析 Redis:缓存穿透、击穿与雪崩问题及实战解决方案
一、缓存基本使用逻辑 在应用程序中,为了提高数据访问效率,常常会使用缓存。一般的缓存使用逻辑是:根据 key 去 Redis 查询是否有数据,如果命中就直接返回缓存中的数据;如果缓存不存在,则查询数据库&#…...
如何使用el-table的多选框
对el-table再次封装,使得功能更加强大! 本人在使用el-table时,因为用到分页,导致上一页勾选的数据在再次返回时,没有选中,故在原有el-table组件的基础之上再次进行了封装。 1.首先让某些不需要勾选的列表进…...
【工具变量】上市公司企业渐进式创新程度及渐进式创新锁定数据(1991-2023年)
测算方式: 参考顶刊《经济研究》孙雅慧(2024)老师的做法,用当期创新和往期创新的内容重叠度作为衡量渐进式创新程度的合理指标。通过搜集海量专利摘要,测算当前专利申请和既有专利的内容相似度,反映企业在…...
LM Studio 部署本地大语言模型
一、下载安装 1.搜索:lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理,否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…...
嵌入式工程师面试经验分享与案例解析
嵌入式工程师岗位受到众多求职者的关注。面试流程严格,技术要求全面,涵盖C/C编程、数据结构与算法、操作系统、嵌入式系统开发、硬件驱动等多个方向。本文将结合真实案例,深入剖析嵌入式工程师的面试流程、常见问题及应对策略,帮助…...
