Shader 中的光源

1、Shader 开发中常用的光源属性

Unity当中一共支持四种光源类型:

  • 平行光(Directional)
  • 点光源(Point)
  • 聚光灯(Spot)
  • 面光源(Area)— 面光源仅在烘焙时有用

不管光源类型到底是什么,我们在Shader开发当中经常会使用到的光源相关属性有:
位置、方向、颜色、强度、衰减
也就是说我们在Shader中处理光照效果时,经常会用到这些光的属性参与到计算当中

2、对比平行光、点光源、聚光灯

2.1 平行光

充当角色:太阳
照射范围:无限制
特点:

  1. 它不存在固定的位置
  2. 它的重要属性只有方向(可以通过Transform的Rotation属性来改变方向)
  3. 它到场景中所有点的方向都是一样的
  4. 由于它没有位置,因此它没有衰减的概念(光的强度不会随着距离而发生变化

2.2 点光源

充当角色:灯泡、烛光 等
照射范围:有限
特点:

  1. 它的光是由一个点发出的,向四面八方延伸的光
  2. 它的范围由参数Range来决定
  3. 它的位置由Transform中的Position来决定
  4. 它存在衰减,随着物体离点光源距离决定衰减强弱

2.3 聚光灯

充当角色:探照灯、手电筒 等
照射范围:有限
特点:

  1. 它的光范围由空间中的一块锥形区域定义
  2. 它的范围由参数Range和Spot Angle 共同决定
  3. 它的位置由Transform中的Position来决定
  4. 它存在衰减,随着物体离聚光灯距离决定衰减强弱。

但是它相对点光源衰减计算公式更复杂,因为需要点是否在锥形范围内

3、判断光源类型

Unity中提供了三个重要的宏,分别是:

  • _DIRECTIONAL_LIGHT - 平行光
  • _POINT_LIGHT - 点光源
  • _SPOT_LIGHT - 聚光灯
#if defined(_DIRECTIONAL_LIGHT)平行光逻辑
#elif defined (_POINT_LIGHT)点光源逻辑
#elif defined (_SPOT_LIGHT)聚光灯逻辑
#else其他逻辑
#endif

4、光照衰减

光照衰减通常指的是在渲染过程中考虑光线在空间中传播时的减弱效应,比如:任何光源的光照亮度会随着物体离光源的距离增加而迅速衰减。一般常见的光照衰减计算方式有

  1. 线性衰减:光强度与距离成线性关系。即光照衰减与光源到被照射表面的距离成正比。
  2. 平方衰减:光强度与距离的平方成反比。这种模型更符合现实世界中光照的特性,因为光在空间中的传播过程中通常会遵循平方衰减规律。

4.1 Unity中的光照衰减

Unity中为了提升性能,我们一般不会直接通过数学公式计算光照衰减,而是使用一张纹理作为查找表(LUT, lookup table) 在片元着色器中计算逐像素光照的衰减

Unity Shader中有一个内置的纹理类型的变量 _LightTexture0,该纹理中存储了衰减值相关的数据,Unity 内部预先就计算好了相关数据 并存入到该纹理中,避免重复计算,提升性能表现
其中它的对角线上的纹理颜色值,表明了光源空间中不同位置的点对应的衰减值
纹理中的对角线,起点(0, 0) 位置,表示和光源重合的点的衰减值;终点(1,1) 位置,表示在光源空间中离光源距离最远的点的衰减值

一般我们直接从 _LightTexture0 中进行纹理采样后,利用其中的 UNITY_ATTEN_CHANNEL 宏来得到衰减值所在的分量:

tex2D(_LightTexture0, 对应纹理uv坐标).UNITY_ATTEN_CHANNEL

注意:如果光源存在cookie,也就是灯光遮罩,那么衰减查找纹理便是 _LightTextureB0

4.2 光源空间变换矩阵

Unity Shader中 内置的 光源空间变换矩阵是用于将世界空间下的位置转换到光源空间下(光源位置为原点)

  • 老版本:_LightMatrix0
  • 新版本:unity_WorldToLight

由于我们需要从 _LightTexture0 光照纹理中取出对应的衰减数据,因此我们需要将顶点位置从世界空间中转换到光源空间中,然后再来从其中取得衰减数据,我们可以通过矩阵运算将世界空间下的点转换到光源空间下

mul(unity_WorldToLight, float4(worldPos, 1));

4.3 点光源的衰减计算

注意:一般点光源不会为其添加cookie光照遮罩,一般想要使用光照遮罩都会在聚光灯中使用

步骤:

(1)将顶点从世界空间转换到光源空间

float3 lightCoord = mul(unity_WorldToLight, float4(worldPos, 1)).xyz;

lightCoord 是光源坐标系下顶点根据光源的范围range规范化后的坐标,相当于是一个模长为0~1之间的向量

(2)利用该光源空间下的坐标来计算离光源的距离,并利用距离参数,从光源纹理中采样

fixed atten = tex2D(_LightTexture0, dot(lightCoord,lightCoord).xx).UNITY_ATTEN_CHANNEL;

dot(LightCoord, LightCoord).xx 中dot(LightCoord, LightCoord) 是为了通过点乘得到,结果 x² +y² +z² = 离光源距离 distance²
xx是一种特殊写法,目的是构建一个 float2 代表uv坐标,这里的 uv坐标 相当于是(distance², distance²),用distance²做uv坐标,而不是distance

  • 为了避免开平方带来性能消耗
  • 采用这种平方衰减更符合现实世界中光照的特性

因为人眼对亮部不敏感,而对暗部敏感,这样我们就可以将 衰减值的精度 集中在比较远的地方
(distance是0.5时,distance2是0.25,这样LUT查找表中大部分值都会留给比较远的部分)

4.4 聚光灯衰减计算

灯光组件中有一个Cookie参数,是用来关联光照遮罩图片的,对于平行光和点光源,默认是不会提供任何光照遮罩信息的,但是对于聚光灯来说,Unity会默认为它提供一个Cookie光照遮罩,主要是用于模拟聚光灯的区域性,而此时光照纹理中

  • _LightTexture0 存储的是Cookie纹理信息
  • _LightTextureB0 存储的是光照纹理信息,里面包含衰减值

因此,获取聚光灯衰减值时,需要从_LightTextureB0中进行采样;获取遮罩范围相关数据时,需要从_LightTexture0中进行采样

(1)将顶点从世界空间转换到光源空间

 float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));

注意:这里我们转换后和点光源不同的是,点光源只会获取其中的xyz,而聚光灯会获取其中的xyzw,这是因为在聚光灯光源空间下的w值有特殊含义,会参与后续的计算

(2)利用光源空间下的坐标信息
我们会通过3个步骤去获取聚光灯的衰减信息
 

fixed atten = (lightCoord.z > 0) * //第一步tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * //第二步tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL; //第三步

我们首先分析和点光源相同的部分——第三步

tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;

这一步的规则和点光源规则一致,直接根据距离光源原点距离的平方从光照纹理中获取衰减值
需要注意的是

  • 聚光灯的光照衰减纹理为_LightTextureB0
  • dot函数只会计算xyz,w不会计算

接着我们来分析用于进行范围判断的部分——第一、二步

第一步:(lightCoord.z > 0)
CG语法中没有显示的bool类型,一般情况下 0 表示false,1表示true,也就是说lightCoord.z > 0的返回值,条件满足时为1,条件不满足为0
这里的z代表的其实是 目标点 相对于 聚光灯照射面 距离,如果 lightCoord.z <= 0 证明在聚光灯照射方向的背面,就不应该受到聚光灯的影响, 也就是说这一步的主要作用,是用来决定顶点是否受到聚光灯光照的影响

第二步:tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w
我们以前在进行纹理采样时都会进行一个 先缩放 后 平移 的操作,比如:

uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;

而第二步中的 lightCoord.xy / lightCoord.w + 0.5 其实也是在做这样的一个操作,这样做的主要目的是因为:
我们需要把uv坐标映射到0~1的范围内再从纹理中采样,lightCoord.xy / lightCoord.w 进行缩放后 x, y的取值范围是-0.5~0.5之间,再加上0.5后,x,y的取值范围就是0~1之间,便可以进行正确的纹理采样了,而 lightCoord.xy / lightCoord.w 是因为聚光灯有很多横截面,我们需要把各横截面映射到最大的面上进行采样

总结一下:看似复杂的聚光灯光照衰减计算方式,其实就是由 “遮罩衰减” 和 距离衰减 共同决定的

  • 第一步:判断是否能有机会照到光 看得到为1,看不到为0:fixed atten = (lightCoord.z > 0) 
  • 第二步:缩放平移,映射到遮罩纹理采样 根据遮罩纹理的信息决定衰减叠加:tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w *
  • 第三步:从光照衰减纹理中取出按距离得到的衰减值:tex2D(_LightTextureB0,dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;

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

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

相关文章

通过MCGS在ARMxy边缘计算网关上实现物流自动化

随着电子商务和智能制造的快速发展&#xff0c;物流行业面临着前所未有的挑战与机遇。高效的物流系统不仅可以加快货物周转速度&#xff0c;降低运营成本&#xff0c;还能显著提升客户满意度。 1. ARMxy BL340系列简介 ARMxy BL340系列是针对工业自动化领域设计的一款高性能、…

2024年最新苹果cms升级插件【泛目录专用】

苹果CMS是一款专为视频内容管理而设计的系统&#xff0c;近年来在视频站点搭建中逐渐成为热门选择。其直观的用户界面和灵活的管理功能&#xff0c;使得无论是新手还是专业开发者都能轻松上手。 苹果CMS提供了多种主题和模板&#xff0c;用户可以根据自身需求进行定制&#xf…

北京买新能源车,天津上牌攻略

背景说明 我是在北京买的新能源汽车&#xff08;增程式&#xff09;&#xff0c;因没有摇上北京车牌号&#xff0c;所以计划在天津上牌。前期问了一些代理&#xff0c;要是帮忙弄的话得花500元左右&#xff0c;要是自己搞定的话&#xff0c;大约150元左右&#xff08;130元的车…

计算机毕业设计Spark+Hive旅游景点推荐 旅游推荐系统 景区游客满意度预测与优化 Apriori算法 景区客流量预测 旅游大数据 景点规划

流程&#xff1a; 1.DrissionPage自动化爬虫框架采集旅游数据约10万条存入mysql数据库、.csv文件作为数据集(旅游数据、用户数据、评论数据)&#xff1b; 2.使用pandasnumpy或MapReduce对数据进行数据清洗&#xff0c;生成最终的.csv文件并上传到hdfs(含nlp情感分析)&#xff1…

【每日刷题】Day127

【每日刷题】Day127 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 349. 两个数组的交集 - 力扣&#xff08;LeetCode&#xff09; 2. LCR 022. 环形链表 II - 力扣&a…

软设9.20

1 已知一个文件中出现的各字符及其对应的频率如下表所示。若采用定长编码&#xff0c;则该文件中字符的码长应为()。若采用Hufman编码&#xff0c;则字符序列“face”的编码应为()。 1.&#xff08;&#xff09; A.2 B.3 C.4 D.5 2.&#xff08;&#xff09; A.110001001101…

工程师 - PFM介绍

在电子电路设计中&#xff0c;PFM&#xff08;Pulse Frequency Modulation&#xff0c;脉冲频率调制&#xff09;是一种调制技术&#xff0c;其主要特点是在负载变化时调整脉冲的频率&#xff0c;而保持脉冲的宽度&#xff08;时间长度&#xff09;相对恒定。与PWM&#xff08;…

详解Vue事件总线的原理与应用:EventBus

Vue 事件总线 - 组件通信的桥梁 引言 在 Vue.js 开发中&#xff0c;组件通信是一个重要的话题。Vue 提供了多种方式来实现不同组件之间的通信&#xff0c;譬如Props、 $emit、Ref实例、Vuex状态管理及事件总线等等&#xff0c;可谓是五花八门&#xff0c;它们之间使用各有优缺…

4款音频转文字在线转换工具帮你解锁新的记录模式。

越来越多的人都知道使用一些工具来将音频直接转换成文字&#xff0c;这样便省去了手动输入的麻烦。而且使用音频进行记录也能够提高工作的效率&#xff0c;像会议记录&#xff0c;课堂教学记录&#xff0c;采访录音等。如果大家有需要将自己的音频转成文字&#xff0c;可以试试…

STM32 使用 CubeMX 实现按键外部中断

目录 问题背景知识参考需要改什么注意尽量不要在中断函数使用 循环函数做延时中断函数中延时方法调试 问题 我想实现按钮触发紧急停止类似功能&#xff0c;需要使用按键中断功能。 背景知识 GPIO 点亮 LED。stm32cubemx hal学习记录&#xff1a;GPIO输入输出。STM32—HAL库 …

[c++进阶(八)]STL容器适配器之queue

1.前言 和stack一样&#xff0c;队列也没有把他放在容器的一栏里面&#xff0c;而是把他放在容器适配器的一栏。这也是因为queue是使用了别人的相关接口&#xff0c;空间然后来封装自己的内容&#xff0c;最后再给上层用户使用。 2.队列 队列的性质就是先进先出&#xff0c;他…

黑科技网址推荐:特殊功能的工具网址

1、【网站】WebRTC—— 点对点网络摄像头实时监控 &#xff1b; 网址&#xff1a;https://webcamera.cc/zh 先连接摄像头&#xff0c;将作为摄像头的设备进入摄像头页面&#xff0c;输入连接ID&#xff0c;点连接。 监控端进入监控页面&#xff0c;填入与摄像头相同的连接ID&…

Java中ArrayList和LinkedList的比较

注&#xff1a;Joshua Bloch 就是 LinkedList 的作者 在Java中&#xff0c;ArrayList和LinkedList都是常用的列表实现类&#xff0c;它们都实现了List接口&#xff0c;但在内部工作原理和性能方面有显著差异。 ArrayList&#xff1a;基于动态数组实现。随着元素的增加&#x…

spark之不同序列化对比

一&#xff0c;spark的rdd的序列话不同介绍 下面是使用不同序列化后的占用资源和数据大小 2&#xff0c;sparksql中序列化的区别 sparksql中使用序列化和不使用差别不大&#xff0c;英文sparksql中默认使用了encode自己实现的序列化方法&#xff0c;加上与不加序列化差别不大…

C++ day03

思维导图 头文件 #ifndef SEQLIST_H #define SEQLIST_Husing datatype int;class seqlist { private:datatype *ptr; // 动态数组指针int size; // 顺序表最大容量int len 0; // 当前长度public:void init(int n); // 初始化顺序表bool empty(); …

RflySim工具链常见问题答疑

1. RflySim结合硬件能不能实现无人机颜色巡线呢&#xff1f; 可以&#xff0c;内置有一个通过相机识别来攻击小球的实验&#xff0c;可见&#xff1a;【RflySim安装路径】\RflySimAPIs\8.RflySimVision\1.BasicExps\1-VisionCtrlDemos\e3_ShootBall&#xff0c;不过要想实现无人…

elasticsearch同步mysql方案

文章目录 1、1. 使用数据库触发器2. 使用定时任务3. 监听MySQL二进制日志&#xff08;binlog&#xff09;4. 使用数据管道5. 使用第三方工具或服务6. 编写自定义脚本注意事项 2、1. 使用Logstash步骤&#xff1a;示例配置&#xff1a; 2. 使用Debezium步骤&#xff1a; 3. 自定…

ES6标准---【九】【学习ES6标准看这一篇就够了!!!】

目录 以往ES6文章 JavaScript在浏览器中的加载 传统方法 加载规则 注意 顶部变量外部不可用 this关键字返回undefined JavaScript的循环加载 ES6模块的循环加载 块级作用域 let取代var 全局变量和线程安全 以往ES6文章 ES6标准---【一】【学习ES6看这一篇就够了&…

小小扑克牌算法

1.定义一个扑克牌类Card&#xff1a; package democard; public class Card {public String suit;//表示花色public int rank;//表示牌点数Overridepublic String toString() {return "{"suit rank"}";}//实例方法&#xff0c;初始化牌的点数和花色public…

【Redis入门到精通三】Redis核心数据类型(List,Set)详解

目录 Redis数据类型 ​编辑 1.List类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 2.Set类型 &#xff08;1&#xff09;常见命令 &#xff08;2&#xff09;内部编码 Redis数据类型 查阅Redis官方文档可知&#xff0c;Redis提供给用户的核…