URP源码学习(四)光照

2023-11-09

光照可以分两部分来看,一个是对光源的处理,主要逻辑在C#代码ForwardLights类,一个是shader的计算,核心是Lighting.hlsl文件。

先看看光源的一些设置

在管线设置

主光

  • 2个选项,关闭,逐像素。
  • 只支持平行光,选择亮度最大的一个,在GetMainLightIndex函数获取,没有返回-1。
  • 可选是否产生阴影以及阴影贴图分辨率。

附加光

  • 3个选项,关闭,逐顶点,逐像素。
  • 逐顶点不能产生阴影。
  • 每个物体受附加光影响有数量限制,PC上最大8个。
  • 选逐像素可以产生阴影并设置阴影贴图分辨率。
  • 点光源和平行光没有实时阴影,聚光灯选逐像素有。

UniversalAdditionalLightData,看名字是设置光源的属性,实际影响的参数只有shadowBias和shadowNormalBias,感觉这名字起的有点误导,不需要手动添加,如果在light里对Bias选择Custom,则会自动添加。


ForwardLights源码理解

这个类的主要作用就是处理光源,提交光照数据给GPU使用

首先定义了一个类LightConstantBuffer,将shader变量的id封装了一下,基本上就是坐标,颜色,强度。

public static int _MainLightPosition; 
public static int _MainLightColor;  
public static int _AdditionalLightsCount; 
public static int _AdditionalLightsPosition; 
public static int _AdditionalLightsColor; 
public static int _AdditionalLightsAttenuation; 
public static int _AdditionalLightsSpotDir;  
public static int _AdditionalLightOcclusionProbeChannel; 

这里有一个StructuredBuffer的概念,在7.2版本的URP没有开启,官方的注释原因是有性能问题,vulkan有绑定的问题。以后可以开启了再细看。

核心函数Setup,作用是设置相关shader变量值,这部分看着代码不少,实际就是根据光源类型,设置不同数值。要注意的是现在不支持shadowmask,后续版本应该会有支持。


shader部分

可以说C#只是设置了一些参数,真正的计算都是在shader完成的,URP实现了一个简化版的PBR,基于Minimalist CookTorrance BRDF,算法详细信息在http://www.thetenthplanet.de/archives/255。下面分成几个部分,看看shader代码是怎么实现的。

shader可以分两部分来看,一个是底层运算库(hlsl文件),一个组合这些底层运算,实现不同效果需求。

先看看输入

输入分成几个文件,依次看一下

SurfaceInput.hlsl

  • 看名字就是保存表面颜色的数据,首先定义了base、bump、emission贴图和采样器,URP定义材质的方法有点改变,要分别定义TEXTURE2D和SAMPLER,具体里边咋实现的也没查着,反正按格式写就行了。
  • SurfaceData结构体,只定义了数值没有贴图。
  • Alpha函数,封装了alpha的计算,如果开启ALPHATEST_ON,执行clip,然后返回alpha值。
  • 接着是封装base、bump、emission3个采样函数。

UnityInput.hlsl

  • 上边一些宏定义都是VR相关的,做手游的就不看了。
  • 时间相关的一些参数,在通过管线设置。
  • 相机世界坐标,_ProjectionParams,_ScreenParams,_ZBufferParams,unity_OrthoParams,有些在不同宏定义下,数据不同。
  • camera相关的矩阵,各种坐标变换矩阵。这部分会在Input.hlsl封装成宏定义。
  • 一些全局值
real4 glstate_lightmodel_ambient; 
real4 unity_AmbientSky; 
real4 unity_AmbientEquator; 
real4 unity_AmbientGround; 
real4 unity_IndirectSpecColor; 
float4 unity_FogParams; 
real4  unity_FogColor; 
real4 unity_ShadowColor; 
  • 运行时用到的贴图声明
// Unity specific
TEXTURECUBE(unity_SpecCube0);
SAMPLER(samplerunity_SpecCube0);

// Main lightmap
TEXTURE2D(unity_Lightmap);
SAMPLER(samplerunity_Lightmap);
// Dual or directional lightmap (always used with unity_Lightmap, so can share sampler)
TEXTURE2D(unity_LightmapInd);

// We can have shadowMask only if we have lightmap, so no sampler
TEXTURE2D(unity_ShadowMask);
  • 其他代码就不贴了,具体用到再回来查。

Input.hlsl

  • 上边是一堆宏定义,作用就是定义最大可见光数量,分为SSBO和UBO,由于硬件限制,SSBO短期应该不能应用在是手机上,所以现在可以简单理解为可见光就是32个,反正就是个数量,以后是会越来越多的 。
  • InputData结构体,这是像素shader用到的数据,要注意的是这并不是顶点shader返回的那个数据,URP用到的几个地方,都是先进行了一下数据的处理,比如LitForwardPass里的InitializeInputData函数。
全局的颜色
half4 _GlossyEnvironmentColor;
half4 _SubtractiveShadowColor;

float4x4 _InvCameraViewProj;
float4 _ScaledScreenParams;
  • 之后定义了主光和附加光源的各种属性,通过ForwardLights设置。

Lighting.hlsl,实现光照计算

如果没定义LIGHTMAP_ON,GI通过球谐函数计算,在顶点处理以减轻shader ALU压力。

PBR的核心函数是UniversalFragmentPBR,下面详细看一下这个函数的流程。

BRDFData brdfData;
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData);
  • 首先填充BRDFData结构体,包含brdf需要的参数,漫反射、高光、光滑度等值。
    • InitializeBRDFData这个函数根据工作流,设置高光和漫反射值。
Light mainLight = GetMainLight(inputData.shadowCoord); 
  • 然后通过GetMainLight获取主光的数据
    • 主要是方向和颜色。
    • distanceAttenuation默认是unity_LightData.z,unity_LightData.z值如果被裁剪是1,否则是0,如果开启lightmap或在subtractive模式下需要乘上unity_ProbesOcclusion.x。
    • shadowAttenuation用于计算阴影,默认是1,MainLightRealtimeShadow函数用来计算实时阴影的,具体逻辑在阴影部分细看。
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0));
void MixRealtimeAndBakedGI(inout Light light, half3 normalWS, inout half3 bakedGI, half4 shadowMask)
#if defined(_MIXED_LIGHTING_SUBTRACTIVE) && defined(LIGHTMAP_ON)
    bakedGI = SubtractDirectMainLightFromLightmap(light, normalWS, bakedGI);
#endif
  • 这个函数的作用是计算bakedGI的值,在定义了Subtractive同时开启lightmap时生效,原因是这一个点的颜色在烘焙时已经计算了主光源的光照和阴影值,避免和实时光再叠加一次,选择bake和实时光更暗的那个值作为最终值。
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS); 
  • 根据brdfData、烘焙GI值、遮蔽值、法线、观察方向,计算GI,也就是所有的间接光颜色。计算分为以下几个部分:
    • 先计算菲涅尔值。间接反射= bakedGI * occlusion
    • GlossyEnvironmentReflection函数计算间接高光,如果开启了环境反射,就采样unity_SpecCube0,然后处理HDR,返回最终值。unity_SpecCube0保存了环境的立体贴图(天空盒和后面的反射探针形成的立体贴图,都算在这里),这个贴图有mipmap。
    • ps:unity_SpecCube1,存储的离物体最近的反射探针的数据,这里没用到。
    • EnvironmentBRDF:看名字是计算环境的BRDF,具体算法也没看懂,不知道啥原理,可能是个经验算法吧,反正能算出个颜色值就对了。
color += LightingPhysicallyBased(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS); 
  • 这个函数是计算主光的brdf值,调用DirectBDRF函数,算法基于Minimalist CookTorrance BRDF。反正BRDF也就是DNF那3个函数,具体细节吧,相信unity就对了,反正我是看不懂~。
for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
{
    Light light = GetAdditionalLight(lightIndex, inputData.positionWS);
    color += LightingPhysicallyBased(brdfData, light, inputData.normalWS, inputData.viewDirectionWS);
}
  • 接着是计算附加光的颜色值,在一个for循环处理,也就是之前说的单pass渲染,不像内置管线那样有forward add pass了。
  • 计算和主光差不多,但是没有间接光的计算,
#ifdef _ADDITIONAL_LIGHTS_VERTEX
    color += inputData.vertexLighting * brdfData.diffuse;
#endif
color += emission;
  • 最后一步是加上顶点光照,和自发光颜色。

到这PBR的光照计算就结束了,有很多细节略过了,以后有更深的理解再补上。

最后一步,LitForwardPass.hlsl

这个文件实现了顶点和像素shader,定义了输入输出结构体,PBR的输入参数大致都差不多,就不贴代码了。

LitPassFragment也比较简单,首先定义一些数据,并初始化,调用UniversalFragmentPBR做光照计算,然后加上雾的颜色,就是最终颜色。


光照相关的代码和shader大致看了一遍,核心内容就是光照计算的流程,URP实现的是一个简化版的计算,这部分项目中实际要改的应该不多,设置好各种宏就可以了,光照算法也不是我这个水平能改的了的,会用就行~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

URP源码学习(四)光照 的相关文章

  • pnPm——比npm&yarn更胜一筹的包管理器

    官网 Fast disk space efficient package manager pnpm 安装 在 POSIX 系统上 即使没有安装 Node js 也可以使用以下脚本安装 pnpm curl fsSL https get pnp
  • 常用的编译器(不限制编程语言、不限制平台)

    在提到这个问题之前我们应该了解编译器是什么 简单来说 编译器就是将一种语言 通常为高级语言 翻译为另一种语言 通常为低级语言 的程序 一个现代编译器的主要流程有 源代码 gt 预处理器 gt 编译器 gt 目标代码 gt 链接器 gt 可执
  • Spring Cloud Hystrix

    服务容错保护 Spring Cloud Hystrix 在微服务的架构中 存在着很多的服务单元 若一个单元出现故障 就很容易因依赖关系而引发故障蔓延 最终导致整个系统瘫痪 这样的架构相较传统的架构更加不稳定 为了解决这样的问题 产生了断路器

随机推荐

  • Java IO流(FileReader,FileWriter)讲解

    FileReader和FileWriter类是用来读取 写入字符文件的便捷类 使用FileReader FileWriter 可以实现文本文件的复制 对于非文本文件 视频文件 音频文件 图片 只能使用字节流 字符输入流 FileReader
  • Linux脚本练习之script061-输出7的倍数

    script061 题目 题目来源于 SHELL3 输出7的倍数 写一个 bash 脚本以输出数字 0 到 500 中 7 的倍数 0 7 14 21 的命令 脚本一 seq 命令可以输出数字序列 请参考 Linux命令之产生序列化数seq
  • linux之sed用法

    sed是一个很好的文件处理工具 本身是一个管道命令 主要是以行为单位进行处理 可以将数据行进行替换 删除 新增 选取等特定工作 下面先了解一下sed的用法 sed命令行格式为 sed nefri command 输入文本 常用选项 n 使用
  • datagridview数据清空

    处理关于datagridview数据清空问题 尝试后总结 1 程序dt值为空 DataTable dt DataTable dataGridView1 DataSource dt Rows Clear dataGridView1 DataS
  • redis中hashtable 的 rehash/ resizing 策略

    依赖于连续存储的数据结构 具体的 就是依赖array 都有一个resizing问题 对于vector 一般的策略就是满的时候double and copy 降到1 4时候 halve and copy Hashtable也可以这么做 均摊的
  • cloudify学习小结

    参考 http docs getcloudify org 3 4 0 installation from packages http docs getcloudify org 3 4 0 manager bootstrapping clou
  • 【财富空间】卡耐基梅隆首席科学家大卫·伯恩:机器人学与商业机遇

    本文转载自中国人工智能学会 ID CAAI 1981 2017年12月11日 国际知名机器人专家 美国卡耐基梅隆大学机器人研究所所长马歇尔 赫伯特 Martial Hebert 教授和首席科学家大卫 伯恩 David Bourne 教授访问
  • 用animation实现弹框或图片的淡入淡出效果

    CSS部分 skin modal body dl float left padding 5px animation skin 2s keyframes skin 0 opacity 0 25 opacity 0 5 50 opacity 1
  • CCF-CSP 202206-3 角色授权 Java满分题解

    严格按照题意模拟 User类type字段表示用户 用户组 使用一个Map存User所对应的role列表先实现通过role来鉴权 再遍历role列表即可 import java util class Role String roleName
  • mysql之常见函数(单行函数)09

    1 常见函数 单行函数 进阶4 常见函数 这里代表单行函数 概念 类似于java的方法 将一组逻辑语句封装在方法体中 对外暴露方法名 好处 1 隐藏了实现细节 2 提高代码的重用性 调用 select 函数名 实参列表 from 表 特点
  • MFC中实现用DC画线

    void CDrawView OnLButtonDown UINT nFlags CPoint point TODO Add your message handler code here and or call default Messag
  • 9-组件扫描(注解开发)

    组件扫描 component scanning Spring 能够从 classpath 下自动扫描 侦测和实例化具有特定注解的组件 特定组件包括 Component 基本注解 标识了一个受 Spring 管理的组件 Respository
  • 代码审计之若依系统

    文章目录 前言 一 本地项目部署 二 漏洞挖掘 1 整理思路 2 shiro反序列化漏洞 2 SQL注入 3 Thymeleaf模板注入 4 SnakeYaml 反序列化 5 Druid未授权访问 6 swagger ui html接口文档
  • pandas 读取某一单元格的值_pandas读取表格后的常用数据处理操作

    关键时刻 第一时间送达 本文已获原作者授权 欢迎分享转发 今天给大家讲讲pandas读取表格后的一些常用数据处理操作 这篇文章其实来源于自己的数据挖掘课程作业 通过完成老师布置的作业 感觉对于使用python中的pandas模块读取表格数据
  • HLS-M3U8流媒体视频加密KEY介绍以及平台案例!

    首先介绍M3U8 M3U M3U8其实是 HTTP Live Streaming 缩写为 HLS 协议的部分内容 而 HLS HTTP Live Streaming 是Apple的动态码率自适应技术 主要用于PC和Apple终端的音视频服务
  • IJCV2021 人脸关键点检测器PIPNet

    阿联酋起源人工智能研究院 IIAI 科学家提出了一种新颖的人脸关键点检测方法PIPNet 通过融合坐标回归和热力图回归的优势 并结合半监督学习充分利用大量无标注数据提升跨域的泛化性能 最终得到一个又快又准又稳的人脸关键点检测器 相关论文已被
  • 关于云计算存储虚拟化技术三个层次上的实现

    关于云计算存储虚拟化技术三个层次上的实现 随着企业的成长 业务和应用不断增加 IT 系统规模日益庞大 带来高能耗 数据中心空间紧张 IT 系统总体拥有成本过高等问题 而现有服务器 存储系统等设备又没有充分被利用起来 资源极度浪费 IT基础架
  • Xposed框架安装、使用及插件开发

    往期推荐 HOOK startActivity HOOK框架 动态代理 HOOK框架 静态代理 实战案列分析 Crakeme01 需要相关资料的朋友 可以 加入此处即可打包获取 Xposed框架是一款可以在不修改APK的情况下影响程序运行
  • 医学图像DICOM文件解析——DICOM内部信息详解篇

    文章目录 一 医学影像学介绍 二 DICOM文件简介 三 DICOM内部信息详解 DICOM Tag与VR 1 常见的TAG 1 Patient Tag 2 Study Tag 3 Series Tag 4 Image Tag 2 VR数据
  • URP源码学习(四)光照

    光照可以分两部分来看 一个是对光源的处理 主要逻辑在C 代码ForwardLights类 一个是shader的计算 核心是Lighting hlsl文件 先看看光源的一些设置 在管线设置 主光 2个选项 关闭 逐像素 只支持平行光 选择亮度