Shader 中的光源
1、Shader 开发中常用的光源属性
Unity当中一共支持四种光源类型:
- 平行光(Directional)
- 点光源(Point)
- 聚光灯(Spot)
- 面光源(Area)— 面光源仅在烘焙时有用
不管光源类型到底是什么,我们在Shader开发当中经常会使用到的光源相关属性有:
位置、方向、颜色、强度、衰减
也就是说我们在Shader中处理光照效果时,经常会用到这些光的属性参与到计算当中
2、对比平行光、点光源、聚光灯
2.1 平行光
充当角色:太阳
照射范围:无限制
特点:
- 它不存在固定的位置
- 它的重要属性只有方向(可以通过Transform的Rotation属性来改变方向)
- 它到场景中所有点的方向都是一样的
- 由于它没有位置,因此它没有衰减的概念(光的强度不会随着距离而发生变化
2.2 点光源
充当角色:灯泡、烛光 等
照射范围:有限
特点:
- 它的光是由一个点发出的,向四面八方延伸的光
- 它的范围由参数Range来决定
- 它的位置由Transform中的Position来决定
- 它存在衰减,随着物体离点光源距离决定衰减强弱
2.3 聚光灯
充当角色:探照灯、手电筒 等
照射范围:有限
特点:
- 它的光范围由空间中的一块锥形区域定义
- 它的范围由参数Range和Spot Angle 共同决定
- 它的位置由Transform中的Position来决定
- 它存在衰减,随着物体离聚光灯距离决定衰减强弱。
但是它相对点光源衰减计算公式更复杂,因为需要点是否在锥形范围内
3、判断光源类型
Unity中提供了三个重要的宏,分别是:
- _DIRECTIONAL_LIGHT - 平行光
- _POINT_LIGHT - 点光源
- _SPOT_LIGHT - 聚光灯
#if defined(_DIRECTIONAL_LIGHT)平行光逻辑
#elif defined (_POINT_LIGHT)点光源逻辑
#elif defined (_SPOT_LIGHT)聚光灯逻辑
#else其他逻辑
#endif
4、光照衰减
光照衰减通常指的是在渲染过程中考虑光线在空间中传播时的减弱效应,比如:任何光源的光照亮度会随着物体离光源的距离增加而迅速衰减。一般常见的光照衰减计算方式有
- 线性衰减:光强度与距离成线性关系。即光照衰减与光源到被照射表面的距离成正比。
- 平方衰减:光强度与距离的平方成反比。这种模型更符合现实世界中光照的特性,因为光在空间中的传播过程中通常会遵循平方衰减规律。
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;
相关文章:

Shader 中的光源
1、Shader 开发中常用的光源属性 Unity当中一共支持四种光源类型: 平行光(Directional)点光源(Point)聚光灯(Spot)面光源(Area)— 面光源仅在烘焙时有用 不管光源类型到…...

【django】局域网访问django启动的项目
目录 一、现象 二、django的settings.py配置 三、启动django项目 四、获取本机IP 五、局域网机器访问 前言:本机使用pycharm启动的项目,局域网其他机器访问 一、现象 django开发了接口,想给其他同志访问接口测试,无法通过I…...

【计算机组成原理】主存储器深度解析
📢博客主页:https://blog.csdn.net/2301_779549673 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! 📢本文由 JohnKi 原创,首发于 CSDN🙉 📢未来很长&#…...

docker在基础镜像上,比如rockylinux,如何配置yum仓库
在基础镜像rockylinux上 启动的容器,没有yum仓库,就执行不了一些命令 ~]docker run -itd --name linux rockylinux:8.5~]# docker exec -it linux bash /]# ifconfig bash: ifconfig: command not found/]# vim bash: vim: command not found …...

libtorch落地AI项目的一些总结
总结 1. 为啥C 写AI C 是一个非常强大的编程语言,它具有非常强大的计算能力,可以处理非常大的数据集,并且可以非常快速地完成计算。很多项目需要嵌入式部署,C 是一个非常适合的编程语言。C 可以非常快速地完成计算,并…...

ffmpeg面向对象——参数配置秘密探索及其设计模式
目录概览 0.参数配置对象流程图0.1 用到的设计模式0.2 与朴素思想的对比 1.参数传递部分1.1 AVDictionary字典容器类1.1.1 类定义及类图1.1.2 构造函数1.1.3 析构函数1.1.4 设置/读取等配置参数 1.2 参数配置实例 2.参数配置生效部分2.1参数过滤模块2.1.1 AVOption类2.1.1.1 类…...

华为eNSP使用详解
eNSP(Enterprise Network Simulation Platform)是华为提供的一款网络仿真平台,它允许用户在没有真实设备的情况下进行网络实验和学习网络技术。eNSP可以模拟各种网络设备,如交换机、路由器、防火墙等,并支持创建多种网…...

一文入门生成式AI(理解ChatGPT的原理)
一、什么是生成式AI? 以ChatGPT为代表的生成式AI,是对已有的数据和知识进行向量化的归纳,总结出数据的联合概率。从而在生成内容时,根据用户需求,结合关联字词的概率,生成新的内容。 可以这么联想&#x…...

C# 中Faker
在 C# 中,Faker 类通常用于生成模拟数据(也称为虚拟数据、测试数据),这对于开发、测试以及演示应用程序非常有用。一个流行的库叫做 Faker,它提供了一种简单的方式来生成各种随机数据。 安装 Faker 库 要使用 Faker …...

数据权限的设计与实现系列9——前端筛选器组件Everright-filter集成框架开发2
功能实现 规则转换为 SQL 片段 规则解析 首先我们来构造一个典型的规则,包括两个条件组,每个组由两个条件组成,由且与或两种逻辑关系,如下图: 然后看看生成的规则,如下: {"filt…...

鸿蒙Harmony-Next 徒手撸一个日历控件
本文将介绍如何使用鸿蒙Harmony-Next框架实现一个自定义的日历控件。我们将创建一个名为CalendarView的组件(注意,这里不能叫 Calendar因为系统的日历叫这个),它具有以下功能: 显示当前月份的日历支持选择日期显示农历日期可以切换上一月和下一月 组件…...

直播音频解决方案
音频解决方案公司具体解决的是什么样的问题?什么样的客户需要找音频方案公司?相信还是有很多人不是很了解。音频解决方案公司工作就像是为音频设备“量身定制衣服”,帮助客户解决各种音频相关的问题。无论你是音响制造商、会议设备商、耳机品…...

Git基本用法总结
设置全局用户名 git config --global user.name xxx #设置全局用户名 设置全局邮箱地址 git config --global user.email xxxxxx.com #设置全局邮箱地址 查看所有的 Git 配置,包括用户信息 git config --list #查看所有的 Git 配置,包括用户信…...

SQLite的入门级项目学习记录(四)
性能评估和测试 规划项目 1、框架选择:前端交互和线程控制用pyside,SQLite作为数据库支持。 2、预估数据量:每秒10个数据,每个月约26000000(26M)条。 3、压力测试:首先用python脚本创建一个数据…...

Docker工作目录迁移
文章目录 前言一、迁移步骤1.停掉docker服务2.创建存储目录3.迁移docker数据4.备份5.添加软链接6.重启docker服务,测试 总结 前言 安装docker,默认的情况容器的默认存储路径会存储系统盘的 /var/lib/docker 目录下,系统盘一般默认 50G&#…...

【多维动态规划】64. 最小路径和(面试真题+面试官调整后的题目)
64. 最小路径和 难度:中等 力扣地址:https://leetcode.cn/problems/minimum-path-sum/description/ 1. 原题以及解法 1.1 题目 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和…...

Web后端开发技术:RESTful 架构详解
RESTful 是一种基于 REST(表述性状态转移,Representational State Transfer)架构风格的 API 设计方式,通常用于构建分布式系统,特别是在 Web 应用开发中广泛应用。REST 是一种轻量级的架构模式,利用标准的 …...

【Fastapi】参数获取,json和query
【Fastapi】参数获取,json和query 前言giteegithub query形式json传递同步方法使用json 前言 花了半个月的时间看了一本小说,懈怠了…今天更新下fastapi框架的参数获取 gitee https://gitee.com/zz1521145346/fastapi_frame.git github https://git…...

【Node.js】初识微服务
概述 Node.js 的微服务架构是一种通过将应用程序分解为独立的、松耦合的小服务的方式进行系统设计。 每个微服务负责处理一个特定的业务功能,并且这些服务可以独立开发、部署、扩展和管理,并且可以通讯。 它的核心思想就是解耦。 微服务和微前端是类…...

React项目实战(React后台管理系统、TypeScript+React18)
### 项目地址:(线上发布) (1)别人的项目地址 gitgitee.com:zqingle/lege-react-management.git (2)我自己的项目地址 gitgitee.com:huihui-999/lege-react-management.git ### B站讲解视频地址 https://www.bilibili.com/video/BV1FV4y157Zx?p37&spm_id_frompageDrive…...

【专题】2024中国生物医药出海现状与趋势蓝皮书报告合集PDF分享(附原数据表)
原文链接:https://tecdat.cn/?p37719 出海已成为中国医药产业实现提速扩容的重要途径。目前,中国医药产业发展态势良好,创新能力不断增强,然而也面临着医保政策改革和带量集采带来的压力。政府积极出台多项政策支持医药企业出海…...

【iOS】KVC
文章目录 KVC的定义 容器类中KVC的实现 KVC设值 KVC取值 KVC使用KeyPath KVC处理异常 KVC处理设值nil异常 KVC处理UndefinedKey异常 KVC处理数值和结构体类型属性 KVC键值验证 KVC处理集合 简单集合运算符 对象运算符 KVC处理字典 KVC应用 动态地取值和设值 用…...

【2024年华为杯研究生数学建模竞赛C题】完整论文与代码
这里写目录标题 基于数据驱动下磁性元件的磁芯损耗建模一、问题重述1.1问题背景1.2问题回顾 问题分析与模型假设模型建立与求解 基于数据驱动下磁性元件的磁芯损耗建模 一、问题重述 1.1问题背景 在现代电力电子和变压器设计中,磁性元件是确保能量高效传递和系统稳…...

svn回退到以前历史版本修改并上传
svn回退到以前版本,并在以前版本上修改代码后,上传到svn库当中,如下步骤: 3、 以回退到版本号4为例:选中版本号4,右键->Revert to this version,在出现的对话框中 点击yes! 4、 5、...

fiddler抓包07_抓IOS手机请求
课程大纲 前提:电脑和手机连接同一个局域网 (土小帽电脑和手机都连了自己的无线网“tuxiaomao”。) 原理如下: 电脑浏览器抓包时,直接就是本机网络。手机想被电脑Fiddler抓包,就要把Fiddler变成手机和网络…...

Windows系统及Ubuntu系统安装Java
Java语言简介 Java是一种高级编程语言,Java语言的创始可以追溯到1990年代初,当时任职于Sun Microsystems(后来被甲骨文公司收购)的詹姆斯高斯林(James Gosling)等人开始开发一种名为“Oak”(名字来源于詹姆…...

uni-data-select 使用 localdata 传入数据出现 不回显 | 下拉显示错误的 解决方法
目录 1. 问题所示2. 正确Demo3. 下拉显示错误(Bug复现)4. 下拉不回显(Bug复现)1. 问题所示 uni-app的下拉框uni-data-select 使用 localdata 传入数据 主要总结正确的Demo以及复现一些Bug 数据不回显数据不显示下拉选项2. 正确Demo 详细的基本知识推荐阅读:uni-app中的…...

图解 TCP 四次挥手|深度解析|为什么是四次|为什么要等2MSL
写在前面 今天我们来图解一下TCP的四次挥手、深度解析为什么是四次? 上一片文章我们已经介绍了TCP的三次握手 解析四次挥手 数据传输完毕之后,通信的双方都可释放连接。现在客户端A和服务端B都处于ESTABLISHED状态。 第一次挥手 客户端A的应用进…...

DevExpress中文教程:如何将WinForms数据网格连接到ASP. NET Core WebAPI服务?
日前DevExpress官方发布了DevExpress WinForms的后续版本——将.NET桌面客户端连接到安全后端Web API服务(EF Core with OData),在本文中我们将进一步演示如何使用一个更简单的服务来设置DevExpress WinForms数据网格。 P.S:DevExpress WinForms拥有180…...

SpringBoot3核心特性-核心原理
目录 传送门前言一、事件和监听器1、生命周期监听2、事件触发时机 二、自动配置原理1、入门理解1.1、自动配置流程1.2、SPI机制1.3、功能开关 2、进阶理解2.1、 SpringBootApplication2.2、 完整启动加载流程 三、自定义starter1、业务代码2、基本抽取3、使用EnableXxx机制4、完…...