【Unity】官方文档学习-光照系统

目录

[1 前言](#1 前言)

[2 光照介绍](#2 光照介绍)

[2.1 直接光与间接光](#2.1 直接光与间接光)

[2.2 实时光照与烘焙光照](#2.2 实时光照与烘焙光照)

[2.3 全局光照](#2.3 全局光照)

[3 光源](#3 光源)

[3.1 Directional Light](#3.1 Directional Light)

[3.1.1 Color](#3.1.1 Color)

[3.1.2 Mode](#3.1.2 Mode)

[3.1.3 Intensity](#3.1.3 Intensity)

[3.1.4 Indirect Multiplier](#3.1.4 Indirect Multiplier)

[3.1.5 Shadow Type](#3.1.5 Shadow Type)

[3.1.6 Baked Shadow Angle](#3.1.6 Baked Shadow Angle)

[3.1.7 Realtime Shadows](#3.1.7 Realtime Shadows)

[3.1.7.1 Strength](#3.1.7.1 Strength)

[3.1.7.2 Resolution](#3.1.7.2 Resolution)

[3.1.7.3 Bias](#3.1.7.3 Bias)

[3.1.7.4 Normal Bias](#3.1.7.4 Normal Bias)

[3.1.7.5 Near Plane](#3.1.7.5 Near Plane)

[3.1.8 Cookie](#3.1.8 Cookie)

[3.1.9 Draw Halo](#3.1.9 Draw Halo)

[3.1.10 Flare](#3.1.10 Flare)

[3.1.11 Render Mode](#3.1.11 Render Mode)

[3.1.12 Culling Mask](#3.1.12 Culling Mask)

[3.2 Point Light](#3.2 Point Light)

[3.2.1 Range](#3.2.1 Range)

[3.2.2 Baked Shadow Radius](#3.2.2 Baked Shadow Radius)

[3.3 Spot Light](#3.3 Spot Light)

[3.3.1 Spot Angle](#3.3.1 Spot Angle)

[3.4 Area Light](#3.4 Area Light)

[3.5 注意点](#3.5 注意点)

[3.6 光照模式](#3.6 光照模式)

[3.6.1 Realtime](#3.6.1 Realtime)

[3.6.2 Baked](#3.6.2 Baked)

[3.6.3 Mixed](#3.6.3 Mixed)

[3.7 Cookies](#3.7 Cookies)

[3.7.1 启用和禁用Baked Cookies](#3.7.1 启用和禁用Baked Cookies)

[3.7.2 为内置渲染管线创建Cookie](#3.7.2 为内置渲染管线创建Cookie)

[3.8 发光材质](#3.8 发光材质)

[3.8.1 演示](#3.8.1 演示)

[3.9 环境光](#3.9 环境光)

[4 阴影](#4 阴影)

[4.1 阴影映射](#4.1 阴影映射)

[4.1.1 阴影映射是如何工作的](#4.1.1 阴影映射是如何工作的)

[4.1.2 阴影贴图分辨率](#4.1.2 阴影贴图分辨率)

[4.1.3 替换阴影贴图分辨率](#4.1.3 替换阴影贴图分辨率)

[4.2 阴影配置项](#4.2 阴影配置项)

[4.3 阴影距离](#4.3 阴影距离)

[4.3.1 设置阴影距离](#4.3.1 设置阴影距离)

[4.3.2 减少闪烁阴影](#4.3.2 减少闪烁阴影)

[4.3.2 阴影距离与阴影遮罩光照模式](#4.3.2 阴影距离与阴影遮罩光照模式)

[4.4 阴影层叠](#4.4 阴影层叠)

[4.4.1 透视混叠](#4.4.1 透视混叠)

[4.4.2 阴影层叠原理](#4.4.2 阴影层叠原理)

[4.4.3 使用阴影层叠](#4.4.3 使用阴影层叠)

[4.5 阴影问题解决方案](#4.5 阴影问题解决方案)

[4.5.1 阴影性能](#4.5.1 阴影性能)

[4.5.2 阴影暗斑](#4.5.2 阴影暗斑)

[4.5.3 光偏移设置](#4.5.3 光偏移设置)

[4.5.4 阴影平坠](#4.5.4 阴影平坠)

[4.5.5 阴影不显示](#4.5.5 阴影不显示)

[5 全局光照](#5 全局光照)

[5.1 让物体参与GI](#5.1 让物体参与GI)

[5.2 实时GI](#5.2 实时GI)

[5.2.1 演示](#5.2.1 演示)

[5.3 烘焙GI](#5.3 烘焙GI)

[5.3.1 Mixed](#5.3.1 Mixed)

[5.3.2 Mixed演示](#5.3.2 Mixed演示)

[5.3.3 Baked](#5.3.3 Baked)

[5.3.4 Baked演示](#5.3.4 Baked演示)

[5.4 Lighting窗口](#5.4 Lighting窗口)

[5.4.1 相关 API](#5.4.1 相关 API)

[5.4.2 内部包含的窗口](#5.4.2 内部包含的窗口)

[5.4.3 Scene](#5.4.3 Scene)

[5.4.3.1 Lighting Settings](#5.4.3.1 Lighting Settings)

[5.4.3.2 Realtime Lighting](#5.4.3.2 Realtime Lighting)

[5.4.3.3 Mixed Lighting](#5.4.3.3 Mixed Lighting)

[5.4.3.4 Lightmapping Settings](#5.4.3.4 Lightmapping Settings)

[5.4.3.5 Workflow Settings](#5.4.3.5 Workflow Settings)

[5.4.4 Environment](#5.4.4 Environment)

[5.4.4.1 Environment](#5.4.4.1 Environment)

[5.4.4.2 Other Settings](#5.4.4.2 Other Settings)

[5.4.5 Realtime Lightmaps](#5.4.5 Realtime Lightmaps)

[5.4.6 Baked Lightmaps](#5.4.6 Baked Lightmaps)

[5.4.7 Probe Volumes](#5.4.7 Probe Volumes)

[5.4.8 Control Area](#5.4.8 Control Area)

[6 光照设置资源](#6 光照设置资源)

[6.1 创建Asset文件](#6.1 创建Asset文件)

[6.2 将Asset设置给场景](#6.2 将Asset设置给场景)

[6.3 查看和编辑Asset的属性](#6.3 查看和编辑Asset的属性)

[6.4 默认LightingSettings数据](#6.4 默认LightingSettings数据)

[6.5 属性介绍](#6.5 属性介绍)

[6.6 Lighting Mode](#6.6 Lighting Mode)

[6.6.1 Baked Indirect](#6.6.1 Baked Indirect)

[6.6.1.1 渲染管线支持](#6.6.1.1 渲染管线支持)

[6.6.1.2 混合光行为](#6.6.1.2 混合光行为)

[6.6.1.3 阴影与运行时性能](#6.6.1.3 阴影与运行时性能)

[6.6.1.4 运行是改变光属性](#6.6.1.4 运行是改变光属性)

[6.6.2 Shadowmask](#6.6.2 Shadowmask)

[6.6.2.1 渲染管线支持](#6.6.2.1 渲染管线支持)

[6.6.2.2 Shadowmask质量设置](#6.6.2.2 Shadowmask质量设置)

[6.6.2.3 混合光行为](#6.6.2.3 混合光行为)

[6.6.2.4 阴影与运行时性能](#6.6.2.4 阴影与运行时性能)

[6.6.2.5 阴影遮罩实现详细信息](#6.6.2.5 阴影遮罩实现详细信息)

[6.6.3 Subtractive](#6.6.3 Subtractive)

[6.6.3.1 渲染管线支持](#6.6.3.1 渲染管线支持)

6.6.3.2混合光行为

[6.6.3.3 更改阴影颜色](#6.6.3.3 更改阴影颜色)

[6.6.3.4 主方向光](#6.6.3.4 主方向光)

[7 Light Explorer窗口](#7 Light Explorer窗口)

[7.1 窗口介绍](#7.1 窗口介绍)

[7.2 窗口扩展](#7.2 窗口扩展)

[7.2.1 扩展方法](#7.2.1 扩展方法)

[7.2.2 有用的类和方法](#7.2.2 有用的类和方法)

[8 光照映射](#8 光照映射)

[8.1 Progressive Lightmapper](#8.1 Progressive Lightmapper)

[8.1.1 CPU、GPU Progressive Lightmapper](#8.1.1 CPU、GPU Progressive Lightmapper)

[8.1.2 渲染管线支持](#8.1.2 渲染管线支持)

[8.1.3 平台兼容性](#8.1.3 平台兼容性)

[8.1.4 设置Progressive Lightmapper](#8.1.4 设置Progressive Lightmapper)

[8.1.5 高级过滤设置](#8.1.5 高级过滤设置)

[8.1.6 统计信息](#8.1.6 统计信息)

[8.1.7 ETA](#8.1.7 ETA)

[8.2 Progressive GPU Lightmapper](#8.2 Progressive GPU Lightmapper)

[8.2.1 硬件和软件要求](#8.2.1 硬件和软件要求)

[8.2.2 性能](#8.2.2 性能)

[8.2.3 防止因光照贴图器tiling引起的烘烤速度减慢](#8.2.3 防止因光照贴图器tiling引起的烘烤速度减慢)

[8.2.4 特定平台限制](#8.2.4 特定平台限制)

[8.2.5 如何优化烘焙速度](#8.2.5 如何优化烘焙速度)

[8.2.6 配置GPU的选择](#8.2.6 配置GPU的选择)

[8.3 光照映射:入门](#8.3 光照映射:入门)

[8.4 预览光照映射](#8.4 预览光照映射)

[8.5 Lightmap Parameters Asset](#8.5 Lightmap Parameters Asset)

[8.5.1 创建](#8.5.1 创建)

[8.5.2 属性](#8.5.2 属性)

[8.5.2.1 Realtime Global Illumination](#8.5.2.1 Realtime Global Illumination)

[8.5.2.2 Baked Global Illimination](#8.5.2.2 Baked Global Illimination)

[8.5.2.3 Baked Tags: Details](#8.5.2.3 Baked Tags: Details)

[8.5.3 Baked AO](#8.5.3 Baked AO)

[8.5.4 分配Lightmap Parameters Asset](#8.5.4 分配Lightmap Parameters Asset)

[8.5.4.1 分配给场景](#8.5.4.1 分配给场景)

[8.5.4.2 分配给游戏对象](#8.5.4.2 分配给游戏对象)

[8.6 光照贴图模式](#8.6 光照贴图模式)

[8.6.1 模式](#8.6.1 模式)

[8.6.2 性能问题](#8.6.2 性能问题)

[8.6.3 设置光照贴图的模式](#8.6.3 设置光照贴图的模式)

[8.6.4 附加场景时的注意点](#8.6.4 附加场景时的注意点)

[8.7 环境光遮挡](#8.7 环境光遮挡)

[8.7.1 烘焙环境光遮挡](#8.7.1 烘焙环境光遮挡)

[8.7.2 实时环境光遮挡](#8.7.2 实时环境光遮挡)

[8.7.3 其他资源](#8.7.3 其他资源)

[8.8 光照贴图:技术信息](#8.8 光照贴图:技术信息)

[8.8.1 编码方案](#8.8.1 编码方案)

[8.8.2 HDR光照贴图支持](#8.8.2 HDR光照贴图支持)

[8.8.3 高质量(BC6H)光照贴图的优点](#8.8.3 高质量(BC6H)光照贴图的优点)

[8.8.4 预计算实时全局光照 (GI)](#8.8.4 预计算实时全局光照 (GI))

[8.9 光照映射与着色器](#8.9 光照映射与着色器)

[8.9.1 The Meta Pass](#8.9.1 The Meta Pass)

[8.9.2 带有Meta Pass的着色器实例](#8.9.2 带有Meta Pass的着色器实例)

[8.9.3 Meta Pass 技术信息](#8.9.3 Meta Pass 技术信息)

[8.9.4 自定义RGB透明度](#8.9.4 自定义RGB透明度)

[8.10 光照贴图UVs](#8.10 光照贴图UVs)

[8.10.1 介绍](#8.10.1 介绍)

[8.10.1.1 Baked lightmap UVs](#8.10.1.1 Baked lightmap UVs)

[8.10.1.2 Real-time lightmap UVs](#8.10.1.2 Real-time lightmap UVs)

[8.10.1.3 Unity如何计算real-time lightmap UVs](#8.10.1.3 Unity如何计算real-time lightmap UVs)

[8.10.2 生成光照贴图 UV](#8.10.2 生成光照贴图 UV)

[8.10.2.1 如何提供自己的光照贴图UVs](#8.10.2.1 如何提供自己的光照贴图UVs)

[8.10.2.2 如何自动生成光照贴图UVs](#8.10.2.2 如何自动生成光照贴图UVs)

[8.10.2.3 自动生成光照贴图UVs问题解决](#8.10.2.3 自动生成光照贴图UVs问题解决)

[8.10.3 可视化光照贴图UVs](#8.10.3 可视化光照贴图UVs)

[8.10.4 解决光照贴图UV重叠](#8.10.4 解决光照贴图UV重叠)

[8.10.4.1 识别](#8.10.4.1 识别)

[8.10.4.2 解决方案](#8.10.4.2 解决方案)

[8.11 光照贴图接缝缝合](#8.11 光照贴图接缝缝合)

[8.11.1 接缝缝合的局限性](#8.11.1 接缝缝合的局限性)

[8.11.2 使用接缝缝合](#8.11.2 使用接缝缝合)

[8.12 自定义衰减](#8.12 自定义衰减)

[9 Enlighten实时全局光照](#9 Enlighten实时全局光照)

[9.1 渲染管线支持](#9.1 渲染管线支持)

[9.2 何时使用](#9.2 何时使用)

[9.3 使用](#9.3 使用)

[9.4 如何工作的](#9.4 如何工作的)

[9.5 光照探针与实时GI](#9.5 光照探针与实时GI)

[9.6 阴影与实时GI](#9.6 阴影与实时GI)

[9.7 性能注意事项](#9.7 性能注意事项)

[9.8 优化实时GI](#9.8 优化实时GI)

[9.9 LOD和实时GI](#9.9 LOD和实时GI)

[10 光照探针](#10 光照探针)

[10.1 光照探针技术信息](#10.1 光照探针技术信息)

[10.2 Light Probe Group组件](#10.2 Light Probe Group组件)

[10.2.1 创建](#10.2.1 创建)

[10.2.2 组件属性](#10.2.2 组件属性)

[10.2.3 案例](#10.2.3 案例)

[10.2.4 Ringing现象](#10.2.4 Ringing现象)

[10.2.5 放置位置](#10.2.5 放置位置)

[10.2.6 体积问题](#10.2.6 体积问题)

[10.2.7 移动光源与光照探针](#10.2.7 移动光源与光照探针)

[10.2.8 放置位置错误情况](#10.2.8 放置位置错误情况)

[10.3 脚本创建光照探针思路](#10.3 脚本创建光照探针思路)

[10.4 MeshRenderer](#10.4 MeshRenderer)

[10.5 场景加载](#10.5 场景加载)

[10.6 运行时移动光照探针](#10.6 运行时移动光照探针)

[10.7 Light Probe Proxy Volume组件](#10.7 Light Probe Proxy Volume组件)

[10.7.1 组件使用前提](#10.7.1 组件使用前提)

[10.7.2 组件属性](#10.7.2 组件属性)

[10.7.3 案例](#10.7.3 案例)

[10.7.4 粒子系统样本着色器代码示例](#10.7.4 粒子系统样本着色器代码示例)

[10.7.5 硬件需求](#10.7.5 硬件需求)

[11 反射探针](#11 反射探针)

[11.1 背景](#11.1 背景)

[11.2 工作原理](#11.2 工作原理)

[11.3 反射探针类型](#11.3 反射探针类型)

[11.3.1 Baked反射探针](#11.3.1 Baked反射探针)

[11.3.1.1 特点](#11.3.1.1 特点)

[11.3.1.2 使用演示](#11.3.1.2 使用演示)

[11.3.2 Custom反射探针](#11.3.2 Custom反射探针)

[11.3.2.1 特点](#11.3.2.1 特点)

[11.3.3 Realtime反射探针](#11.3.3 Realtime反射探针)

[11.3.3.1 特点](#11.3.3.1 特点)

[11.3.3.2 使用演示](#11.3.3.2 使用演示)

[11.4 反射探针的使用](#11.4 反射探针的使用)

[11.4.1 默认反射探针](#11.4.1 默认反射探针)

[11.4.2 探针位置](#11.4.2 探针位置)

[11.4.3 重叠的作用范围](#11.4.3 重叠的作用范围)

[11.4.4 混合](#11.4.4 混合)

[11.4.4.1 Mesh Renderer(混合控制)](#11.4.4.1 Mesh Renderer(混合控制))

[11.4.4.2 混合权值计算](#11.4.4.2 混合权值计算)

[11.5 高级反射探针特性](#11.5 高级反射探针特性)

[11.5.1 互反射](#11.5.1 互反射)

[11.5.2 盒子投影](#11.5.2 盒子投影)

[11.6 反射探针性能](#11.6 反射探针性能)

[11.6.1 通用优化](#11.6.1 通用优化)

[11.6.1.1 Resolution](#11.6.1.1 Resolution)

[11.6.1.2 Culling Mask](#11.6.1.2 Culling Mask)

[11.6.1.3 Texture compression](#11.6.1.3 Texture compression)

[11.6.2 实时反射探针优化](#11.6.2 实时反射探针优化)

[11.6.2.1 Refresh Mode](#11.6.2.1 Refresh Mode)

[11.6.2.2 Time Slicing](#11.6.2.2 Time Slicing)

[11.7 反射探针组件](#11.7 反射探针组件)

[11.7.1 概念](#11.7.1 概念)

[11.7.2 属性与渲染管线](#11.7.2 属性与渲染管线)

[11.7.3 属性](#11.7.3 属性)

[11.7.3.1 Type](#11.7.3.1 Type)

[11.7.3.2 Runtime settings](#11.7.3.2 Runtime settings)

[11.7.3.3 Cubemap capture settings](#11.7.3.3 Cubemap capture settings)

[11.7.4 细节](#11.7.4 细节)

[12 预计算的光照数据](#12 预计算的光照数据)

[12.1 生成光照数据](#12.1 生成光照数据)

[12.1.1 生成数据](#12.1.1 生成数据)

[12.1.2 构建之前](#12.1.2 构建之前)

[12.2 光照数据Asset文件](#12.2 光照数据Asset文件)

[12.2.1 默认Asset文件](#12.2.1 默认Asset文件)

[12.3 全局光照缓存](#12.3 全局光照缓存)

[12.3.1 清理GI缓存](#12.3.1 清理GI缓存)

[13 调试光照的绘制模式](#13 调试光照的绘制模式)

[13.1 Lighting](#13.1 Lighting)

[13.1.1 Contributors / Receivers](#13.1.1 Contributors / Receivers)

[13.1.2 Shadow Cascades](#13.1.2 Shadow Cascades)

[13.2 Global Illumination](#13.2 Global Illumination)

[13.2.1 Indirect (Realtime Global Illumination only)](#13.2.1 Indirect (Realtime Global Illumination only))

[13.2.2 Directionality](#13.2.2 Directionality)

[13.2.3 Albedo](#13.2.3 Albedo)

[13.2.4 Emissive](#13.2.4 Emissive)

[13.2.5 UV Charts](#13.2.5 UV Charts)

[13.2.6 Systems (Realtime Global Illumination only)](#13.2.6 Systems (Realtime Global Illumination only))

[13.2.7 Clustering (Realtime Global Illumination only)](#13.2.7 Clustering (Realtime Global Illumination only))

[13.2.8 Lit Clustering (Realtime Global Illumination only)](#13.2.8 Lit Clustering (Realtime Global Illumination only))

[13.2.9 Texel Validity (Baked Global Illumination only)](#13.2.9 Texel Validity (Baked Global Illumination only))

[13.2.10 UV Overlap (Baked Global Illumination only)](#13.2.10 UV Overlap (Baked Global Illumination only))

[13.2.11 Lightmap indices (Baked Global Illumination only)](#13.2.11 Lightmap indices (Baked Global Illumination only))

[13.2.12 Light Overlap (Baked Global Illumination only)](#13.2.12 Light Overlap (Baked Global Illumination only))

[13.2.13 Baked Lightmap (Baked Global Illumination only)](#13.2.13 Baked Lightmap (Baked Global Illumination only))

[13.2.14 Shadowmask (Baked Global Illumination only)](#13.2.14 Shadowmask (Baked Global Illumination only))

[13.3 光照可视化悬浮窗口参考](#13.3 光照可视化悬浮窗口参考)

[13.4 更多内容](#13.4 更多内容)

[14 后记](#14 后记)


1 前言

关于Unity光照系统的一个笔记,主要是对官方文档内容的学习,总结下就是"文档翻译+自己的注释"。Unity中文和英文我都看,基本是结合着搞的,有些中文官方翻译甚至都有问题,一点都不上心。毕竟是个人笔记,也没法保证内容100%的正确性,不过基本没问题,遇到不懂的地方可以结合官方文档看。

虽然官方文档写得很烂,但不多不说里面的东西还是实在的。很多内容网上的教程是不会提的,但这些内容在文档中都会有提及,看了之后就会有原来如此的感觉。本身光照系统我是没打算看文档的,但教程内容缺乏,只能在文档中找内容看,发现了文档里包含了很多东西就转而直接看文档了。

看官方文档时,个人觉得完整的看完还是有必要的。比如我在看文档的某一块时,常常有看不懂的时候,不知道这里说的是什么,理解上有歧义,没法确定,但随着继续看下去,可能在其他部分就能看到相关联的内容,进而解决了之前遗留的疑惑。有一种单个知识点分散到整个文档的感觉,这也说明了官方文档的内容实际并不是严格按照知识点划分的,很多时候一个知识点需要从多个部分来了解,最终汇总理解。

另外,对于文中遇到的一些不懂的名词、给的链接,可以现在文中搜索下看看能不能搜到相关解释,前面也说了知识点是分散的,活用Ctrl+F。当然也可以用来在全文中查找相关内容的多个解释,比如文中介绍了一个属性,但描述简单令人难懂,这时候就可以全文搜索此属性名称,也许就可以在另一个小节中找到对其的详细解释了。

PS:

  1. 文档版本:Unity 6
  2. texel概念:纹理数组中的单个值,常常称为纹理单元,也叫纹素(纹理像素,texel)。

2 光照介绍

Unity中的照明工作原理近似于光在现实世界中的行为。Unity使用详细的光线工作模型,来获得逼真的光线效果。厚实使用简化的模型来获得更具风格化的效果。

2.1 直接光与间接光

直接光是发射出来的光,照射到物体表面一次,然后直接反射到传感器(例如,眼睛的视网膜或照相机)。间接光是指所有其他最终被反射到传感器中的光,包括多次照射物体表面反射的光和天空光。如图:

Unity可以计算直接光照,间接光照。具体光照技术的使用,取决于我们的项目配置。

2.2 实时光照与烘焙光照

实时光照是Unity在运行时实时计算的光照。烘焙光照是Unity提前执行光照计算,并将结果存储为光照数据,然后在运行时使用。在Unity中,我们可以只使用其中一种方式,或混合使用两种方式。

2.3 全局光照

全局光照(Global Illumination)是一组模拟直接光照、间接光照的基数,来提供逼真的光照效果。Unity有两个全局光照系统,包含了直接光照与间接光照。

  1. Baked Global Illumination系统由光照贴图(Lightmap)、光照探针(Light Probe)、反射探针(Reflection Probe)组成。我们可以使用Progressive Lightmapper(CPU或GPU)来执行烘焙操作。
  2. Real-time Global Illumination系统是Enlighten Realtime Global Illumination。

3 光源

这里介绍的光源都是在Unity内置渲染管线下的光源。右键创建灯光,和创建Cube类似,就不细说了。Light组件:

通过第一个属性Type,可以选择四种类型光源:

  1. Directional(方向光)
  2. Point(点光)
  3. Spot(聚光灯)
  4. Area(区域光)

后面将分别介绍。在介绍时,前面介绍过的属性后面便不再介绍。

3.1 Directional Light

方向光。方向光在许多方面的行为就像太阳一样,可以被认为是存在于无限远处的遥远光源。方向光没有任何可识别的光源位置,因此光照对象可以放置在场景中的任何地方。场景中的所有物体都可以被照亮,好像光线总是来自同一个方向。光与目标物体的距离没有定义,所以光不会因距离而减弱。

默认情况下,每个新的Unity场景都包含一个方向光。我们可以删除此光源,创建一个新的光源。

3.1.1 Color

调整光源颜色。

3.1.2 Mode

光照模式。有三种模式可选:

  1. Realtime(实时)
  2. Baked(烘焙)
  3. Mixed(混合)

3.1.3 Intensity

光照强度。

3.1.4 Indirect Multiplier

间接光照强度。若此值低于1,则每次光照反弹的光线变得越暗;若高于1,则每次反弹的光线就越亮。

3.1.5 Shadow Type

阴影类型。有三种,分别为:

  1. No Shadows(无阴影):即没有阴影,可以节省性能。
  2. Hard Shadows(硬阴影):硬阴影边缘存在锐利的形状,与软阴影相比其并不真实,但它只需要较少的处理,节省一些性能。
  3. Soft Shadows(软阴影):软阴影较为柔和、真实,但相对的也比较耗性能。

3.1.6 Baked Shadow Angle

烘焙阴影角?如果Model设置为Baked或Mixed,类型设置为定向,Shadow Type设置为Soft Shadows时,此属性可用。此属性设置可以在阴影边缘添加一些人工软化,并使其看起来更自然。

3.1.7 Realtime Shadows

在Model设置为Realtime或Mixed,Shadow Type设置为Hard Shadows或Soft Shadows时,此属性组可用。可使用这些属性来控制实时阴影渲染设置。

3.1.7.1 Strength

控制光照投射的阴影的暗度,用0到1之间的值表示。默认设置为1,最暗。

3.1.7.2 Resolution

控制阴影贴图的渲染分辨率。更高的分辨率增加了阴影的保真度,但需要更多的GPU时间和内存使用。

3.1.7.3 Bias

控制阴影偏移位置,值范围0-2,用于调整阴影位置。默认值0.05。

3.1.7.4 Normal Bias

控制阴影投射表面沿表面法线收缩的距离,定义为0到3之间的值,用于调整阴影位置。默认值0.4。

3.1.7.5 Near Plane

在渲染阴影时,控制阴影与物体表面距离的参数,定义为0.1到10之间的值。这个值被限制在0.1个单位或光的范围属性的1%,以较低者为准。默认设置为0.2。

指定一个纹理蒙版,通过它投射阴影(例如,为光创建轮廓或图案照明)。即投射图案阴影。同时通过其下方的Size设置投影图案的大小。

3.1.9 Draw Halo

绘制光环。若有Range属性时(点光、聚光灯才有),Range可以控制光环的大小。Halo是一个组件。

3.1.10 Flare

可提供一个光晕(Flare)使用。Flare是一种asset资源。

3.1.11 Render Mode

渲染方式。可以设置所选光源的渲染优先级,这会影响光照保真度和性能。可选方式:

  1. Auto:在运行时确定渲染方法,具体取决于附近光源的亮度和当前项目设置中的Quality是如何设置的。
  2. Important:光线总是以每像素的质量渲染(像素光)。只对最明显的视觉效果使用重要模式(例如,玩家汽车的前灯)。效果好,吃性能。
  3. No Important:带你观点光源以快速、顶点/对象模式进行渲染(顶点光)。

3.1.12 Culling Mask

可设置哪些层下的物体是否会被此光源光照影响。选中的层是会受光照影响的。

3.2 Point Light

点光。由一点向四周发射光线,比如"火把"、"灯泡"。光照强度随距离光源的远近而减小,在一定范围内达到零。光强与到光源距离的平方成反比。这被称为"平方反比定律",与光在现实世界中的行为类似。

3.2.1 Range

可设置点光源的光照范围,或者理解为光传播的距离。(此属性仅点光和聚光灯才有)

3.2.2 Baked Shadow Radius

此属性只有在Type为Point、Spot,Mode为Baked、Mixed,Shadow Type为Soft Shadows时,才可用。此属性将在阴影边缘添加一些人工软化,使其看起来更自然。

3.3 Spot Light

聚光灯。字面意思,从一个点往某一方向投射光。与点光类似,聚光灯也有自己特定的范围,是一个锥形区域,在此范围外光线将消失。光线在聚光灯锥的边缘也会减弱,角度的扩大可以增加圆锥体的宽度,并随之增加了这种褪色的大小,称为"半影(penumbra)"。

3.3.1 Spot Angle

调整聚光灯锥底的角度(以度为单位)。

3.4 Area Light

区域光。在场景中由矩形或圆形定义的光,在其表面均匀地向所有方向发射光,但只从矩形或圆形的一侧发射光。区域光提供的照明强度以与光源距离的平方成反比的速率衰减。不能用于实时运行,只能用于烘焙全局光照,需要使用"Baked Global Illumination",全局光照后面会讲。

因为一个区域光同时从几个不同的方向照亮一个物体,所以阴影比其他类型的光更柔和和微妙。我们可以用它来创建一个逼真的街灯或一排靠近玩家的灯。一个小的区域光可以模拟较小的光源(如室内照明),但效果比点光更逼真。

3.5 注意点

  • 如果我们创建一个带有Alpha通道的纹理,并设置到光源组件的Cookie属性上,那么Cookie就会从光源投射出来。Cookie的Alpha Mask可以调节光线的亮度,在光照表明产生明暗斑点,这可以为场景增加复杂性或氛围。
  • VertexLit着色器不能显示cookie或阴影。
  • 请注意,当使用正向渲染时,带有cookie的方向光的阴影是禁用的。在这种情况下,可以编写自定义着色器来启用阴影。更多细节见文档writing surface shaders

小提示:

  • 聚光灯可以非常有效地创造从窗户进来的光的效果。
  • 低强度点光可以很好地为场景提供深度。
  • 为了获得最佳性能,可以考虑使用VertexLit着色器。这个着色器只做每个顶点的光照,在低端卡上提供更高的吞吐量。不过一般是不建议使用此着色器,老着色器了。

3.6 光照模式

光照模式分为三种:Realtime、Baked、Mixed。

3.6.1 Realtime

此模式为实时光。Unity在运行时执行实时灯光的照明计算,每帧一次。我们可以在运行时更改实时灯光的属性,以创建诸如灯泡闪烁或火炬通过黑暗房间的效果。实时灯光通常用于在角色或可移动的几何体上照明和投射阴影。

实时光行为:

  • 实时灯光投射阴影到阴影距离(Shadow Distance)。
  • 默认情况下,实时灯光只向场景提供实时直接光照。如果你使用内置的渲染管道,并且在你的项目中启用了Realtime Global Illumination(全局光照部分的内容),实时光照也会为场景提供实时的间接光照。

实时光限制:

  • 实时光耗费性能,尤其在复杂场景。
  • 因为实时灯光在默认情况下只为场景提供直接光照,所以阴影看起来完全是黑色的,没有任何间接光照效果,例如颜色反弹。这可能会导致场景中的灯光不真实。

3.6.2 Baked

此模式为烘焙光。Unity在编辑器中执行烘焙灯光的计算,并将结果作为光照数据保存到磁盘上,这个过程叫做烘焙。在运行时,Unity加载烘焙的光照数据,并使用它来照亮场景。因为复杂的计算是提前完成的,所以烘焙光在运行时减少了阴影成本,并减少了阴影的渲染成本。烘焙光照对于运行时不会改变的东西很有用,比如风景。

烘焙光行为:

  • Unity将直接光和间接光烘焙到光照贴图(lightmap)中。
  • Unity将直接光和间接光烘焙到光照探针(Light Probe)中。

烘焙光限制:

  • 在运行时,无法更改烘焙光的属性。
  • 烘焙光不会产生镜面照明(specular lighting)。
  • 动态物体无法使用烘焙光。

注意,如果我们禁用Baked Global Illumination,再强制执行烘焙行为,那么些设置为Baked、Mixed模式的灯光将像将它们的模式设置为实时一样。当这种情况发生时,Unity会在Light组件上显示一个警告。

3.6.3 Mixed

此模式为混合光。混合灯光结合了实时光和烘焙光。拥有实时的直接光、烘焙的间接光。可以实现动态阴影与来自同一光源的烘焙光照的结合。

混合光行为:

  • 场景中所有混合光的行为取决于Lighting window中的Lighting Mode设置。不同选项的设置具有非常不同的性能特征,以及不同的视觉保真度。
  • 可以在运行时改变混合光,这将会更新实时光照的部分,所以要注意可能产生的异常视觉效果出现。

混合光限制:

  • 混合光的性能成本根据Lighting Mode的不同而有很大的区别。其涉及一部分的实时光计算,一部分的烘焙光计算,一个占运算时间,一个占内存空间,所以使用时要权衡性能。

3.7 Cookies

Cookie是一个蒙版,我们把它放在一个光源上,可以通过改变光的外观和强度来创建一个具有特定形状或颜色的阴影。Cookie是一种模拟复杂灯光效果的有效方法,对运行时性能的影响最小或没有影响。可以用Cookie模拟的效果包括焦散、柔和阴影和光线形状。

Cookie在Light组件的Cookie属性上设置。

Cookie可能支持不同的特性,这取决于我们使用的渲染管线。

使用Baked Light Cookies而实现的假焦散效果:

3.7.1 启用和禁用Baked Cookies

对于在Unity 2020.1或更高版本中创建的项目,默认情况下,Progressive Lightmapper中的Baked灯光和Mixed灯光会启用Baked Cookies。对于在2020.1之前版本的Unity中创建的项目,默认情况下,Progressive Lightmapper中的Baked灯光和Mixed灯光的Baked Cookies是禁用的。这是为了提供向后兼容性。

我们可以在编辑器设置窗口中设置在Progressive Lightmapper中,Cookies是否被用于Baked、Mixed灯光。如图:

3.7.2 为内置渲染管线创建Cookie

使用内置渲染管道创建Cookie最方便的方法是创建一个灰度纹理,将该纹理导入Unity,然后让Unity将纹理的亮度转换为alpha。注意,在内置渲染管道中,Cookie只使用来自alpha通道的数据,这意味着我们可以为Cookie定义形状,但不能定义颜色。

首先要有张图像:

然后就将其拖拽到项目当中,设置其类型,如图:

创建一个场景,去掉天空盒(让场景暗一点),创建Spot Light,将图片拖到Cookie属性上,如图:

PS:Cookie的像素不需要完全透明或不透明,但也可以包含介于两者之间的任何值。我们可以使用中间值来模拟光线路径中的灰尘或污垢,或模拟腐蚀性效应,例如由汽车前灯的脊线产生的效应。

3.8 发光材质

发光材质(Emissive materials)可以让物体表面发光,并且相关属性可以在运行时修改,即可实时状态下使用发光材质。发射"是标准着色器的一个属性,它允许场景中的静态物体发光。默认情况下,"发射"的值被设置为零,这意味着使用标准着色器分配材质的物体不会发出光。

发光材质没有范围值,但发射的光将再次以二次速率衰减。

只有在Inspector面板中设置为Static(Contribute GI)才能接收到发射的光线。同样,也只有静态物体才能使用发光材质对场景造成光照影响,若是非静态,则自身发光但不会对场景造成影响。对于静态物体,若也想自身发光但不对场景造成影响,那么我们可以在材质的属性设置面板中设置来实现此效果,只需要将Global Illumination设置为None即可。发射材料只直接影响场景中的静态几何对象,如果需要动态或非静态几何对象来收到光照影响,则需要使用光照探针。

像这样的自发光材料是创造霓虹灯或其他可见光源效果的有效方法。

3.8.1 演示

首先创建一个材质,然后将其放到一个物体上,注意材质的属性,如图:

勾选Emission,材质即可变为发光材质。Color处可以修改光的颜色、强度。Global Illumination修改全局光照类型,有Realtime、Baked、None。None前面说过了,就是自身发光但不影响周围环境;Realtime需要配合"实时全局光照"使用,Baked需要配合"烘焙全局光照"使用(全局光照(Global Illumination)部分的内容,可以之后再单独了解)。这里我们以Realtime来演示:

3.9 环境光

环境光(Ambient light),也被称为漫射环境光,是存在于场景周围的光,并不来自任何特定的源对象。它可以对场景的整体外观和亮度做出重要贡献。环境光在很多情况下都是很有用的,这取决于我们项目的艺术风格。比如卡通风格,可能就需要明亮的环境光,甚至照明效果也是手绘到纹理中的。另外,也可以通过环境光来调整整个场景的亮度,而不需要调整其他灯光。

环境光可以在Lighting面板中设置。另外,环境光也是会被"实时全局光照"与"烘焙全局光照"系统所影响的。

4 阴影

4.1 阴影映射

Unity使用一种称为阴影映射(shadow mapping)的技术来渲染实时阴影。

4.1.1 阴影映射是如何工作的

阴影映射使用称为阴影贴图(shadow maps)的纹理。阴影贴图类似于深度纹理。光源生成阴影贴图的方式与相机生成深度纹理的方式类似。可以想象一个相机与光源在同一位置,那么相机看不到的场景区域就是光线无法到达的场景区域,因此,他们处于阴影之中。

Unity在阴影贴图中填充了光线在达到物体表明之前所走过的距离信息,然后对阴影地图进行采样,以计算光线击中的GameObjects的实时阴影。

4.1.2 阴影贴图分辨率

首先,确定光可以照亮的屏幕视图的面积。对于方向光,可以照亮整个屏幕。对于聚光灯和点光,区域是光的范围的形状在屏幕上的投影(锥形、圆形)。投影形状在屏幕上具有以像素为单位的宽度和高度,然后取这两个值中较大的那个,这个值被称为光的像素尺寸。

然后,确定阴影质量的相乘系数。这个数值在Unity的Quality窗口中的Shadow Resolution属性这里设置(Project Settings→Quality→Shadows→Shadow Resolution)。可设置选项如下:

  • Very High: 1.0
  • High: 0.5
  • Medium: 0.25
  • Low: 0.125

最后,执行计算,并将结果裁剪到最大分辨率范围内。计算:

| 光照类型 | 计算公式 | 最大分辨率(像素单位) |
| 方向光 | NextPowerOfTwo (pixel size * shadow quality multiplier * 3.8) | 当阴影分辨率是非常高的质量或GPU有512MB或更多的RAM,则分辨率设置为4096×4096,否则为2048×2048。 |
| 聚光灯 | NextPowerOfTwo (pixel size * shadow quality multiplier * 2.0) | 如果GPU的内存大于等于512MB,则为2048 * 2048,否则为1024 * 1024 。 |

点光 NextPowerOfTwo (pixel size * shadow quality multiplier * 1.0) 如果GPU的内存大于等于512MB,则为1024 * 1024,否则为512 * 512。

点光相比其他类型在尺寸上限制得更小,因为其使用Cubemap来绘制阴影。这意味着有相同分辨率的Cubemap的6个面会同时存储在显存中,另外,其渲染成本也是非常高的,因为潜在的阴影可能需要渲染到Cubemap的6个面中。

4.1.3 替换阴影贴图分辨率

在内置渲染管道中,我们可以通过将Light.shadowCustomResolution属性设置为一个大于零的值来设置灯光阴影贴图的分辨率。当这个值大于0时,Unity对所有的光源类型执行如下计算:NextPowerOfTwo (shadowCustomResolution)。其会根据Light类型与硬件情况计算,然后裁剪到最大分辨率范围内。

PS:我记得在灯光Inspector面板里也可以设置阴影质量相乘系数,可以选择使用质量面板的设置,也可以自己选择低高四个档位。

4.2 阴影配置项

在Light组件中可以设置灯光模式为Realtime、Baked、Mixed,对应的引用也会采用相对的模式。什么样的光产生什么样的阴影,实时光产生实时阴影,烘焙光产生烘焙阴影。具体会在全局光照里有讲到。

Mesh Renderer组件中有Cast Shadows和Receive Shadows属性,可分别设置物体本身是否可以投射阴影(有影子),以及是否接收阴影(身上可被投射别的物体的阴影)。

Cast Shadows的选项有:Off、On、Two Sided、Shadows Only。Off是关闭阴影投射;On是打开;Two Sided允许阴影投射到表面的任何一侧,因此为了投射阴影,背面剔除此时将被忽略;Shadows Only会使物体不可见,但阴影依旧存在。

4.3 阴影距离

阴影距离(Shadow Distance)用于确定从相机到Unity渲染实时阴影的最远距离,超过此距离就不渲染了。游戏对象离摄像机越远,其阴影越不明显,这是因为阴影在屏幕上显得比较小,而且远处的游戏对象通常不太会被关注。我们可以利用这个特点来降低渲染对性能的消耗,只需要禁用远距离游戏对象的实时阴影即可。另外,场景通常没有远处的阴影会看起来更好。

如果当前相机的近平面(Camera Far Plane)比阴影距离更近一些,则Unity会使用近平面距离,而非阴影距离。此时只有在近平面距离内的阴影才能被渲染。

超出阴影距离而没有被渲染的阴影,其缺失会比较显眼,我们可以使用相关视觉效果来遮掩它们,比如雾。

4.3.1 设置阴影距离

在使用Unity内置渲染管线的情况下,我们可以在项目的质量设置中设置阴影距离。如图:

在通用渲染管线( URP)中,在通用渲染管线的Asset中来设置阴影距离。

在高清渲染管线(HDRP)中,需要为每个Volume设置阴影距离。

4.3.2 减少闪烁阴影

如果阴影距离相机较远,则它们可能会出现闪烁的情况。更多信息参考 Understanding the View Frustum

如果阴影比世界空间原点离相机更近,则启用相机相对剔除。Unity使用相机作为阴影计算的相对位置,而不是世界空间原点,这减少了闪烁。

要启用相机相对剔除,在下图中位置设置:

4.3.2 阴影距离与阴影遮罩光照模式

若在启用Baked Global Illimination后,选择了阴影遮罩(Shadowmask Lighting)模式,Unity可以使用光照探针或一个阴影遮罩纹理来渲染阴影距离以外的混合(Mixed)光阴影。我们可以配置Unity如何在阴影距离之外渲染阴影。

4.4 阴影层叠

阴影层叠(Shadow Cascades)有助于解决透视混叠(perspective aliasing)问题:当方向光的实时阴影靠近相机时,它们会出现像素化。阴影层叠只适用于方向光。

4.4.1 透视混叠

一个方向光通常模拟阳光,一个单一的方向光可以照亮整个场景。这意味着它的阴影贴图覆盖了场景的很大一部分,这可能导致一个叫做透视混叠的问题。透视混叠意味着靠近相机的阴影贴图像素相比于那些远离相机的看起来更大、更厚。

如图,远处(A)的阴影具有适当的分辨率,而靠近相机(B)的阴影显示透视混叠。

由于阴影贴图的不同区域被相机视角不成比例地缩放,因此会出现透视混叠。光线的阴影贴图只需要覆盖场景中相机可见的部分,这是由相机的视锥体(view frustum)定义的。比如,方向光直接从上方照下,产生一个阴影,如下图所示,我们就可以看到视椎体和阴影贴图之间的关系。

在这个简化的例子中,视椎体的远端被20个像素的阴影贴图覆盖,而近端仅被4个像素覆盖。然而,两端在屏幕上显示的尺寸相同(可以自己在场景里对着地面的线框试下)。结果是,对于靠近相机的阴影区域,其贴图的分辨率实际上要低得多。

4.4.2 阴影层叠原理

当我们使用Soft Shadows,并使用一个高分辨率的阴影贴图时,透视混叠实际是不明显的。但是这种处理方式在渲染时将会占用更多的内存和带宽。

当使用阴影层叠时,Unity将会根据距离相机的距离将视椎体划分为两个区域。在相机近端区域,使用一个单独的尺寸被缩放变小的阴影贴图(尺寸缩小,但分辨率不变)。这种阴影贴图尺寸的阶段性缩减,被称为级联阴影贴图(cascaded shadow maps),有时也被称为Parallel Split Shadow Maps。

如图所示,靠近相机区域使用了一个单独的阴影贴图,尺寸缩小,分辨率不变(总感觉说分辨率不变有歧义...)。

4.4.3 使用阴影层叠

在项目中,当我们使用音阴影层叠时,我们可以旋转使用0、2、4层叠。Unity会计算各层叠在相机视椎体内的位置。使用的层叠越多,阴影受透视混叠的影响就越小。但层叠越多,渲染开销也就越大,不过,相比在整个阴影中使用高分辨率的贴图,层叠的开销仍然要小。

若使用内置渲染管线,则在项目的质量设置窗口中配置每个质量级别的阴影层叠。如图:

若使用通用渲染管线(URP),则在渲染管线的Asset文件中设置阴影层叠。

若使用高清渲染管线(HDRP),则需要为每个Volume设置阴影层叠。

4.5 阴影问题解决方案

4.5.1 阴影性能

  • 实时阴影具有相当高的渲染开销,任何可能投射阴影的游戏对象必须首先渲染到阴影贴图中,然后该贴图将用于渲染可能接收阴影的对象。
  • Soft shadows比Hard Shadows有更大的渲染开销,但这只对GPU有较大影响,对CPU影响较小。
  • 如果为复杂的几何体渲染实时阴影的成本过高,可以考虑使用低LOD网格。
  • 当角色阴影性能开销较大时,还可以考虑将一个模糊的纹理应用到一个简单的Mesh或Quad上,然后将其放到角色下当做阴影,或者使用自定义的shader来创建阴影。

4.5.2 阴影暗斑

被光直接照射的表面有时看起来部分处于阴影中。这是因为应该准确地在阴影贴图中指定距离上的像素,其有时被计算到更远的距离了(这是使用阴影过滤或阴影贴图使用低分辨率图像造成的)。其结果是,一些像素处于阴影之中,而它们本应是被照亮的,产生了一种被称为"阴影暗斑(shadow acne)"的视觉效果。如图,在低分辨率阴影情况下,物体表明出现了一些类似波纹的东西。

4.5.3 光偏移设置

为了预防阴影暗斑,我们可以调整光偏移设置(Light Bias Settings)。

我们可以在阴影贴图中增加一个距离偏移,以确保边缘上的像素通过比较,或者我们可以沿着它的法线(normals)插入一点几何偏移。

在内置渲染管道中,当启用阴影时,我们可以在Light Inspector窗口中使用Bias和Normal Bias属性设置这些值。

不要将Bias值设置得太高,因为在投射它的GameObject附近的阴影区域有时会被错误地照亮。这导致了一个不连贯的阴影,使GameObject看起来好像在地面上飞行。如图:

同样地,将Normal Bias值设置得太高会使GameObject的阴影显得太窄,有种变小的感觉:

在某些情况下,Normal Bias会导致一种不必要的效果,称为"光出血(light bleeding)",即光线从附近的几何图形中渗出到应该阴影的区域。一个潜在的解决方案是将GameObject的Mesh Renderer组件上的Cast Shadows属性更改为Two sides。这有时会有所帮助,尽管它在渲染场景时可能会占用更多资源并增加性能开销。

关于Bias的值,一般需要合理的调整,以确保不发生不想要的效果,通常用眼睛来衡量正确的值比试图计算它更容易。

比如,如果设置不合理的偏移值(包括Bias、Normal Bias),就会导致如下情况:

如图,阴影中出现了一块空洞。而其本来的模样是这样的:

4.5.4 阴影平坠

为进一步预防阴影暗斑,我们正在使用一种被称为阴影平坠(Shadow pancaking)。这个想法是为了减少沿光照方向渲染阴影贴图时使用的光照空间范围。这样可以提高阴影贴图的精度,减少阴影暗斑。

阴影平坠原理介绍,原理图:

在上图中:

  • 浅蓝色的圆圈代表阴影投射者,即产生阴影的物体。
  • 深蓝色矩形代表原始光照空间
  • 绿线表示优化后的近平面(将视锥体中不可见的所有阴影投射物排除在外)

将这些阴影投射物钳制在优化空间的近裁剪面(在顶点着色器中)。请注意,虽然这通常很有效,但对于穿过近裁剪面的大型三角形,这会带来瑕疵,如图:

在此情况下,只有蓝色三角形的一个顶点位于近裁剪面背后并被钳制到此处(由这个就看懂了,这个"钳制"含义:图中右侧可看到三角形顶点被强行往近裁剪面移动了)。但是,这会改变三角形的形状,并可能产生不正确的阴影。

我们可以从Quality窗口中调整 Shadow Near Plane Offset 属性以避免发生此问题。这将拉回近裁剪面(即图中往下移动)。但是,如果将此值设置得非常高,最终会引入阴影暗斑,因为它会提高阴影贴图需要在光照方向上覆盖的范围(即图中值越大近裁剪面越往下走)。或者,我们也可以拆分有问题的阴影投射三角形。

4.5.5 阴影不显示

如果我们发现一个或多个对象未投射阴影,则可以从以下几点来检查:

  • 检查Quality窗口,看看对应的质量级别,是否启用了阴影。
  • 检查对象的Mesh Renderer中Receive Shadows和Cast Shadows属性是否启用。
  • 只有不透明对象才投射和接受阴影,因此使用内置透明着色器或粒子着色器的对象既不会投射也不会接受阴影。通常,我们可以使用透明镂空着色器代替具有"间隙"的对象,例如栅栏、植被等。自定义着色器必须采用像素光照并使用几何渲染队列
  • 使用顶点光照 (VertexLit) 着色器的对象不能接受阴影,但可投射阴影。
  • 如果游戏对象的材质具有"无光照"类型的着色器,Unity 无法为这些游戏对象计算阴影。只有材质的着色器支持光照时,Unity 才能为材质计算阴影。
  • 在内置渲染管道中,使用正向渲染路径(Forward Rendering Path),一些着色器只允许最亮的方向光投射阴影(特别是,这发生在Unity的内置着色器4.x版本)。如果我们想有一个以上的阴影投射光,那么我们应该使用延迟着色(Deferred Shading)渲染路径。我们可以通过使用fullforwardshadows表面着色器指令来启用我们自己的着色器来支持"完全阴影(full shadows)"。

5 全局光照

全局光照(Global Illumination,GI),考虑场景中直接来自光源的光照,同时又考虑经过场景中其他物体反射后的光照的一种渲染技术。GI可以有效地增强场景的真实感。毫无疑问,实时性的GI是非常耗费性能的,所以通常会选择进行预先计算。Unity中,GI有两种,第一种是实时GI(Enlighten Realtime Global Illumination),第二种是烘焙GI(Baked Global Illumination)。

  • 实时GI:可以对静态物体(参与GI的物体,本身会变为静态状态)进行光线反射部分数据的预计算(光照贴图),在运行时,支持实时光线的改变(也会改变光照贴图)。但这样就会存在一定的延迟,不适合快速变化的灯光。所属系统:Enlighten Realtime Global Illumination系统。
  • 烘焙GI:将全局光照信息提前计算,并烘焙到一张LightMap贴图(光照贴图)中不必实时计算,以此来节省性能资源。但相对的,光照就定死位置了,无法再发生变化(光照贴图无法改变)。烘焙GI有两种模式,一种是Baked模式只有烘焙光,另一种Mixed模式将包含烘焙光和实时光。所属系统:Lightmapper系统。

好了,到这里我们就知道了三种情况的光照,一种是Unity默认光照,一种是实时GI,一种是烘焙GI(两种模式)。

5.1 让物体参与GI

想要使用GI,首先要让物体能够参与到GI中,那么如何设置呢?如图:

选中物体,将其设置为静态,这里实际只需勾选图中的"Contribute GI"即可,勾选后物体变变为静态的,无法在运行时移动了(对于非静态物体也可以使用光照探针进行间接光处理,这里先不讲),但同时也可以参与GI了。

或者也可以在物体的MeshRender组件中设置,勾选"Contribute Global Illumination"即可,这个与我们设置的"Contribute GI"是会同步改变的,勾选一个,另一个就会自动勾选。

5.2 实时GI

实时计算直接光、间接光,生成的光照贴图辅助计算间接光。Window→Rendering→Lighting,打开Lighting面板。勾选"Realtime Global Illumination",表示在生成光照数据时,生成实时光照数据。之后选择我们的光源对象,将其Mode设置为Realtime。

然后点击Lighting面板的Generate Lighting即可(一般也会将这个操作称为"烘焙"),生成光照贴图。

PS:注意点击按钮右侧的倒三角,可在下拉列表中清除已经生成的光照数据。

之后我们可以在"Realtime Lightmaps"面板中看到生成的光照贴图,此光照贴图会随着光源的改变而改变,这里就不再演示了,可以在打开此窗口的同时,试一试调整光线颜色。

另外,还要注意生成的光照数据文件:

一般会自动生成一个同场景名的文件夹,来存放生成的数据。

5.2.1 演示

这里还是演示下吧。首先搭建一个全黑场景,涉及相机、环境光设置,如图:

这样以来,整个场景就暗下来了,这里我搭建了一个小房子(给了材质改了个颜色),外加一个方向光,修改为Realtime模式,整体场景如图:

接下来使用实时GI:

可以看到,在烘焙完实时GI后,房子内的细节瞬间就有了。然后我开始旋转光源,由于直接光、间接光都是实时的,可以看到阴影、房子内的细节都会随着光源的旋转而变化。

PS:

  1. 旋转光源,有时候会出现间接光变化不及时的问题,这是正常的,因为实时间接光较耗费性能,计算速度过慢导致有时候变化不及时。
  2. 还有一种就是旋转光源,但间接光不变化,不变化就是不变化。这就是BUG,重启Unity就好。这BUG搞了两个多小时没找出问题,重启解决了,感觉可能跟GI缓存有关,但我又没证据。

5.3 烘焙GI

这里我们需要勾选"Baked Global Illumination",表示在生成光照数据时,生成烘焙光照数据。这里若不需要实时GI的话可以取消"Realtime Global Illumination"的勾选,这里我们取消,方便案例说明。之后同样,我们需要去设置光源的Mode,不过这里有两种可选:Mixed、Baked。

5.3.1 Mixed

光源Mode选择Mixed表示混合,即包含实时光与烘焙光。Lighting面板的Lighting Mode为默认的Shadowmask时(目前只讨论这个),我们点击"Generate Lighting",只会生成间接光 的烘焙光照数据,直接光此时为实时计算的,即实时光照。我们可以看下生成的光照数据:

即,无实时GI光照贴图。

即,有烘焙GI光照贴图。

Project中生成的光照数据文件。

5.3.2 Mixed演示

场景搭建与实时GI演示里一样,这里使用mixed烘焙GI来看看效果,由于烘焙时间过长,这里直接展示烘焙好的场景:

如图所示,是已经烘焙好有细节的场景,由于直接光是实时的、间接光是定死的,所以随着光源的旋转可以看到阴影是实时变化的,但房子左侧地面上的光(间接光造成的)却没有变化,即使其侧已经没有光照了地面却依旧在发亮。实际内部细节也没有变化,这不过这里可能看不太清楚。

5.3.3 Baked

光源Mode选择Mixed表示纯烘焙,即只包含烘焙光。Lighting面板的Lighting Mode为默认的Shadowmask时,我们点击"Generate Lighting",会生成直接光间接光的烘焙光照数据。

5.3.4 Baked演示

场景搭建与实时GI演示里一样,这里使用baked烘焙GI来看看效果,由于烘焙时间过长,这里直接展示烘焙好的场景:

如图所示,是已经烘焙好有细节的场景,由于直接光、间接光是定死的,所以随着光源的旋转,什么都没有变化,阴影不变,细节也不变。

5.4 Lighting窗口

Lighting 窗口是 Unity 光照功能的主要控制点。使用 Lighting 窗口调整与场景中的光照有关的设置,并根据质量、烘焙时间和存储空间来优化预计算的光照数据。

5.4.1 相关 API

可以使用 LightingSettingsLightmapping API,通过脚本执行 Lighting 窗口中提供的许多函数。

5.4.2 内部包含的窗口

  • Scene。
  • Environment。
  • Realtime Lightmaps。
  • Baked Lightmaps。
  • Probe Volumes,高清渲染管线(HDRP)独有。
  • Control Area,在Lighting窗口底部。

5.4.3 Scene

此窗口中显示的信息是分配给当前激活场景的光照设置资源(Lighting Settings Asset)文件中的信息。如没有给当前激活场景分配此文件,则会显示默认光照设置(LightingSettings)对象的信息(一个内置的只读对象)。

5.4.3.1 Lighting Settings
  • Lighting Settings Assets:光照设置资源文件。光照设置就是此Lighting面板中的各种设置,设置的内容都会被记录到此属性上的资源文件中。我们可以在这里替换文件。我们也可以点击New按钮创建多个资源文件,分别存储不同的光照设置,点击Clone按钮可以复制当前资源文件。
5.4.3.2 Realtime Lighting

本节包含了与 Enlighten Realtime Global Illumination系统相关的设置。

  • Realtime Global Illumination:勾选后,启用实时GI系统,在Generate Lighting时会生成实时光照数据。
  • Realtime Environment Lighting:勾选后,使用实时GI系统来计算、更新环境光。只有在"Baked Global Illimination"启用后才可设置,若没有启用,则此属性默认强制勾选。
  • Indirect Resolution:指定用于实时光照贴图的每个单元的纹理数。增加这个值可以提高光图质量,但也会增加渲染时间。
5.4.3.3 Mixed Lighting

本节包含在使用此Lighting Settings Asset的场景中影响Baked光和Mixed光行为的设置。

  • Baked Global Illumination:勾选后,启用烘焙GI系统,在Generate Lighting时会生成烘焙光照数据。
  • Lighting Mode:光照模式。可选项有Shadowmask、Baked Indirect、Subtractive。
    • Shadowmask:对所有Mixed模式的灯光使用此模式。提供实时直接照明,而间接光被烘烤成光照贴图和光照探针。这种模式结合了实时和烘烤阴影,效果较好。
    • Baked Indirect:对所有Mixed模式的灯光使用此模式。提供实时直接照明,而间接光被烘烤成光照贴图和光照探针。实时提供阴影。
    • Subtractive:Mixed模式的灯光为静态物体提供直接和间接照明。动态对象接收实时直接照明,并使用定向光投射阴影。性能最好,效果最差。
5.4.3.4 Lightmapping Settings
  • Lightmapper:光照映射器。选择使用什么来计算场景中的光照贴图。有CPU、GPU两种选择。

    • Progressive Updates:启用此设置可使渐进光照映射器将更改应用于 Scene 视图中当前可见的纹素,然后将更改应用于视图外的纹素。

    • Importance Sampling:启用此选项以使用多重重要性采样对环境进行采样。当生成lightmaps时,这通常会导致更快的收敛,但在某些低频环境中可能会导致嘈杂的结果。这在默认情况下是禁用的。

    • Direct Samples:这个设置控制CPU、GPU采样数量,这些采样将用于计算直接光照。只能设置为2的幂。最大值为2的30次方。

    • Indirect Samples:这个设置控制CPU、GPU采样数量,这些采样将用于计算间接光照。只能设置为2的幂。最大值为2的30次方。

    • Environment Samples:环境样本属性决定了Unity向天空盒发射的环境光线的总数,以直接收集光线。Unity根据上下文从光贴图纹理或光探针位置发射这些光线。默认值为256。更高的值可能会产生更平滑的结果,但代价是烘烤时间的增加。只能将滑块设置为2的幂,最小值为1,最大值为2048。在HDR天空盒的场景中,通常需要更多的样本来减少最终光图或探头中的噪点。包含明亮的奇点(如太阳)或具有显著对比度的高频细节(如背光云)的天空盒场景也受益于更高数量的样本。

    • Light Probe Sample Multiplier:控制多少采样用于光照探针,这里设置的是一个乘数,会影响最终所用采样数。更高的值提高光照探针的质量,但他们将需要更长的烘烤时间。要启用此功能,请转到Project Settings > Editor,并禁用"Use legacy Light Probe sample counts"。此项默认值为4。

    • Max Bounces:Lightmapper所包含的直接光的最大反射次数。默认值2,范围[0,100]。设置为10适用于大多数场景,高于10的值可能会导致烘焙时间明显延长。每次反弹都会增加烘焙场景所需的计算资源。对室内场景可使用较高的值,对室外场景和有许多明亮表面的场景可以使用较低的值。

    • Filtering:过滤。配置Progressive Lightmapper对光照贴图进行后处理的方式,以限制噪点。对于光照贴图的后处理,光照贴图被分成直接遮挡,间接遮挡和环境遮挡三种类型,Unity分别应用后处理,然后将它们组合成一个单一的光照贴图。直接:任何从光源直接到达传感器(通常是相机)的光。间接:任何从光源间接到达传感器的光。这通常适用于从其他游戏对象反射的光。环境遮挡:光照系统计算的任何环境光。可配置选项:

      • None:选择此选项,对光照贴图不进行后处理去噪。

      • Auto:使用平台相关的预设来对光照贴图进行后处理。如果开发机器满足运行OptiX (NVIDIA OptiX AI-Accelerated Denoiser)的要求,Progressive Lightmapper使用高斯滤波器去噪器,该滤波器对所有目标具有1 texel(纹理)半径。如果开发机器不能运行OptiX,则Progressive Lightmapper会退回到OpenImageDenoise。

      • Advanced:为每种类型的光照贴图目标手动配置选项。目标类型有直接遮挡、间接遮挡和环境遮挡。有关详细信息,请参阅

  • Lightmap Resolution:指定用于光照贴图的每个单元的纹理数。增加这个值可以提高光照贴图质量,但也会增加烘焙时间。注意,加倍这个值会导致texels的数量翻四倍,因为它决定了光照贴图的高度和宽度。

  • Lightmap Padding:决定在光照贴图中分离形状之间的分离度(以texel为单元)。

  • Max Lightmap Size:最大光照贴图尺寸。默认1024。

  • Lightmap Compression:光照贴图压缩级别。

  • Ambient Occlusion:控制烘焙环境遮挡中表面的相对亮度。这只适用于我们用来烘焙光照的光照映射器所计算的间接照明。如果启用此属性,将会提供额外的三个设置:最大距离,间接贡献,和直接贡献。对于所有三种设置,更高的值表示遮挡和完全照亮区域之间的对比度更大。(三个设置官方文档写得很清楚,这里就不写了)

  • Directional Mode:使光照贴图能够存储物体表面上每个点的主要入射光的特征信息。可选项:

    • Directional:在此模式下,Unity生成第二个光照贴图来存储入射光的主要方向。这使得漫射法线贴图材质可以与全局照明系统一起工作。着色器在渲染过程中对两种光照贴图纹理进行采样。因此,定向模式需要的显存大约是非定向模式的两倍,以存储额外的光照贴图数据。此选项在烘焙时将会耗费更多性能。在SM2.0硬件或使用GLES2.0时,方向光照贴图无法解码。

    • Non-directional:此模式光照贴图只包含一个纹理。因此,它们比Directional光照贴图需要更少的显存和存储空间,并且在着色器中解码速度更快。但这些优化降低了视觉质量。

  • Albedo Boost:指定Unity在表面之间反射的光量。取值范围为1 ~ 10。增加这个值将会把用于间接光计算的反照率值拉向白色。默认值1在物理上是准确的。

  • Indirect Intensity:决定存储在实时和烘焙光照贴图中的间接光的亮度。这是一个介于0和5之间的值。大于1的值增加了间接光的强度,小于1的值减少了间接光的强度。默认值为1。

  • Lightmap Parameters:此为一个资源对象,存储了与烘焙GI相关的设置值。编辑器提供了几个默认的光照贴图参数资源可供选择,我们也可以使用选项下拉框中的Create New选项创建自己的光照贴图参数资源。

5.4.3.5 Workflow Settings
  • GPU Baking Device:使用此属性更改 Unity 用于预计算光照数据的 GPU。

  • Light Probe Visualization:用于过滤哪些光照探针显示在 Scene 视图中。默认值为 Only Probes Used By Selection。三种可选项:

    • Only Probes Used By Selection:只有影响当前选择的光照探针才会显示在 Scene 视图中。

    • All Probes No Cells:所有光照探针都将显示在 Scene 视图中。

    • All Probes With Cells:所有光照探针都将显示在 Scene 视图中,还会显示用于光照探针数据插值的四面体。

    • None:任何光照探针都不显示在 Scene 视图中。

    • Display Weights:启用此复选框时,Unity 将从用于有效选择的光照探针到用于插值的四面体上的位置绘制一条线。这是一种调试探针插值和放置问题的方法。

    • Display Occlusion:启用此复选框时,如果 Lighting Mode 设置为 Shadowmask,则 Unity 将显示光照探针的遮挡数据。

    • Hightlight Invalid Cells:高亮不能被用于光照探针插值的无效四面体。

  • Recalculate Environment Lighting:重新计算环境光照。勾选后,如果没有生成预先计算的照明数据,Unity会使用SkyManager自动计算所有开放场景的环境照明。

5.4.4 Environment

包含相关当前场景的环境光照效果的设置。可配置内容取决于我们的项目使用的渲染管线。

5.4.4.1 Environment

Environment 部分包含与光照相关的设置和控件,这些设置和控件适用于当前场景中的环境光照,例如天空盒、漫射光照和反射。

  • Skybox Material:天空盒材质,它出现在场景中的所有其他对象后方,用于模拟天空或其他遥远的背景。使用此属性可选择要用于场景的天空盒。默认值是内置的默认天空盒 (Default Skybox)。

  • Sun Source:选择一个光源作为场景中的太阳。Unity使用这个光来模拟天空盒和场景中太阳的位置和强度的效果。如果你将此设置为None, Unity将场景中最亮的方向光视为太阳。若所选光源的渲染模式属性设置为不重要,则其不会影响天空盒。

  • Realtime Shadow Color:定义Unity在Subtractive照明模式下渲染实时阴影时使用的颜色。

  • Environment Lighting:此部分包含可影响当前场景中的环境光的相关设置。

    • Source:定义场景中环境光的光源颜色。默认值为Skybox。可选有Skybox、Gradient、Color。Skybox:使用Skybox Material中设置的天空盒颜色来确定来自不同角度的环境光。这可以实现比Gradient更精细的效果。Gradient:可为来自天空、地平线和地面的环境光选择单独的颜色,并在它们之间平滑混合。Color:对所有环境光使用单调颜色。
  • Environment Reflections:此部分包含反射探针烘焙的全局设置,以及影响全局反射的设置。

    • Source:使用此设置来指定是否使用Skybox进行反射效果,或是选择Cubemap。默认值是Skybox。Skybox:选择此选项可使用天空盒作为反射源。Custom:选择此选项可以使用Cubemap资源或类型为cube的RenderTexture进行反射。

    • Resolution:使用此属性可设置用于反射的天空盒的分辨率。仅当Source设置为Skybox时,此属性才可见。

    • Cubemap:使用此属性可设置用于反射的Cubemap。仅当Source设置为Custom时,此属性才可见。

    • Compression:使用此属性可定义是否压缩反射纹理。默认设置是Auto。Auto:如果压缩格式合适,则压缩反射纹理。Uncompressed:反射纹理以非压缩状态存储在内存中。Compressed:压缩纹理。

    • Intensity Multiplier:反射源在反射对象中可见的程度。可以理解为反射强度。

    • Bounces: 当来自一个对象的反射随后被另一个对象反射时,便发生反射反弹。使用此属性可设置反射探针评估对象之间来回反弹的次数。如果设置为 1,则 Unity 只会考虑初始反射(即上面Source属性中指定的天空盒和Cubemap)。

5.4.4.2 Other Settings

Other Settings 部分包含雾(Fog)、光环(Halo)、光晕(Flare)、剪影(Cookies)的设置。

  • Fog:在场景中启用或禁用雾效。请注意,此模式无法用于延迟渲染路径。勾选后会出现额外三个属性。

    • Color:设置Unity在场景中所绘制的雾的颜色。

    • Mode:定义雾化效果随着与摄像机距离变化而积累的方式。有Linear、Exponential、Exponential Squared。Linear:雾效强度随着距离线性增加,同时具有两个可设置属性,Start:设置在距离摄像机多远时开始雾效,End:设置在距离摄像机多远时雾效完全遮挡场景游戏对象。Exponential: 雾效强度随着距离呈指数增加,有一个可设置属性,Density:用于控制雾效的强度,雾效强度随着Density增加而增加。Exponential Squared:雾效强度随着距离更快速增加(指数和平方),有一个可设置属性,Density:用于控制雾效的强度。雾效强度随着Density增加而增加。

  • Halo Texture:光环纹理。设置要用于在光源周围绘制光环的纹理。

  • Halo Strength:光环强度。定义光源周围光环的可见性,值在 0 到 1 之间。

  • Flare Fade Speed:光晕过渡速度。定义最初出现镜头光晕之后从视图中淡出的时间(以秒为单位)。默认情况下,该值设置为3。(PS:名字是Speed,文档却说设置值表示时间,真自由啊。)

  • Flare Strength:光晕强度。定义光源下镜头光晕的可见性,值在 0 到 1 之间。

  • Spot Cookie:设置你想要用于聚光灯的Cookie纹理(即,聚光灯默认情况下的Cookie)。默认为soft。若要选择soft,请选择None(即为soft)。

5.4.5 Realtime Lightmaps

显示实时GI系统生成的所有光照贴图。更具体的,显示了当前场景中由Enlightenment实时全局照明系统生成的所有光照贴图。如果项目中没有启用"Realtime Global Illumination",则此窗口将为空。

5.4.6 Baked Lightmaps

显示烘焙GI系统生成的所有光照贴图。更具体的,显示了由Lightmapper为当前场景生成的所有光照贴图,和光照数据资源(Lighting Data Asset)。如果我们使用场景视图绘制模式(Scene view Draw Modes)来预览光照映射,那么这个窗口将还包含Unity为预览生成的临时光照贴图。如果项目中未启用"Baked Global Illumination",则该窗口为空。

5.4.7 Probe Volumes

此窗口包含与Probe Volumes相关的设置。只有在项目中使用HDRP时才会出现此窗口。有关更多信息:Probe Volumes

5.4.8 Control Area

用于预计算光照数据的控件,其位于 Lighting 窗口的底部。

  • GPU Baking Device:使用这个来改变Unity用于预计算光照数据的GPU。此属性仅在使用GPU Progressive Lightmapper后端时可见。

  • GPU Baking Profile:在此属性中选择的配置文件定义了GPU Lightmapper如何将光照贴图分解为更小的块(tile)以减少GPU内存使用。选择自动配置文件后,Unity根据最大光照贴图的尺寸来选择tile大小,同时仍然以良好的GPU利用率为目标。最高性能(Highest Performance)和高性能配置文件(High Performance profiles)强制所有光照贴图使用一个更高的固定的tile尺寸。低内存使用(Low Memory Usage)和最低内存使用配置文件(Lowest Memory Usage profiles)强制所有光照贴图使用一个更低的固定的tile尺寸。更小的tile占用更少的GPU内存,以降低GPU利用率为代价,导致更长的烘烤时间。此属性仅在使用GPU Progressive Lightmapper时可见。

  • Generate Lighting:点击此按钮来预先计算所有打开场景的光照数据。这些数据包括"Baked Global Illumination"系统光照贴图、"(Enlighten )Realtime Global Illumination"系统光照贴图、光照探针、反射探针。在点击按钮开启烘焙流程后,再进行编辑操作不会影响烘焙光照。

    • 单击Generate Lighting右侧的下拉菜单,然后单击Bake Reflection Probes,只烘焙所有打开场景中的反射探针。单击Generate Lighting右侧的下拉菜单,

    • 单击Generate Lighting右侧的下拉菜单,然后点击Clear Baked Data,清除所有打开场景中的所有预先计算的光照数据,但不会清除GI缓存(Global Illumination cache)。

6 光照设置资源

光照设置资源(Lighting Settings Asset)是LightingSetting类的一个保存实例,该类存储了Baked Global Illumination和Enlighten Realtime Global Illumination系统的数据。当使用这两个系统中一个或两来预计算光照数据时,Unity会使用此类中存储的数据来进行预计算。

我们可以将一个Lighting Settings Asset或LightingSetting类的实例分配给多个场景,很容易的就让多场景使用相同的光照设置。

6.1 创建Asset文件

三种创建方式,第一种:

这种方式会在当前Project窗口目录中创建一个Asset文件。

第二种:

这种方式会在当前Project中创建一个Asset文件,然后直接分配给当前激活的场景。也可以点击Clone按钮复制当前激活的场景的Asset文件,复制的文件会放在当前Project中,并直接分配给当前激活的场景。

第三种:

我们还可以从脚本创建Lighting Settings Asset。为此,需要在脚本中创建一个 LightingSettings 类的实例并将其保存到磁盘,或将其指定给场景并保存该场景。有关更多信息和代码示例,请参阅 LightingSettings API 文档

6.2 将Asset设置给场景

在Lighting窗口中设置,就在上面第二种创建方式那个属性那里放Asset文件即可。只需要注意一点,Lighting面板对应的是激活场景,所以打开多个场景的时候只需要把我们要设置的场景调为激活状态即可。

还有一种就是脚本设置了,从脚本将Lighting Settings Asset指定给激活场景。比如加载Lighting Settings Asset以获取 LightingSettings 类的实例,然后使用Lightmapping.lightingSettings API将该LightingSettings实例指定给激活场景。有关更多信息和代码示例,请参阅 LightingSettings API 文档

6.3 查看和编辑Asset的属性

有三种方式:Asset文件的Inspector面板、Lighting窗口的Scene窗口、脚本访问。

Inspector面板:

Lighting窗口的Scene窗口:

脚本访问:

加载Lighting Settings Asset以获得LightingSettings类的实例,并访问其属性。有关更多信息和代码示例,请参阅LightingSettings API documentation

6.4 默认LightingSettings数据

当一个场景没有Lighting Settings Asset分配给它时,Unity使用该场景的默认LightingSettings对象。默认的LightingSettings对象是LightingSettings类的一个内部只读实例。

如果一个场景使用了默认LightingSettings对象,那么我们不能对此场景的LightingSettings数据进行任何更改,但是Unity可以使用此设置执行烘焙。

总之,想修改,就别用默认的。

6.5 属性介绍

见全局光照中的Lighting窗口介绍。【链接】

6.6 Lighting Mode

Lighting Mode 决定使用 Lighting Settings Asset 的场景中的说有混合光源的行为(烘焙的混合光)。可用的模式有:Baked Indirect、Shadowmask、Subtractive。Lighting Mode在Lighting面板中,位置如图:

6.6.1 Baked Indirect

当我们设置此模式时,混合光的行为就如同实时光一样,但着有额外的好处:将间接光烘焙到光照贴图中。此时混合光所照亮物体投射的阴影是实时的,且阴影最大距离是在项目中定义的阴影距离(Shadow Distance)。

6.6.1.1 渲染管线支持

有关跨渲染管线支持Baked Indirect Lighting Mode的更多信息,请参阅render pipeline feature comparison

6.6.1.2 混合光行为

点亮的动态物体能接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照探针)。

  • 来自动态游戏对象的(实时)阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

点亮的静态物体能接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照贴图)。

  • 来自动态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

6.6.1.3 阴影与运行时性能

在此模式下,所有来自混合光的阴影都是实时的,这可能会影响性能。不过我们可以使用阴影距离属性来限制 Unity 绘制实时阴影的最大距离,从而降低这种影响。

6.6.1.4 运行是改变光属性

在 Baked Indirect 光照模式下,可以在运行时巧妙地更改混合光源的属性。此类更改会影响混合光源对场景的实时直接光照,但不会影响混合光源对场景的烘焙间接光照。这样便可以将间接光照的优点与实时光照的某些动态功能结合起来。由于缺少预先计算的阴影(相对的意思就是阴影是实时的),因此在 Baked Indirect 光照模式下效果特别好。

在运行时对光源属性进行更改必须特别小心,只进行微小的更改,确保不会引起实时直接光照和烘焙间接光照之间不切实际的组合。例如,如果将红色混合光源烘焙到光照贴图中,然后在运行时将其颜色从红色更改为绿色,则直接光照将为绿色,但烘焙到光照贴图中的间接光照仍为红色。此情况同样适用于在运行时移动混合光源:直接光照将跟随光源的新位置,但间接光照将保持在光源烘焙位置。

以下视频举例说明了如何略微修改混合光源而不会导致间接光照出现明显不一致问题:https://youtu.be/XN6ya31gm1I(请自带魔法)

6.6.2 Shadowmask

Shadowmask模式与Baked Indirect模式类似,此模式将实时直接光照与烘焙间接光照相结合。但是,Shadowmask模式不同于Baked Indirect的地方是:Shadowmask渲染阴影。Shadowmask模式使Unity在运行时结合烘焙和实时阴影并在远处渲染阴影成为可能。它通过使用一个额外的光照贴图纹理,即阴影遮罩(shadow mask),并通过在光照探针中存储额外的信息来实现这一点。Unity为烘焙的阴影(即前面说的渲染的阴影)生成阴影遮罩和光照探针遮挡数据。(PS:我的理解是相比Baked Indirect,其多了阴影遮罩、光照探针的使用,由此可以在阴影距离之外渲染阴影。)

Shadowmask照明模式在所有照明模式中提供最高保真度的阴影,但具有最高的性能成本和内存要求。它适合在高端或中档硬件上渲染可以看到远处GameObjects的逼真场景,例如开放世界。

6.6.2.1 渲染管线支持

有关跨渲染管线支持Shadowmask Lighting Mode的更多信息,请参阅render pipeline feature comparison

6.6.2.2 Shadowmask质量设置

在Quality面板中可以设置Shadowmask的模式:

  • Distance Shadowmask:其以更高的性能成本提供更高的保真度阴影。

  • Shadowmask:其以较低的性能成本提供较低的保真度阴影。

6.6.2.3 混合光行为

Shadowmask质量设置为Distance Shadowmask模式下:

混合光源照亮的动态游戏对象将接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照探针)。

  • 来自动态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的烘焙阴影(使用光照探针),超过阴影距离 (Shadow Distance)。

混合光源照亮的静态游戏对象将接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照贴图)。

  • 来自动态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的烘焙阴影(使用阴影遮罩),超过阴影距离 (Shadow Distance)。

Shadowmask质量设置为Shadowmask模式下:

混合光源照亮的动态游戏对象将接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照探针)。

  • 来自动态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的烘焙阴影(使用光照探针),不超过以及超过阴影距离 (Shadow Distance)。

混合光源照亮的静态游戏对象将接收:

  • 实时直接光照。

  • 烘焙间接光照(使用光照贴图)。

  • 来自动态游戏对象的实时阴影(使用阴影贴图),不超过阴影距离 (Shadow Distance)。

  • 来自静态游戏对象的烘焙阴影(使用阴影遮罩),不超过以及超过阴影距离 (Shadow Distance)。

6.6.2.4 阴影与运行时性能

同上,使用阴影距离属性来限制 Unity 绘制实时阴影的最大距离。

6.6.2.5 阴影遮罩实现详细信息

在运行时,Unity 使用阴影遮罩来确定某个像素是否在阴影中。阴影遮罩纹理包含有关烘焙光源的遮挡信息。该纹理与相应的光照贴图共享相同的 UV 布局和分辨率。还针对每个纹素包含最多四个光源的遮挡信息(以 RGBA 格式存储)。

如果超过四个光源发生重叠,则多余光源会回退至烘焙光照 (Baked Lighting)。烘焙系统会决定哪个光源回退至烘焙光照,并在各烘焙之间保持一致,除非我们修改其中某个重叠的光源。光照探针也会接收最多四个光源的相同信息。

Unity 将独立于接收阴影的对象来计算光源重叠。因此,一个对象可受到全部来自同一个阴影遮罩/探针通道的十个不同混合光源的影响,只要这些光照包围体在空间中的任何点都不重叠即可。如果某些光源重叠,则 Unity 会使用更多通道。如果某个光源确实发生重叠而所有四个通道都在使用中,则该光源将回退至完全烘焙。【?没太懂...】

6.6.3 Subtractive

在 Subtractive 光照模式下,场景中的所有混合光源都提供烘焙直接光照和间接光照。Unity 将静态游戏对象投射的阴影烘焙到光照贴图中。除了烘焙阴影外,一种方向光(称为主方向光)还为动态游戏对象提供实时阴影。

因为阴影被烘焙到光照贴图中,所以 Unity 在运行时缺少将烘焙阴影和实时阴影准确地结合在一起所需的信息。但是,Unity 提供了 Realtime Shadow Color 属性来减少光照贴图的影响,从而在烘焙阴影和实时阴影之间创建正确的混合视觉效果。还可以调整颜色来实现某种艺术风格。

Subtractive 光照模式在低端硬件上非常有用,因为低端硬件需要注重性能,并且只需要一个实时阴影投射光源。这种光照模式不会提供特别逼真的光照效果,而是更适合风格化美学,例如卡通风格。

6.6.3.1 渲染管线支持

有关跨渲染管线支持Subtractive Lighting Mode的更多信息,请参阅render pipeline feature comparison

6.6.3.2混合光行为

混合光源照亮的动态游戏对象将接收:

  • 实时直接光照

  • 烘焙间接光照(使用光照探针)

  • 来自主方向光照亮的动态游戏对象的实时阴影,使用阴影贴图,不超过阴影距离 (Shadow Distance)

  • 来自静态游戏对象的(烘焙)阴影(使用光照探针)

混合光源照亮的静态游戏对象将接收:

  • 烘焙直接光照(使用光照贴图)

  • 烘焙间接光照(使用光照贴图)

  • 来自主方向光照亮的动态游戏对象的实时阴影,使用阴影贴图,不超过阴影距离 (Shadow Distance)

  • 来自静态游戏对象的烘焙阴影(使用光照贴图)

6.6.3.3 更改阴影颜色

将场景的 Lighting Mode 设置为 Subtractive 时,Unity 将在 Lighting 窗口中显示 Realtime Shadow Color 属性。Unity 将实时阴影与烘焙阴影结合在一起时将使用此颜色。更改此值可以近似于场景内间接光照的颜色,使实时阴影与烘焙阴影更好地匹配。(PS:在Lighting窗口的Enviroment窗口中可找到此属性,而且也不是必须设置为Subtractive才显示。)

6.6.3.4 主方向光

Unity 会自动在场景中选择强度值最高的方向光作为主方向光。

7 Light Explorer窗口

7.1 窗口介绍

在 Light Explorer 窗口中可选择和编辑光源。要从菜单中打开 Light Explorer 窗口,请导航至 Window > Rendering > Light Explorer。窗口如图:

使用面板顶部的四个选项卡来查看当前场景中的灯光、2D灯光、反射探针、光照探针和静态放射(比如自发光材质)的设置,可编辑的参数是每种组件类型最常用的字段。使用搜索字段可在每个选项卡中筛选名称。我们也可以勾选Isolate Selection复选框,这样只有我们在场景中选中的对象才会显示到面板中,或是勾选Show Inactive Objects,在列表中显示那些被隐藏的对象。

7.2 窗口扩展

通过 Light Explorer 扩展,可以创建自定义版本的 Light Explorer 窗口。可以将这种扩展用于调整 Light Explorer 窗口的功能,以便其可处理"可编程渲染管线 (SRP) "或者"高清渲染管线的自定义光源"。

在 Light Explorer 窗口中,可以看到场景中的所有光源并可编辑它们的属性。使用此扩展,可以通过多种方式扩展当前窗口。例如,可以执行以下操作:

  • 更改选项卡(从简单地更改选项卡名称到添加我们自己的自定义选项卡)以显示不同类型的游戏对象的列表。例如,如果要显示我们自定义的反射探针的属性信息,这将很有用。

  • 更改选项卡上的列(同样从更改名称到添加您自己的自定义列),这里列就是指属性。例如,我们想要查看光源的其他属性。

7.2.1 扩展方法

要扩展 Light Explorer,可以继承自:

  • ILightingExplorerExtension 接口,然后重写 GetContentTabs 方法。

  • DefaultLightingExplorerExtension 类(继承自 ILightingExplorerExtension)。此类提供窗口中已经存在的所有内容。如果只希望重写选项卡的数量、每个选项卡的标题或要显示的光源,请使用此选项。要了解如何以这种方式扩展 Light Explorer,请参阅下面的示例(下面代码是官方文档里的,这里我就不深入看了,只粘贴上来)。

下面的示例展示了如何扩展默认的 Light Explorer 类以仅显示光源的名称 (Name) 列,以及如何更改选项卡的数量。在我们自己的实现中,可以根据需要重写任意数量的方法。以下示例显示了光源的名称列:

cs 复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;

[SupportedOnRenderPipeline(typeof(ExampleRenderPipelineAsset))]
public class SimpleExplorerExtension : DefaultLightingExplorerExtension
{
    private static class Styles
    {
        public static readonly GUIContent Name = EditorGUIUtility.TrTextContent("Name");
    }
    
    protected override LightingExplorerTableColumn[] GetLightColumns()
    {
        return new[]
        {
            new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 0: Name
        };
    }
}

以下示例仅显示光源的名称和启用状态,并隐藏 Emissive Materials 选项卡(仅显示 3 个选项卡,而不是 4 个)(PS:这不是显示4个吗?)

cs 复制代码
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;

[SupportedOnRenderPipeline(typeof(ExampleRenderPipelineAsset))]
public class ComplexLightExplorerExtension : DefaultLightingExplorerExtension
{
    private static class Styles
    {
        public static readonly GUIContent Name = EditorGUIUtility.TrTextContent("Name");
        public static readonly GUIContent Enabled = EditorGUIUtility.TrTextContent("Enabled");
    }
    
    protected override UnityEngine.Object[] GetLights()
    {
        return Resources.FindObjectsOfTypeAll<Light>();
    }

    protected override LightingExplorerTableColumn[] GetLightColumns()
    {
        return new[]
        {
            new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Name, Styles.Name, null, 200), // 0: Name
            new LightingExplorerTableColumn(LightingExplorerTableColumn.DataType.Checkbox, Styles.Enabled, "m_Enabled", 25), // 1: Enabled
        };
    }

    public override LightingExplorerTab[] GetContentTabs()
    {
        return new[]
        {
            new LightingExplorerTab("Lights", GetLights, GetLightColumns, true),
            new LightingExplorerTab("2D Lights", Get2DLights, Get2DLightColumns, true),
            new LightingExplorerTab("Reflection Probes", GetReflectionProbes, GetReflectionProbeColumns, true),
            new LightingExplorerTab("Light Probes", GetLightProbes, GetLightProbeColumns, true),
            new LightingExplorerTab("Static Emissives", GetEmissives, GetEmissivesColumns, false),
        };
    }
}

7.2.2 有用的类和方法

下面列出了可用于扩展 Light Explorer 的类和方法(就上面说的那两个继承类):

ILightingExplorerExtension:

cs 复制代码
public virtual LightingExplorerTab[] GetContentTabs();
public virtual void OnEnable() {}
public virtual void OnDisable() {}

DefaultLightingExplorerExtension(继承自 ILightingExplorerExtension):

cs 复制代码
public virtual LightingExplorerTab[] GetContentTabs();
public virtual void OnEnable() {}
public virtual void OnDisable() {}

protected virtual UnityEngine.Object[] GetLights();
protected virtual LightingExplorerTableColumn[] GetLightColumns();

protected virtual UnityEngine.Object[] GetReflectionProbes();
protected virtual LightingExplorerTableColumn[] GetReflectionProbeColumns();

protected virtual UnityEngine.Object[] GetLightProbes();
protected virtual LightingExplorerTableColumn[] GetLightProbeColumns();

protected virtual UnityEngine.Object[] GetEmissives();
protected virtual LightingExplorerTableColumn[] GetEmissivesColumns();

8 光照映射

光照映射是预先计算场景中表面亮度的过程,并将结果存储在称为"光照贴图"的纹理中供以后使用。

左:一个简单的光照映射场景。右:Unity 生成的光照贴图纹理。请注意捕获阴影和光照信息的方式。

​ 光照贴图可以包含直射光和间接光。该光照纹理可与颜色(反照率)和浮雕(法线)之类的对象表面信息一起使用,通过与材质相关联的着色器。烘焙到光照贴图中的数据无法实时更改。实时光可以在光照贴图场景上叠加和使用,但不能交互式地更改光照贴图本身。通过这种方法,我们用在游戏中移动灯光的能力来换取潜在的性能提升,以适应功能较弱的硬件,如移动平台。

​ Unity通过 Progresssive Lightmapper来烘焙光照贴图。

8.1 Progressive Lightmapper

Progressive Lightmapper是一个快速的基于路径跟踪的Lightmapper系统,其提供了能在 Editor 中逐渐刷新的烘焙光照贴图和光照探针。要求不重叠的 UV 具有较小的面积和角度误差,以及棋盘格图表之间有足够的填充。

Progressive Lightmapper采取了一个短暂的准备步骤来处理几何体与实例的更新,同时生成 G-buffer 和图表遮罩。然后,它会立即生成输出,并随着时间的推移逐步细化输出,以实现更完善的交互式照明工作流。此外,烘焙时间更加可预测,因为Progressive Lightmapper在烘焙时提供估计时间。

Progressive Lightmapper还可单独为每个纹素(texel)分别以光照贴图分辨率烘焙全局光照 (GI),无需采用上采样方案或依赖任何光照强度缓存或其他全局数据结构。因此,Progressive Lightmapper具有强大的功能,并允许我们烘焙光照贴图的选定部分,从而更快测试和迭代场景。

如需观看介绍交互式工作流程的深入视频,请参阅 Unity 的视频演练:开发中 - 渐进光照贴图 (In Development - Progressive Lightmapper) (YouTube)

8.1.1 CPU、GPU Progressive Lightmapper

可以在Progressive Lightmapper的两个后端之间进行选择。Progressive CPU Lightmapper是使用计算机 CPU 和系统 RAM 的后端。Progressive GPU Lightmapper是使用计算机 GPU 和 VRAM 的后端。

8.1.2 渲染管线支持

有关跨渲染管线支持Progressive Lightmapper的更多信息,请参阅render pipeline feature comparison

8.1.3 平台兼容性

苹果硅版本的Unity Editor与CPU Progressive Lightmapper不兼容。但是,它与Progressive GPU Lightmapper兼容。

8.1.4 设置Progressive Lightmapper

在Lighting面板中设置其相关属性,如图:

CPU、GPU后端选择,一些其他属性都是在这里设置。具体每个属性的含义,参考"全局光照-Lighting窗口"【链接】。我们可以使用 LightmapEditorSettingsLightmapping API,通过脚本执行此窗口中提供的许多函数。

8.1.5 高级过滤设置

将Filtering属性设置为Advanced,窗口中将会出现一些新的属性,这些属性按光照贴图类型划分,我们可手动配置每种类型的光照贴图的相关属性。类型分为:

  • Direct:直接光。

  • Indirect:间接光。

  • Ambient Occlusion:光照系统计算的任何环境光(环境光)。

属性有:

  • Denoiser:为光照贴图选择一个去噪器(Denoiser)。可选项有:

    • Optix:NVIDIA Optix去噪器是一种AI加速去噪器,可以减少烘烤光图中的噪声。它需要NVIDIA GeForce, Quadro或Tesla GPU,带有Maxwell或新一代架构,使用驱动程序版本R495.89或以上。Optix仅支持Windows操作系统。

    • OpenImageDenoise:英特尔的OpenImageDenoise是一个ai加速的去噪器,可以减少烘烤光图中的噪点。

    • None:不使用去噪器。

  • Filter:选择要用于光照贴图目标的过滤器。可选项:

    • Gaussian:选择此选项可将 Gaussian 过滤器用于光照贴图目标。Gaussian 过滤器将双向 Gaussian 过滤器应用于光照贴图。这会使光照贴图模糊并减少可见噪点。

    • A-Trous:选择此选项可将 A-Trous 过滤器用于光照贴图目标。A-Trous 过滤器可以将模糊程度降至最低,同时可以消除光照贴图中的可见噪点。

    • None:不使用过滤器。

  • Radius:仅当 Filter 设置为 Gaussian 时,此选项才可用。使用 Radius 值可设置 Gaussian 过滤器内核的半径(以纹素为单位)。较高的 Radius 值可以提高模糊强度并减少可察觉的噪点,但可能导致光照中的细节丢失。

  • Sigma:仅当 Filter 设置为 A-Trous 时,此选项才可用。使用 Sigma 值可调整保留细节或使光照模糊的程度。较高的 Sigma 值可以提高模糊强度并减少可察觉的噪点,但可能导致光照中的细节丢失。

8.1.6 统计信息

Lighting窗口中,在Generate Lighting按钮下方的面板显示了有关光照映射的统计信息,包括:

  • Unity 已创建的光照贴图数量。

  • 内存使用量 (Memory Usage):当前光照贴图所需的内存量。

  • 占用的纹理像素 (Occupied Texels):在光照贴图 UV 空间中占用的纹理像素数量。

    • 融合 (Converged):这些光照贴图的所有计算都已完成。

    • 未融合 (Not Converged):这些光照贴图的烘焙仍在进行中。

  • 烘焙性能 (Bake Performance):每秒的光线数量。如果此值很低(即小于 2),则应调整"设置"或"硬件"以便一次处理更多光线。

8.1.7 ETA

Unity 烘焙光照贴图时出现的进度条提供了"预计到达时间"(显示为 ETA),这是完成当前烘焙的估计时间(秒)。可以让我们快速了解当前光照设置所需的烘焙时间。

8.2 Progressive GPU Lightmapper

Progressive GPU Lightmapper是Progressive Lightmapper的后端,它使用计算机的GPU和Dedicated Video Ram(VRAM)来生成烘焙的光照贴图和光照探针。

8.2.1 硬件和软件要求

为了使用Progressive GPU Lightmapper,计算机需要满足以下最低要求:

  • 至少一个支持 OpenCL 1.2 的 GPU。

  • 至少2GB的VRAM专用于该GPU。

  • 一个支持 SSE4.1 指令的 CPU。

如果我们正在烘焙的场景所需的 VRAM 多于指定 GPU 上可用 VRAM,则烘焙时间会显著增加。有关帮助我们减少烘烤场景所需时间的信息,请参阅下方性能(Performance)。另外,Progressive GPU Lightmapper不支持OpenCL CPU设备。

8.2.2 性能

在大多数配置中,Progressive GPU Lightmapper比Progressive CPU Lightmapper执行得快得多。性能取决于所选择的烘焙配置文件,但是,用于烘焙的GPU规格和专用于该GPU的VRAM数量都会影响我们对lightmapper性能的体验。其他抢夺GPU资源的应用程序也会影响烘焙性能。取决于硬件供应商,减少Unity可用的VRAM数量可能会减慢烘焙过程或导致它失败。

为了获得与Progressive CPU Lightmapper相同质量的结果,Progressive GPU Lightmapper需要多达四倍的间接光采样。Progressive CPU Lightmapper使用一种称为分支路径追踪的方法,它在每次反弹时发射更多的光线。分支路径跟踪方法不适合GPU。

Unity提供了一个电子表格来帮助我们确定Progressive GPU Lightmapper需要多少内存来烘焙我们的场景。有关更多信息,请参阅Progressive GPU Lightmapper Memory Spreadsheet

8.2.3 防止因光照贴图器tiling引起的烘烤速度减慢

Progressive GPU Lightmapper包含一个称为lightmapper tiling的功能。lightmapper tiling防止Progressive GPU Lightmapper使用所有可用的GPU内存。相反,Unity会在CPU上分配一些临时内存,然后将灯光烘焙进tiles中,tiles的尺寸与GPU可用内存的数量相匹配。Unity根据所选择的烘焙配置文件配置tiling。另外,Progressive CPU Lightmapper没有这种功能。

当tiling功能被激活,同时光照贴图图集分辨率为512px或更大时,烘焙相同的场景,烘焙过程可能比不使用tiling功能的要慢。

8.2.4 特定平台限制

在macOS上,很难确定有多少内存可用。因此,在此平台上更适合退回到CPU,并且按照下面"优化烘焙速度"的说明进行操作是比较合适的。

8.2.5 如何优化烘焙速度

有多种方法可以减少烘焙时间,并避免烘焙超过指定给 GPU 用于烘焙的 VRAM 的情况。

  • 关闭其他具有GPU加速的应用程序。GPU加速的2D图像编辑和3D建模软件会使用VRAM,可以关闭图形加速功能或退出这些应用程序。

  • 对较小的游戏对象使用光照探针。碎片或小道具等游戏对象会占用光图中的空间,但可能不会对场景的外观产生重大影响。因此,为了优化烘焙速度,可以禁用这些游戏对象的Mesh Renderer的Contribute Global Illumination(实际就修改为非静态了,至少在GI这方面非静态了,如果其他方面有勾选静态那还是静态的),并使用光照探针照亮它们。

  • 指定单独的GPU用于渲染和烘焙。如果你的电脑有多个GPU,你可以指定一个用于渲染,一个用于烘焙。请参见下面的配置GPU选择。

  • U使用较少数量的抗锯齿(Anti-aliasing)采样。在Lightmap Parameters Asset(后面会讲,一个设置参数的Asset文件)中这个设置的默认值是8。当你增大这个值时,Unity会使用更多的VRAM。当我们使用4096或更高大的光照贴图时,这可能很快溢出许多消费级GPU的内存。

  • 使用更少的采样(直接采样,间接采样,环境采样。这些都是Lighting窗口中的可设置属性),并使用去噪器来清除光图中剩余的噪声。

  • 降低光照贴图的分辨率。

8.2.6 配置GPU的选择

如果我们至少有两个GPU,我们可以指定一个GPU用于渲染场景,另一个用于烘焙光照。在默认GPU可用的VRAM不足的情况下,这可能是可取的,我们既可以渲染场景,也可以使用Progressive GPU Lightmapper进行烘焙。

我们需要在Lighting面板中,设置Unity使用哪个GPU进行烘焙:

8.3 光照映射:入门

从Unity Editor菜单中选择Window > Rendering > Lighting打开Lighting窗口。确保你想要应用光照贴图的任何网格都有适当的UVs进行光照映射,最简单的方法是打开Mesh import settings并启用Generate Lightmap UVs设置。接下来,要控制光照贴图的分辨率,请前往 Lighting窗口中的Lightmapping Settings 部分并调整 Lightmap Resolution 值。

想要被包含在光照贴图中(即使用光照贴图功能),渲染器必须满足以下标准:

  • 具有 Mesh Renderer 或 Terrain 组件。

  • 标记为 Contribute GI(设置为静态了)。

  • 使用内置的 Unity 材质、标准着色器 (Standard Shader) 或具有 Meta Pass 的着色器

然后就是烘焙创建光照贴图了(光照映射),就前面全局光照中讲的实时GI、烘焙GI。当执行完成后,Unity的场景和游戏视图会自动更新,同时,Unity 将向Assets文件夹中添加光照数据资源、烘焙光照贴图、反射探针(若用到了),一般是根据场景名创建一个同名文件夹存放内容。

8.4 预览光照映射

我们可以编辑场景并预览光照贴图和光照的变化,而不会影响烘焙的光照映射。

直接看官方文档吧,我用的Unity版本有点低了,没法演示。

8.5 Lightmap Parameters Asset

光照贴图参数资源(Lightmap Parameters Asset)中包含了一组用于控制 Unity 光照功能的参数值。这些资源允许定义和保存不同的光照值集合,以便用于不同的情况。光照贴图参数资源可以用来为不同类型的游戏对象或为不同平台和不同场景类型(例如,室内或室外场景)创建优化的预设。

8.5.1 创建

8.5.2 属性

选中创建的文件,即可在Inspector面板中看到相关属性:

8.5.2.1 Realtime Global Illumination

这些参数用于配置Enlighten Realtime Global Illumination(其光照贴图)。请参render pipeline feature comparison,了解更多关于跨渲染管线支持Enlighten Realtime Global Illumination的信息。(目前下面讲的这个应该是内置渲染管线的)

  • Resolution:此值在Lighting窗口的Scene选项卡中缩放Realtime Resolution(【没找到这个,或者说不太确定这里指的是选项卡中的哪个属性】),以每单位距离的纹理像素来给出光照贴图的最终分辨率。

  • Cluster Resolution:聚类分辨率(内部计算光反射的分辨率)与最终光照贴图分辨率的比率。有关更多信息,请参阅GI Visualizations in the Scene view

  • Irradiance Budget:此值确定用于照射光照贴图中每个纹理像素的入射光数据的精度。从纹理像素的位置对场景的"视图"采样来获得每个纹理像素的光照。较低的辐照度预算值会导致样本更模糊。值越高,样本的清晰度越高。较高的辐照度预算会改善光照,但这会增大运行时内存使用量并可能增大 CPU 使用率。

  • Irradiance Quality:使用滑动条来定义投射的射线数量,并用于计算哪些聚类影响给定的输出的光照贴图纹理。较高的值可在光照贴图中实现视觉改善,但会增加 Unity Editor 中的预计算时间。该值不会影响运行时性能。

  • Modelling Tolerance:此值控制网格几何体中允许光线通过的间隙的最小大小。降低此值可允许光线穿过环境中更小的间隙。

  • Edge Stitching:如果启用此属性,则表示光照贴图中的 UV 图表应无缝连接在一起,从而避免不必要的视觉瑕疵。

  • Is Transparent:如果启用此属性,在全局光照计算期间,对象显示将为透明。背面不会参与计算,光线将穿过表面。这可用于不可见的发光表面。

  • System Tag:一组对象,它们的光照贴图纹理组合在同一个光照贴图图集内,这组对象称为"系统"。如果无法将所有对象放入单个图集中,Unity Editor 会自动定义其他"系统"及其附带的图集。但是,有时自己定义单独的"系统"会非常有用(例如,不同房间内有多个对象,每个房间单独定义一个系统来划分对象)。更改System Tag的数值可强制创建新的系统和光照贴图。这些数值的序列不重要,即顺序不重要。

8.5.2.2 Baked Global Illimination

这些参数用于配置Lightmapping(光照映射,这里应该说Baked Global Illimination的光照贴图更好理解)。请参render pipeline feature comparison,了解更多关于跨渲染管线支持lightmapping的信息。(目前下面讲的这个应该是内置渲染管线的)

  • Anti-aliasing Samples:(Anti-aliasing可以理解为抗锯齿)。确定采样光照贴图纹理时要使用的子纹理位置的数量。将值设置在1以上,使用超采样来提高光图质量并减少与混叠(aliasing)相关的伪影。

    • 值为1禁用超级采样。

    • 值为4提供2x2超采样。这是移除大多数混叠伪影的默认值。

    • 值为16提供4x4的超采样。使用这些值来去除伪影,比如直接照明下的锯齿状边缘。

    • 注意:更高的Anti-aliasing Samples值使用更多的内存。这意味着如果我们在一个大的场景中,并且光照贴图的纹理尺寸较大,这时我们使用一个较大的Anti-aliasing Samples值,光照贴图的烘焙操作可能无法完成。

  • Backface Tolerance:texel可能被正面、背面光照。此属性用于选择有多少光量(百分比阈值)来自正面几何体才能将texel视为有效。这使得如果太多光线投射到背面(例如,如果texel在几何体内部),Unity可能会使texel无效。例如,1.0的值意味着当Unity的任何光线击中背面时,Unity认为texel无效(因为要求100%,所以有一个背面的就无效)。当一个纹理无效时,Unity会从周围的纹理克隆有效值来防止伪影。降低此值以解决由背面采样引起的照明问题。可使用Texel Validity Scene View Draw模式来调整此值。

  • Pushoff:根据我们在建模单位中指定的值,沿着法线将光线原点推离几何。Unity将此值应用于所有烘焙的光照贴图。它影响直接光,间接光和烘焙环境遮挡。调整此设置以减少自遮挡和自阴影伪影。

  • Baked Tag:以图集来对对象进行划分。与System Tag一样,数值序列并不重要。Unity永远不会将具有不同烘焙标签值的GameObjects放在同一个图集中。但是,不能保证具有相同标记的对象最终会出现在同一个图集中,因为可能有太多具有相同标记的对象无法放入一个图集中。当我们使用多场景烘焙API时,我们不需要设置这个值,因为Unity在这种情况下会自动分组。我们可以使用Baked Tag来复制Lock Atlas选项的一些行为。请参下面的Baked Tags: Details了解更多信息。

  • Limit Lightmap Count:限制光照贴图数量。勾选此属性后,会出现一个Max Lightmaps属性,决定光照贴图数量的最大值。Unity认为如果游戏对象它们具有相同的Anti-aliasing Samples,Pushoff,Baked Tag和Backface Tolerance,则这些游戏对象具有相同的Baked Global Illumination settings。这意味着Unity可能会将拥有不同Lightmap Parameters Asset的GameObjects打包在一起。为了将游戏对象打包到一定数量的光照贴图中,Unity会按比例缩小UV布局,直到所有物体都符合指定数量的光照贴图,这个过程可能会降低光照贴图的分辨率。

8.5.2.3 Baked Tags: Details

上面的两张图显示了同一场景的两个视图:

  • 上图:所有东西都在一个图集中,因为所有的GameObjects都有相同的Baked Tag。

  • 下图:其中一个GameObject被分配了一个不同的Baked Tag,则其并被强制放到第二个光照贴图(第二个光照贴图属于另一个图集)中。

  • PS:为什么说Unity官方文档辣鸡,上图还在说图集,下图就变成光照贴图了,说清楚点有那么难?看个文档看得血压上升。

8.5.3 Baked AO

这些参数用于配置烘焙环境遮挡(Baked Ambient Occlusion,Baked AO)。其中,某些参数都来自上面"属性"中。

  • Quality:评估Baked Ambient Occlusion时投射的光线数。较高的射线数增加了AO质量,但也增加了烘焙时间。

  • Anti-aliasing Samples:对AO进行Anti-aliasing(抗锯齿)时需要采集的样本数量。较高的样品数量增加了AO质量,但也增加了烘焙时间。

8.5.4 分配Lightmap Parameters Asset

8.5.4.1 分配给场景

如图在Lighting窗口中设置:

若我们创建了新的Asset文件,在下拉列表中也可以找到。

8.5.4.2 分配给游戏对象

首先需要确保游戏对象已附加网格渲染器 (Mesh Renderer) 或地形 (Terrain) 组件。对于Mesh Renderer的分配:

对于Terrain的分配:

8.6 光照贴图模式

8.6.1 模式

光照贴图有两种模式:Directional、Non-Directional。这两种模式都兼容Unity的Enlighten Realtime Global Illumination系统的实时光照贴图,以及Unity的Progressive Lightmapper的烘焙光照贴图。默认模式为"Directional"。

当我们烘焙Directional光照贴图时,Unity 会生成两个光照贴图纹理。一个纹理存储光照的强度和颜色的信息,此光照穿过目标表面而接收到,这与Non-Directional光照贴图相同。另一个纹理存储主光照方向,以及描述接收到的总光照中来自该主方向的比例。(Directional相比Non-Directional,多了一个光照贴图纹理)

如图,桶烘焙了Non-Directional光照贴图:

如图,桶烘焙了Directional光照贴图:

8.6.2 性能问题

Directional模式光照贴图由两个纹理组成,着色器在渲染过程中对这两个纹理进行采样。额外的纹理增加了对显存需求。生成额外的方向性纹理也会影响烘焙性能。

Non-Directional模式光照贴图仅包含单个纹理。因此,与Directional贴图相比,它们需要的显存和存储空间更少,并且在着色器中解码速度更快。但是,这些优化会降低视觉质量。

8.6.3 设置光照贴图的模式

我们需要在Lighting Settings Asset中进行设置。前面说过怎么设置这个资源文件,这里我们以使用Lighting窗口为例来设置光照贴图模式:

8.6.4 附加场景时的注意点

Unity 可以以附加方式加载场景。这意味着您可以进行多个场景的编辑。若我们使用Directional模式。当我们以附加方式加载场景时,所有场景都必须使用相同的Directional模式,这包括未烘焙的场景,例如 UI 元素或加载屏幕。对于项目中的所有场景使用相同的Lightmap Parameters asset可以帮助我们避免设置冲突。(PS:什么意思?应该是说要模式相同,然后光照贴图的参数也相同吧。)

8.7 环境光遮挡

环境光遮挡 (Ambient occlusion, AO) 功能可以模拟折痕线、小孔和相互靠近的表面中出现的柔和阴影。这些区域将遮挡(阻挡)环境光,因此它们看起来较暗。此功能可以估算环境光照射到表面上某一点的光照强度,然后,使折痕线、小孔和相互靠近的表面变暗。环境光遮挡功能可用于增添光照的真实感。

8.7.1 烘焙环境光遮挡

如果在场景中启用了 Baked Global Illumination,Unity 可以将环境光遮挡烘焙到光照贴图中。这称为烘焙环境光遮挡 (Baked Ambient Occlusion)。要在场景中启用烘焙环境光遮挡,在Lighting面板中勾选相关属性即可,如图:

8.7.2 实时环境光遮挡

如果我们的场景中没有启用Global Illumination,但我们仍然想要环境遮挡的效果,那么我们可以使用一个后期处理效果来应用实时环境遮挡(Realtime ambient occlusion)到我们的场景中。

如果在场景中启用了Enlighten Realtime Global Illumination,则间接照明的分辨率无法捕获精细细节或动态对象。这里建议使用实时环境遮挡后处理效果(即用后处理),它有更多的细节,并产生更高质量的照明。(PS:这里也没说后处理之外的实时环境光遮挡使用方法呀...)

有关实时环境遮挡后处理效果的信息,请参见post-processing effects

8.7.3 其他资源

想要更详细了解其他内容,请参阅 Wikipedia:环境光遮挡 (Ambient Occlusion)

8.8 光照贴图:技术信息

Unity 可存储采用不同压缩和编码方案的光照贴图,具体取决于目标平台以及 Lighting 窗口中的压缩设置。

8.8.1 编码方案

Unity 项目可以使用两种技术在必要时将烘焙光源强度范围编码为低动态范围纹理:

  • RGBM 编码。RGBM 编码将颜色存储在 RGB 通道中,将乘数 (M) 存储在 Alpha 通道中。在线性空间中,RGBM 光照贴图的范围是 0 到 34.49(52.2),而在伽马空间中,范围是 0 到 5。

  • 双低动态范围 (dLDR) 编码。只需直接将 [0, 2] 范围映射到 [0, 1],即可在移动平台上使用 dLDR 编码。值高于 2 的烘焙光源强度将被钳制。编码值的计算方法是:光照贴图纹理的值乘以 2(如果使用伽马空间),或乘以 4.59482(22.2)(如果使用线性空间)。某些平台将光照贴图存储为 dLDR,这是因为在使用 RGBM 时,这些平台的硬件压缩会产生外观不佳的瑕疵。

使用线性颜色空间时,光照贴图纹理将标记为 sRGB,着色器使用的最终值(采样和解码后)将位于线性颜色空间中。使用伽马颜色空间时,最终值将在伽马颜色空间中。

注意:使用编码时,存储在光照贴图(GPU 纹理内存)中的值始终位于伽马颜色空间中。

UnityCG.cginc着色器 include 文件中的 Decode Lightmap 着色器函数负责在从着色器中的光照贴图纹理读取值之后解码光照贴图值。

8.8.2 HDR光照贴图支持

我们可以在Windows、Mac、Linux、iOS、tvOS和Android上使用HDR光照贴图。要控制这些平台的光照贴图编码/压缩,请转到 Edit > Project Settings > Player > Other Settings > Lightmap Encoding。如图:

选择High Quality将启用HDR光图支持,而Normal Quality将切换到使用RGBM编码,Low Quality将在移动平台上切换到dLDR编码,在其他平台上它相当于Normal Quality。

在桌面平台和游戏主机平台上,Lighting 窗口中启用了光照贴图 Compression 时,将使用 BC6H 压缩格式来压缩光照贴图。对于移动平台,Unity 根据下表选择 HDR 格式。

8.8.3 高质量(BC6H)光照贴图的优点

  • HDR 光照贴图不使用任何编码方案来编码光照贴图值,因此支持的范围仅受到 16 位浮点纹理格式的限制(范围从 0 到 65504)。

  • BC6H 格式质量优于 DXT5 + RGBM 格式编码,并且不会产生 RGBM 编码所具有的任何色带瑕疵。

  • 需要对 HDR 光照贴图进行采样的着色器是一些较短的 ALU 指令,因为不需要对采样值进行解码。

  • BC6H 格式与 DXT5 具有相同的 GPU 内存要求。

以下列表列出了每个目标平台的编码方案及其纹理压缩格式:

目标平台 编码 压缩 - 大小(每像素位数)
独立平台(PC、Mac 和 Linux) RGBM / HDR DXT5 / BC6H - 8 bpp
WebGL 1.0 / 2.0 RGBM DXT5 - 8 bpp
iOS ASTC (1) dLDR / RGBM / HDR ASTC - 3.56 bpp / ASTC - 3.56 bpp / RGB9E5 - 32 bpp
iOS PVRTC dLDR / RGBM / HDR PVRTC RGB - 4 bpp / ETC2 RGBA - 8 bpp / RGB9E5 - 32 bpp
tvOS dLDR / RGBM / HDR ASTC - 3.56 bpp / ASTC - 3.56 bpp / RGB9E5 - 32 bpp
Android ASTC (2) dLDR / RGBM / HDR ASTC - 3.56 bpp / ASTC - 3.56 bpp / ASTC HDR - 3.56 bpp
Android ETC2 dLDR / RGBM / HDR ETC2 RGB - 4 bpp / ETC2 RGBA - 8 bpp / ASTC HDR - 3.56 bpp
Android ETC dLDR / RGBM / HDR ETC1 RGB - 4 bpp / ETC2 RGBA - 8 bpp / ASTC HDR - 3.56 bpp

1\] iOS上用于光照贴图的纹理压缩格式取决于[Player Settings](https://docs.unity3d.com/cn/2023.2/Manual/iphone.html "Player Settings")中的纹理压缩格式设置。 \[2\] Android上用于光照贴图的纹理压缩格式取决于[Player Settings](https://docs.unity3d.com/cn/2023.2/Manual/class-PlayerSettingsAndroid.html#Rendering "Player Settings")和 [Build Settings](https://docs.unity3d.com/cn/2023.2/Manual/android-build-settings.html "Build Settings")。有关这些设置如何相互作用的更多细节,请参阅 [Texture compression settings](https://docs.unity3d.com/cn/2023.2/Manual/android-requirements-and-compatibility.html#texture-compression "Texture compression settings")。 #### 8.8.4 预计算实时全局光照 (GI) GI系统的输入有着不同的范围和编码的输出。表面反照率在伽马空间为8位无符号整数RGB,发射率在线性空间为16位浮点RGB。有关使用meta pass提供自定义输入的建议,请参阅下方面的"光照映射与着色器"小节。 光照强度输出纹理将以 RGB9E5格式存储(如果图形硬件支持此格式),其共享指数浮点,或者以范围在 5 以内的 RGBM 作为后备格式。RGB9E5 光照贴图的范围是 \[0, 65408\]。有关 RGB9E5 格式的详细信息,请参阅 [Khronos.org:EXT_texture_shared_exponent](https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt "Khronos.org:EXT_texture_shared_exponent")。 ### 8.9 光照映射与着色器 本节主要讲如何使着色器与Unity的Lightmappers兼容。 #### 8.9.1 The Meta Pass Meta Pass是一个着色器通道,提供反照率和发射值给Global Illumination系统。这些值与实时渲染中使用的值是分开的,这意味着我们可以使用Meta Pass来控制从光照烘焙系统的角度来看到的游戏对象的外观,而不会影响其运行时的外观(就是说只影响烘焙而不影响实时的。这里所谓烘焙也应该包括实时中的间接光,这点有点不确定,不然实时GI要这个数据也没用啊)。比如,我们想让悬崖上的绿色苔藓在你的光照贴图中产生夸张的绿色间接光,但我们不想在着色器的实时通道中重新给地形上色,就可以使用Meta Pass来控制。 所有Unity的内置材质都有一个Meta Pass,标准着色器包含一个Meta Pass。如果我们使用的就是内置材质、标准着色器,那么我们不需要做任何事情来启用Meta Pass。但如果我们使用自定义着色器,那么我们可以添加自己的Meta Pass。 #### 8.9.2 带有Meta Pass的着色器实例 下面的着色器允许我们指定一个反照率颜色和反照率纹理,只被光照烘焙系统使用,而不影响运行时材质的外观。在这个例子中,发射来自UVs,但任何值都可以用来控制它。 ```cs Shader "Custom/metaPassShader"{ Properties { _Color ("Color", Color)=(1,1,1,1) _MainTex ("Albedo (RGB)",2D)="white"{} _Glossiness ("Smoothness", Range(0,1))=0.5 _Metallic ("Metallic", Range(0,1))=0.0 _GIAlbedoColor ("Color Albedo (GI)", Color)=(1,1,1,1) _GIAlbedoTex ("Albedo (GI)",2D)="white"{} } SubShader { // ------------------------------------------------------------------ // Extracts information for lightmapping, GI (emission, albedo, ...) // This pass is not used during regular rendering. Pass { Name "META" Tags {"LightMode"="Meta"} Cull Off CGPROGRAM #include"UnityStandardMeta.cginc" sampler2D _GIAlbedoTex; fixed4 _GIAlbedoColor; float4 frag_meta2 (v2f_meta i): SV_Target { // We're interested in diffuse & specular colors // and surface roughness to produce final albedo. FragmentCommonData data = UNITY_SETUP_BRDF_INPUT (i.uv); UnityMetaInput o; UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o); fixed4 c = tex2D (_GIAlbedoTex, i.uv); o.Albedo = fixed3(c.rgb * _GIAlbedoColor.rgb); o.Emission = Emission(i.uv.xy); return UnityMetaFragment(o); } #pragma vertex vert_meta #pragma fragment frag_meta2 #pragma shader_feature _EMISSION #pragma shader_feature _METALLICGLOSSMAP #pragma shader_feature ___ _DETAIL_MULX2 ENDCG } Tags {"RenderType"="Opaque"} LOD 200 CGPROGRAM // Physically-based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows nometa // Use Shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; void surf (Input IN,inout SurfaceOutputStandard o){ // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex)* _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } ``` #### 8.9.3 Meta Pass 技术信息 Enlighten Realtime Global Illumination和光照映射(由lightmappers执行映射)使用Unity的Meta Pass从表面提取反照率值,并在每次反弹时使用表面反照率来处理漫反射传输。 具有黑色(或几乎黑色)反照率的金属表面反射很少的光漫反射。Lightmappers只处理漫反射光传输,这意味着我们可能会看到很少的反弹光从这些类型的表面。Unity内置的Meta Pass通过提供增强版的金属色调来实现这种现象(即让我们看不到反弹光),而不是采用物理上正确的反照率。这意味着即使是金属材料,其在Unity中实际也会有反弹。另外,我们可以创建一个自定义Meta Pass,来实现不同的行为。 内置的Meta Pass不处理光谱镜面反射。 注意:如果我使用的是Enlighten Realtime Global Illumination,那么Player中的Meta pass没有DynamicGI.SetEmissive那么快,但它更灵活,因为我们不局限于单一颜色。 #### 8.9.4 自定义RGB透明度 默认情况下,Unity中的着色器使用单色透明度。这意味着Unity使用材质颜色或反照率纹理的alpha通道来评估通过材质的光透过率。 在光照映射过程中,你可以使用自定义RGB透明度,这意味着Unity使用给定纹理的值来评估通过材料的光透过率。当我们想要独立于材料颜色或反照率纹理的基于颜色的透明度时,这是有用的。例如,如果我们想烘焙光照,模拟通过彩色玻璃窗照射的光的行为。 要在光映射期间使用自定义RGB透明度,请在ShaderLab代码中添加以下行: ```cs _TransparencyLM ("Transmissive Texture", 2D) = "white" {} ``` 这将在材质Inspector中创建一个材质属性,名称为"Transmissive Texture"。将所需的纹理分配给该字段。 ### 8.10 光照贴图UVs #### 8.10.1 介绍 Realtime Global Illumination、Baked Global Illumination系统都使用光照贴图,也因此需要光照贴图UVs。Unity在这两个系统中使用单独的光照贴图UVs集合。这有两个原因: 1. 在实时光照贴图和烘焙光照贴图的实例分组中没有直接的对应关系。在相同实时光照贴图中的实例,可能在不同的烘焙光照贴图中,反之亦然。 2. 在烘焙光照贴图中,以不同缩放(scales)出现的网格共享光照贴图UVs,但在实时光照贴图中不共享UVs。 ##### 8.10.1.1 Baked lightmap UVs Baked lightmap UVs(烘焙光照贴图UVs)是per-mesh的:同一网格的所有实例共享相同的Baked lightmap UVs。当我们导入模型时,Unity可以计算Baked lightmap UVs,或者我们可以提供自己的数据。 Unity在网格的[Mesh.uv2](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Mesh-uv2.html "Mesh.uv2")通道中存储baked lightmap UVs,这个通道映射到TEXCOORD1着色器语义,通常被称为"UV1"。 如果启用了Baked Global Illumination,并且给定的MeshRenderer从光照贴图中接收其全局照明,Unity将使用[Mesh.uv2](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Mesh-uv2.html "Mesh.uv2")通道中的数据来正确地把烘焙光照贴图映射到网格。 注意:如果我们想在给定的网格中使用Mesh.uv2用于其他目的,那么必须确保使用该网格的所有MeshRenderer组件都从Light Probes接收全局照明而不是从光照贴图中。可从MeshRenderer组件的Inspector中修改接收设置,也可以使用 [MeshRenderer.receiveGI](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MeshRenderer.receiveGI.html "MeshRenderer.receiveGI") API。 ##### 8.10.1.2 Real-time lightmap UVs Real-time lightmap UVs(实时光照贴图UVs)是per-mesh的:同一网格的所有实例共享相同的输入数据,但不同的Mesh Renderers实例可以在运行时使用不同的real-time lightmap UVs。Unity在预计算阶段计算Realtime Global Illumination系统的UVs。这个计算将per-mesh的UVs作为输入,并使用该数据来创建per-mesh的Renderer UVs。当我们导入模型时,Unity可以生成输入数据:per-mesh的UVs,或者我们可以提供自己的数据。 其工作原理如下: * Unity可以使用[Mesh.uv3](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Mesh-uv3.html "Mesh.uv3")通道中的数据作为real-time lightmap UV计算的输入。Mesh.uv3映射到TEXCOORD2着色器语义,通常被称为"UV2"。 * 如果Mesh.uv3中没有数据,但Mesh-uv2中有数据,Unity就会退回到使用Mesh.uv2中的数据作为real-time lightmap UV计算的输入。Mesh.uv2通常用于baked lightmap UVs。使用baked lightmap UVs作为real-time lightmap UVs的输入数据是很常见的。 * 计算结果存储在per-MeshRenderer的[MeshRenderer.enlightenVertexStream](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MeshRenderer-enlightenVertexStream.html "MeshRenderer.enlightenVertexStream")中。如果启用了Realtime Global Illumination,并且给定的MeshRenderer组件Contributes GI(静态设置)并从光照贴图中接收其全局光照,Unity会自动将MeshRenderer.enlightenVertexStream中的数据传递给着色器中的TEXCOORD2,而不是Mesh.uv3中的数据。【什么时候用Mesh.uv3的数据?有点迷惑。这里是说存储行为是一种可选情况吗,若存储了就不适用uv3了?】 注意:如果我们想在一个使用Realtime Global Illumination的网格中使用Mesh.uv3用于其他目的,我们必须确保所有使用该网格的MeshRenderer组件从Light Probes中而不是光照贴图中接收全局光照。可从MeshRenderer组件的Inspector中修改接收设置,也可以使用 [MeshRenderer.receiveGI](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MeshRenderer.receiveGI.html "MeshRenderer.receiveGI") API。(和Baked lightmap UVs那个一样) ##### 8.10.1.3 Unity如何计算real-time lightmap UVs Unity将per-mesh输入的UVs处理成per-Mesh Renderer输出的UVs时,发生了什么?如下: **Packing(打包)** Unity重新打包real-time lightmap UVs去确保每个chart的边界在所有方向上落到一个texel中心,然后在每个chart的边界周围添加half-texel的填充。这确保了所有chart之间有一个完整texel空间。 这是因为real-time lightmaps的分辨率故意设置得很低,以便实时更新它们。低分辨率不会影响图像质量,因为这些光照贴图只存储低频间接光照,但当chart共享texel时,它可能会导致"bleeding"(这种现象在前面有介绍)。重新打包确保chart永远不会共享texel。这种方式不仅避免了这个问题,而且还可以高效地将chart打包在一起。 ![](https://img-blog.csdnimg.cn/direct/a897af0dec014648877c325e69a27377.png) 如图,图中这些网格就是模型网格的展开图,可以理解为我们前面所说的chart(UV chart)。 注意:这种打包技术意味着计算的UVs依赖于实例的scale和光照贴图分辨率,这就是为什么real-time lightmap UVs是per-Mesh Renderer的。但是,Unity会在可能的情况下自动优化这一点:使用具有相同scale和光照贴图分辨率的相同网格的Mesh Renderers会共享相同的UVs。 **Merging(合并)** 或者,我也可以在这个过程中指示Unity合并UV chart。这会减小光照贴图的尺寸,改善运行时内存的使用和性能。 我们可以在任何带有Mesh Renderer组件的游戏对象上启用这个优化。设置位置如图: ![](https://img-blog.csdnimg.cn/direct/256df2833e5e4157bcaf6ce8e238cf6d.png) 注意:这个功能有时会在原始UV映射中,对不连续性产生错误。例如,一个故意锋利的边缘可能会被误解为一个连续的表面。如果发生这种情况,请禁用此功能。 #### 8.10.2 生成光照贴图 UV 当我们导入模型时,Unity可以计算baked lightmaps UVs,或者我们可以提供自己的数据。 ##### 8.10.2.1 如何提供自己的光照贴图UVs 我们可以在创建模型的软件中,编写自己的lightmap UVs ,Unity使用这些UVs作为计算的输入。 我们把这些数据放在哪里,取决于我们是为baked lightmaps,real-time lightmaps,或是两者提供UVs: * 为baked lightmaps提供,我们必须将lightmap UVs放到Mesh.uv2中,这个通道也被称为"UV1"。 * 为real-time lightmaps提供: * 如果我们已经在网格的Mesh.uv2中存放了baked lightmap UVs,并且想使用相同的UVs作为real-time lightmaps的输入,那么我们不需要做任何事,Unity会退回并共享baked lightmap UVs。 * 如果我们已经在网格的Mesh.uv2中存放了baked lightmap UVs,并且我们想为real-time lightmaps提供不同的UVs作为输入,那么就将real-time lightmap UVs放到Mesh.uv3中,这个通道也被称为"UV2"。 * 如果我们还未在Mesh.uv2中存放baked lightmap UVs,那么我们就要决定为real-time lightmap UVs使用Mesh.uv2还是Mesh.uv3。(就是这两个通道都可以存放的意思) 光照贴图的合理 UV 集应遵循以下规则: * 应在 \[0,1\] x \[0,1\] UV 空间内。 * 在各个图表之间应有足够宽的边距。有关更多信息,请参阅 [UV 重叠结果](https://docs.unity3d.com/cn/2023.2/Manual/ProgressiveLightmapper-UVOverlap.html "UV 重叠结果")。 * 不得有任何重叠面。 * UV 中的角度与原始几何体中的角度之间差异不大。 * 除非我们希望某些区域具有更高的光照贴图分辨率,否则 UV 中三角形的相对比例与原始几何体中三角形的相对比例之间应该存在较小差异。 ##### 8.10.2.2 如何自动生成光照贴图UVs 可以使用 [Model Import Settings](https://docs.unity3d.com/cn/2023.2/Manual/class-FBXImporter.html "Model Import Settings") 告诉 Unity 为模型自动生成光照贴图 UV。 1. 在 Project 视图中选择模型。Unity 将在 Inspector 中打开 Model Import Settings。 2. 在 Model Import Settings 中,导航到 Model 选项卡,然后导航到 Geometry 部分。 3. 勾选 Generate Lightmap UVs 复选框。Lightmap UVs settings 部分显示在 Generate Lightmap UVs 复选框的下方。 4. 可选:配置 Lightmap UVs settings 部分中的设置。 5. 单击 Apply 按钮。Unity 会将光照贴图 UV 生成到 [Mesh.uv2](https://docs.unity3d.com/cn/2023.2/ScriptReference/Mesh-uv2.html "Mesh.uv2") 通道中。 Lightmap UVs settings中的属性: * Hard Angle:相邻三角形之间的角度(以度为单位),Unity 根据此角度将其视为硬边缘并创建接缝。您可以将角度设置为 0 到 180 之间的值。默认情况下设置为 88 度。如果将此角度设置为 180 度,Unity 会认为所有边缘都是平滑的,这适用于生物模型。默认值(88 度)适用于机械模型。 * Angle Error:UV 角度与源几何体中角度的最大可能偏差(百分比形式,范围从 0 到 100)。默认情况下,此值设置为 8%。 此属性控制 UV 空间中三角形与原始几何体中三角形的差异程度。通常,为避免在应用光照贴图时出现瑕疵,此数值应该相当低。 * Area Error:UV 面积与源几何体中面积的最大可能偏差(百分比形式,范围从 0 到 100)。默认情况下,此值设置为 15%。此属性控制 Unity 保留相对三角形面积的程度。增大此值可以创建更少的图表。但是,增大该值可能会改变三角形的分辨率,因此请确保产生的失真不会降低光照贴图的质量。 * Margin Method:边缘方法。分为两种:Manual、Calculate。 * Manual:手动指定Pack Margin。 * Calculate:基于预期的光照贴图分辨率和对象比例,Unity 计算一个足够大的Pack Margin以避免 UV 重叠。 * Pack Margin:在假设网格占据整个 1024x1024 光照贴图的前提下,相邻图表之间的边距(以像素为单位)。您可以将此值设置为 1 到 64 之间的值。较大的值会增加边距,但也会增加图表所需的空间量。默认设置为 4 像素。此属性仅在将 Margin Method 设置为 Manual 时可见。 * Min Lightmap Resolution:所有场景中使用此网格的 MeshRenderers 的最小光照贴图分辨率(纹理像素/单位)。MeshRenderer 的光照贴图分辨率是以下两个属性的组合:MeshRenderer 的 Scale in Lightmap 属性和其所在场景的[光照设置资源](https://docs.unity3d.com/cn/2023.2/Manual/class-LightingSettings.html "光照设置资源")的 Lightmap Resolution 属性。Unity 使用此信息来计算 Pack Margin。此属性仅在将 Margin Method 设置为 Calculate 时可见。 * Min Object Scale:在所有场景中使用此网格的游戏对象的最小变换比例(transform scale)。Unity 使用此信息来计算 Pack Margin。此属性仅在将 Margin Method 设置为 Calculate 时可见。 ##### 8.10.2.3 自动生成光照贴图UVs问题解决 **Pack Margin** 为允许过滤,光照贴图包含图表边缘附近的纹理像素中的光照信息,因此应在图表之间始终包含一定的边距,以免在应用光照贴图时出现光渗(bleeding)。 光照贴图分辨率定义了光照贴图的纹理像素(texel)分辨率。Lightmapper会扩大光照贴图中的一些图表纹理像素以避免黑色边缘,因此网格的 UV 图表必须至少相距两个完整的纹理像素,从而避免光渗。使用 Pack Margin 设置可确保几何体的 UV 图表之间有足够的边距。 ![](https://img-blog.csdnimg.cn/direct/97bbead219fb4ce4a97170220be0b2c4.png) 在光照贴图 UV 空间中,图表之间的填充需要至少两个完整纹理像素,以免出现 UV 重叠和意外光渗。在此图中,黑色空白表示图表之间的空隙。 **Min Lightmap Resolution and Min Object Scale** 将 UV 图表放置得太近可能会导致在最终的光照贴图中出现[图表纹素交叉渗漏](https://docs.unity3d.com/cn/2023.2/Manual/ProgressiveLightmapper-UVOverlap.html "图表纹素交叉渗漏")。将图表放置得太远会浪费内存。对象的理想打包间距取决于分配给它的光照贴图纹素的数量。 Unity 用于 MeshRenderer 的纹素数量取决于 MeshRenderer 的光照贴图分辨率和变换比例(transform scale)。为了计算合适的边距,Unity 需要知道这些属性的期望最小值。 MeshRenderer 的光照贴图分辨率是以下属性的组合:其所在场景的[光照设置资源](https://docs.unity3d.com/cn/2023.2/Manual/class-LightingSettings.html "光照设置资源")的 Lightmap Resolution 属性和 [MeshRenderer](https://docs.unity3d.com/cn/2023.2/Manual/class-MeshRenderer.html "MeshRenderer") 的 Scale in Lightmap 属性。请注意,这意味着同一个 MeshRenderer 在不同的场景中可以具有不同的光照贴图分辨率。 我们可以使用 Inspector 查看给定场景中 MeshRenderer 的光照贴图分辨率,选中带有MeshRenderer组件的对象: ![](https://img-blog.csdnimg.cn/direct/abe545e5d5ff433f96d6857e9534f745.png) 如图,分别显示了光照贴图分辨率、变换比例。需要注意,Baked Lightmap和Realtime LightMap需要在Lighting面板中执行GI烘焙后才会出现。 **Angle distortion** 以下截屏具有相同的分辨率,但具有不同的 UV。第一张图的 Angle Error 较高,结果中包含意料之外的失真。第二张图使用 Angle Error 默认值 (8%)。在具有较多三角形的网格中,角度失真会让形状显著失真。 ![](https://img-blog.csdnimg.cn/direct/1327d82faf3945319a7595d23968de4a.png) **Area distortion** 在下面的图片中,两个具有相同参数的聚光灯照亮圆柱体的侧面。圆柱体的右侧具有较高的Area Error值,这会扭曲三角形并导致较低的分辨率,从而在光线中产生伪影。 ![](https://img-blog.csdnimg.cn/direct/3dbc8468a552492f8425399045df6aff.png) #### 8.10.3 可视化光照贴图UVs 能够查看正在使用的光照贴图UVs是很重要的,Unity有一个可视化工具来帮助我们做到这一点。首先打开Lighting面板,然后执行GI烘焙操作,官方文档里建议值勾选自动烘焙选项,确保烘焙、预计算是最新的。 按照官方文档完全找不到可视化方法:[官方文档](https://docs.unity3d.com/6000.0/Documentation/Manual/LightingGiUvs-visualizing.html "官方文档")。不过这种方法可以简单看一看: ![](https://img-blog.csdnimg.cn/direct/818eb134b76444439ae6181d11d7c319.png) #### 8.10.4 解决光照贴图UV重叠 每个光照贴图包含许多的charts。在运行时,Unity将这些charts映射到网格面上,并使用charts的照明数据来计算最终外观。由于GPU采样的工作方式,如果一个chart太靠近另一个chart,它的数据可能会流到另一个chart的数据中。这通常会导致意想不到的问题,如混叠、像素化等。所造成的光渗问题如图: ![](https://img-blog.csdnimg.cn/direct/df42ab23a7184c328737b6c8fb8a694e.png) 为避免光渗,chart之间必须有足够的空间。当 GPU 对光照贴图进行采样时,光照系统会从最接近采样点的四个纹理像素计算最终采样值(假设使用双线性过滤)。这四个纹理像素称为采样点的双线性"邻域"。如果chart重叠(也就是说,如果chart中任何点的邻域与另一个图表中任何点的邻域重叠),表示图表太靠近。在下图中,白色像素表示chart邻域,红色像素表示重叠的邻域。 ![](https://img-blog.csdnimg.cn/direct/2e797de938a742aab44cc663a8df1d1e.png) 确定最佳图表放置位置和间距可能会很困难,因为它取决于多个参数(例如光照贴图分辨率、网格 UV 和导入器设置)。基于此原因,Unity 提供了轻松识别这些问题的能力,如以下部分所述。 ##### 8.10.4.1 识别 有三种方法可识别重叠: * 密切关注 Unity 的控制台。如果 Unity 检测到重叠的 UV,它会打印一条警告消息,其中包含受影响游戏对象的列表。 * 使用 Scene 视图中的 **UV Overlap** 绘制模式(有关更多信息,请参阅 [Scene 视图中的 GI 可视化](https://docs.unity3d.com/cn/2023.2/Manual/GIVis.html "Scene 视图中的 GI 可视化"))。启用此模式后,Unity 会以红色突出显示chart中的哪些纹理像素过于靠近其他chart中的纹理像素。如果您在 Scene 视图中发现瑕疵,并希望快速检查是否由 UV 重叠导致了此问题,则此方法尤其有用。如图: ![](https://img-blog.csdnimg.cn/direct/5299d8eabb4f458e8c6aee620ad7b320.png) 从此下拉框中可以选择UV Overlap模式。此模式下若出现UV重叠,则视觉效果如图所示: ![](https://img-blog.csdnimg.cn/direct/c6184df26e394e678cf3e6b35d6a96af.png) * 使用 **Baked Lightmaps Preview** 。选择一个游戏对象,前往 Lighting 窗口,然后选择 **Baked Lightmaps** 选项卡。双击突出显示的光照贴图,导航到 Preview 窗口,然后选择 **Baked UV Overlap**(请查看右上角的下拉选单)。Preview 窗口在此视图中将有问题的纹素标记为红色。如图: ![](https://img-blog.csdnimg.cn/direct/cfef9279fcf74e7fbf8586d889c55265.png) 若有重叠,则如图: ![](https://img-blog.csdnimg.cn/direct/aaa509ea75274c45b57545e411fa7e76.png) ##### 8.10.4.2 解决方案 UV 重叠没有唯一的解决方案,因为有很多问题可能导致这种情况。以下是最常见的解决方案: * 如果由我们自己提供光照贴图 UV,可使用我们自己的modelling package添加边距。 * 如果 Unity 自动为模型生成光照贴图 UV,我们可以让 Unity 增加包边距。最简单的方法是将 Margin Method 设置为 Calculate,并设置适当 Min Lightmap Resolution 和 Min Object Scale。如果愿意将 Margin Method 设置为 Manual,那么可以直接调整 Pack Margin 值。如需了解这些设置的更多信息,请参阅有关[生成光照贴图 UV](https://docs.unity3d.com/cn/2023.2/Manual/LightingGiUvs-GeneratingLightmappingUVs.html "生成光照贴图 UV") 的文档。 * 提高整个光照贴图的分辨率。这样将增加图表之间的像素数量,从而降低发生光渗的可能性。缺点是光照贴图可能会变得太大。我们可以在 Lightmapper Settings 下的 Lighting 选项卡中执行此操作。 * 提高单个游戏对象的分辨率。这种情况下可以仅为具有重叠 UV 的游戏对象提高光照贴图分辨率。这也可能增加光照贴图的大小,但可能性较小。我们可以在它的网格渲染器(Mesh Renderer)的lightmap Settings下改变GameObject的lightmap分辨率。(PS:这里说的应该是使用Scale in Lightmap属性来改变。) ![](https://img-blog.csdnimg.cn/direct/9afff17e5cbf47058a6064832b77eadc.png) 如图,正常,没有发生光渗的情况。 ### 8.11 光照贴图接缝缝合 接缝缝合功能可在使用烘焙光照贴图(由渐进光照贴图 (Progressive Lightmapper) 生成)渲染的游戏对象中,平滑不需要的硬边缘。如图,是没有使用接缝缝合功能的情况: ![](https://img-blog.csdnimg.cn/direct/1c0ee7a754dc4f41baf9949ebfb8dd71.png) 使用后: ![](https://img-blog.csdnimg.cn/direct/afaec55e592f4e73920a5025f75caaa7.png) 当Unity烘焙光照贴图时,它会将靠近但彼此分离的网格面识别为在光图空间中是分离的,这些网格的边缘被称为"接缝"。理想情况下,接缝是看不见的,然而,它们有时会显示为坚硬的边缘。这是因为GPU不能在光照贴图中分开的chart之间混合其纹理像素值。 接缝缝合修复了这个问题。当我们启用接缝拼接时,Unity会进行额外的计算来修改光照贴图以改善每个接缝的外观。拼接并不完美,但它往往大大改善了最终的结果。由于Unity做了额外的计算,接缝拼接在烘焙过程中需要额外的时间,所以Unity默认禁用它。 当我们启用缝线时,lightmapper会识别出应该缝合在一起的边缘对,并在缝线上产生尽可能平滑的照明。这只适用于在图集中沿chart边界水平或垂直走向的直边,其并被设计用于与在UV空间中与轴对齐的矩形一起工作。(PS:这话怎么这么抽象。【】) #### 8.11.1 接缝缝合的局限性 接缝缝合与[渐进光照贴图 (Progressive Lightmapper)](https://docs.unity3d.com/cn/2023.2/Manual/progressive-lightmapper.html "渐进光照贴图 (Progressive Lightmapper)") 配合使用。接缝缝合仅对单个游戏对象有效;多个游戏对象无法平滑地缝合在一起。 #### 8.11.2 使用接缝缝合 我们可以使用Mesh Renderer组件在任何GameObject上启用接缝拼接。如图: ![](https://img-blog.csdnimg.cn/direct/488a1471fa084c669405aff34ee0706a.png) 另外,我们也可以使用 [MeshRenderer.stitchLightmapSeams](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/MeshRenderer-stitchLightmapSeams.html "MeshRenderer.stitchLightmapSeams") API。 ### 8.12 自定义衰减 在现实世界中,光线会在远处消失,而且昏暗的光线比明亮的光线具有更低的有效范围。"衰减"一词是指光衰速率。除了 Unity 的默认衰减光照行为外,还可以使用自定义衰减设置。 渐进式光照贴图 (Progressive Lightmapper) 提供自定义衰减预设,我们可以通过脚本来实现这些预设。请参阅表下方的图像以查看这些预设的工作原理的可视化表示,并参阅图像下方的代码示例来了解此功能的用法。 * InverseSquared:应用距离平方倒数法衰减模型。意味着,光照强度与位置到光源的距离的平方成反比减小。有关更多信息,请参阅 Wikipedia:平方反比定律 (Inverse-square law)。此选项在物理上是最准确的。 * InverseSquaredNoRangeAttenuation:应用没有平滑范围衰减的距离平方倒数法衰减模型。这与InverseSquared的工作方式相同,但照明系统不考虑点灯、聚光灯的范围参数的衰减。 * Legacy:应用二次(quadratic)衰减模型。该模型将光衰减基于光源的范围。随着光线远离光源,强度会减弱,但衰减会有非常明显且不自然的下降,视觉效果也不太现实。 * Linear:应用线性衰减模型。在此模型中,衰减与距光源的距离成反比,且衰减以固定的速率从光源开始减弱。 ![](https://img-blog.csdnimg.cn/direct/814f976eef0f411694c79cc94ca9982d.png) 注意:在使用baked或mixed光时,下面的代码示例仅适用于Progressive Lightmapper。要对实时灯光使用如下代码示例,请使用[Enlighten Realtime Global Illumination](https://docs.unity3d.com/6000.0/Documentation/Manual/realtime-gi-using-enlighten.html "Enlighten Realtime Global Illumination")。 ```cs using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Experimental.GlobalIllumination; using UnityEngine.SceneManagement; [ExecuteInEditMode] public class ExtractFalloff : MonoBehaviour { public void OnEnable() { Lightmapping.RequestLightsDelegate testDel = (Light[] requests, Unity.Collections.NativeArray lightsOutput) => { DirectionalLight dLight = new DirectionalLight(); PointLight point = new PointLight(); SpotLight spot = new SpotLight(); RectangleLight rect = new RectangleLight(); DiscLight disc = new DiscLight(); Cookie cookie = new Cookie(); LightDataGI ld = new LightDataGI(); for (int i = 0; i < requests.Length; i++) { Light l = requests[i]; switch (l.type) { case UnityEngine.LightType.Directional: LightmapperUtils.Extract(l, ref dLight); LightmapperUtils.Extract(l, out cookie); ld.Init(ref dLight, ref cookie); break; case UnityEngine.LightType.Point: LightmapperUtils.Extract(l, ref point); LightmapperUtils.Extract(l, out cookie); ld.Init(ref point, ref cookie); break; case UnityEngine.LightType.Spot: LightmapperUtils.Extract(l, ref spot); LightmapperUtils.Extract(l, out cookie); ld.Init(ref spot, ref cookie); break; case UnityEngine.LightType.Rectangle: LightmapperUtils.Extract(l, ref rect); LightmapperUtils.Extract(l, out cookie); ld.Init(ref rect, ref cookie); break; case UnityEngine.LightType.Disc: LightmapperUtils.Extract(l, ref disc); LightmapperUtils.Extract(l, out cookie); ld.Init(ref disc, ref cookie); break; default: ld.InitNoBake(l.GetInstanceID()); break; } ld.cookieID = l.cookie?.GetInstanceID() ?? 0; ld.falloff = FalloffType.InverseSquared; lightsOutput[i] = ld; } }; Lightmapping.SetDelegate(testDel); } void OnDisable() { Lightmapping.ResetDelegate(); } } ``` 注意:上面示例中的所有代码都是自定义衰减工作所必需的,但是,我们可以在 ld.falloff = FalloffType.InverseSquared 这行更改InverseSquared,替换为上述的任何一个预设衰减。 ## 9 Enlighten实时全局光照 Unity使用一个中间件解决方案,称为Enlighten for Realtime Global Illumination。 默认情况下,实时灯光只向场景提供直接光照。如果你在场景中启用了实时全局光照(Enlighten Realtime Global Illumination),实时光照也会为场景提供间接光照。 ### 9.1 渲染管线支持 有关在渲染管线中关于支持Enlighten实时全局光照的更多信息,请参阅[render pipeline feature comparison](https://docs.unity3d.com/6000.0/Documentation/Manual/render-pipelines-feature-comparison.html "render pipeline feature comparison")。 ### 9.2 何时使用 Enlighten Realtime Global Illumination(Realtime GI、实时GI)对于变化缓慢且对场景有显著视觉影响的灯光非常有用,例如太阳在天空中移动,或者在封闭的走廊中缓慢脉动的光线。这个特性并不适用于特殊效果或快速变化的灯光,因为延迟和所需的CPU周期数量使这种应用不切实际。Enlightenment Realtime Global Illumination适用于面向中高端PC系统和主机的游戏。一些高端移动设备可能也足够强大,可以使用此功能,但我们应该保持scene较小,以及实时光照贴图的分辨率较低,以确保可接受的性能消耗。 ### 9.3 使用 Enlighten Realtime Global Illumination的使用,实际就是前面全局光照中的实时GI的使用,这里就不多说了,在Lighting窗口中使用。 开启实时GI后,光源就会为场景提供间接光了,但如果我们想让某一光源不向场景提供间接光,那么我可以在其灯光组件中进行设置,只需要将灯光组件上的 Indirect Multiplier 设置为0,改光源便不会为场景提供间接光。 有关使用Enlighten Realtime Global Illumination的详细步骤建议,请参阅Unity教程中的[Precomputed Realtime GI](https://learn.unity.com/project/lighting-optimization-with-precomputed-realtime-gi "Precomputed Realtime GI")。(PS:官方视频。) ### 9.4 如何工作的 Enlighten Realtime Global Illumination所做事情: 1. 将场景分割成小的surface patches (clusters)。(PS:可以理解为三维模型表面分割为了多块,不知道怎么翻译好,直接用原单词。) 2. 决定patche对于其他patche的可见度。 3. 将彼此可见的patche分组到一个系统中。 在运行时,Enlighten Realtime Global Illumination使用这个预先计算的可见性信息来近似实时灯光在场景中的反弹方式,并将结果保存在一组光照贴图中,然后使用这些光照贴图将间接光照应用到场景中。更新光图的计算量很大,所以这个过程被分成几个帧。它需要Enlighten Realtime Global Illumination几帧来传播整个场景的间接光照的变化。 ### 9.5 光照探针与实时GI * 注意,当我们启用实时GI时,光照探针的行为会有所不同。 * 为了对场景光照的实时变化做出反应,它们在运行时迭代地采样光照。 * 当我们在场景中禁用了实时GI,光照探针只使用baked光照数据,这意味着它们无法对场景光照的实时变化做出反应。 ### 9.6 阴影与实时GI 如果灯光也投射阴影,Unity会将场景中的动态和静态游戏对象渲染到灯光的阴影贴图中。静态和动态GameObjects的材质着色器对这个阴影贴图进行采样,这样这些GameObjects就可以实时地将阴影投射到彼此身上。阴影距离设置项决定阴影开始逐渐消失和完全消失的最大距离,它反过来会影响性能和图像质量。 Enlighten Realtime Global Illumination的阴影通常有更粗粒度,相比光映射所能实现的。 ### 9.7 性能注意事项 Enlighten Realtime Global Illumination使用一组光照贴图来存储实时的间接反弹。所以启用它会增加内存需求,即使我们将它与Baked Global Illumination一起使用。 当我们使用Enlighten Realtime Global Illumination时,着色器计算数量也会增加,这些计算用于生成光照,因为它会采样额外的一组光照贴图和光照探针。 ### 9.8 优化实时GI 如果Enlighten Realtime Global Illumination对场景光照的变化反应不够快,有几种方法可以解决这个问题: * 降低实时光照贴图的分辨率以加快运行时的计算速度。 * 在质量设置窗口中,增加实时GI的CPU使用率。这样做的代价是,其他系统接收到的CPU时间会更少,这是否可以接受取决于我们的项目。这是一个逐场景设置,所以我们可以根据项目中每个单独场景的复杂性投入更多或更少的CPU时间。(【暂时未找到在哪里设置】) ### 9.9 LOD和实时GI 接收全局光照并设置光照贴图的对象有一个光照解决方案,此解决方案伴随有烘焙直接和间接光的光照贴图,以及实时GI的光照贴图。有关接收全局照明的更多信息,请参阅 [Mesh Renderer settings](https://docs.unity3d.com/cn/2023.2/Manual/class-MeshRenderer.html "Mesh Renderer settings"),脚本参考[ReceiveGI](https://docs.unity3d.com/cn/2023.2/ScriptReference/ReceiveGI.html "ReceiveGI")。 当我们在一个场景中使用Unity的LOD系统,并且已经启用了Baked Global Illumination和Enlighten Realtime Global Illumination,系统会从LOD组中照亮最详细的模型,就好像它已经启用了Contribute GI设置,而不是LOD组的一部分。 对于较低的LOD, Enlighten只能计算直接光照,LOD系统必须依靠光照探针来采样间接光照。(烘焙GI的间接光,LOD是可以正常使用的。但实时GI的间接光则是需要用光照探针来使用。) 为了使编辑器能够产生实时GI的光照贴图,选择你想要影响的游戏对象,在检查器窗口中查看它的Renderer组件,并确保"Contribute Global Illumination"是启用的。 对于LOD组中的较低LOD,我们只能将baked光照贴图与来自光照探针或光照探针Volumes的Enlighten Realtime Global Illumination 结合起来,这些光照探针、光照探针Volumes必须分布在LOD Group的周围。 ![](https://img-blog.csdnimg.cn/direct/4ed61711fc0b4d84b40d65b41636f755.gif) 如图,一个动画,显示实时环境颜色如何影响被lower LODs所使用的Enlighten Realtime Global Illumination。在远离到一定程度时,可以看到墙面上的光照细节消失了。 ## 10 光照探针 非静态的物体不会受到GI的影响,而光照探针可以帮我们解决这个问题。 光照探针可以捕获、使用场景中穿过"空白"空间的光的信息(空白空间就是没有物体的空间)。与光照贴图类似,光照探针也会存储烘焙(Generate Lighting)的光照信息。不同的是,光照贴图存储的是光线照射到场景中表面的信息,而光照探针存储的是光线穿过空白空间的信息。光照探针捕获信息的位置即自己所在的位置,也是空白空间的位置。在程序运行时,照到动态对象的间接光是使用距离此对象最近的光照探针的值来近似计算的(插值计算光线;此时动态物体也参与到了GI中)。光照探针的两个作用: 1. 光探头的主要用途是为场景中移动的物体提供高质量的照明(包括间接反射光)。 2. 光探头的第二个用途是为静态场景提供照明信息,当该场景使用Unity的LOD系统时。 实时GI、烘焙GI的mixed模式、烘焙GI的baked模式均可与光照探针组合使用。实时GI、烘焙GI的mixed模式,只有间接光会与光照探针配合,而baked模式直接光、间接光都会与光照探针配合。 ### 10.1 光照探针技术信息 参考[这里](https://docs.unity3d.com/6000.0/Documentation/Manual/LightProbes-TechnicalInformation.html "这里")。 ### 10.2 Light Probe Group组件 光照探针组。 #### 10.2.1 创建 ![](https://img-blog.csdnimg.cn/direct/79c36d028ede4038ac063f59e7344759.png) ![](https://img-blog.csdnimg.cn/direct/3cca5f50f3b344b3a89229bdbf9ad032.png) 如图,创建了一个光照探针组。 #### 10.2.2 组件属性 ![](https://img-blog.csdnimg.cn/direct/fc5e51dfd5004a368fd14dccf3abb070.png) * Show Wireframe:显示线框。 * Remove Ringing:移除Ringing现象(后面有介绍)。 * Edit Light Probe Positions:编辑关照探针组的位置。此属性选中后,可以选中光照探针组中的单个光照探针进行位置调整。同时下面的四个属性变为可用状态。 * Add Probe:增加探针。 * Select All:选中所有。 * Delete Selected:删除所选探针。 * Duplicate Selected:复制所选探针。 * Selected Probe Position:所选探针的位置坐标。选中探针时变为可用状态。 #### 10.2.3 案例 ![](https://img-blog.csdnimg.cn/direct/4e93e87523724d8db81ac48f7b069d66.png) 如图所示,在两个Panel中间有一个Cube,同时创建了一个光照探针组包裹住Cube。两个Panel为静态(Contribute GI),Cube为非静态。场景中有一个方向光,Mode设置为Mixed,我们进行烘焙GI。 ![](https://img-blog.csdnimg.cn/direct/1e94d4790fd64e38b12aa490b5ae903d.png) 烘焙完成后,左右移动Cube效果如下: ![](https://img-blog.csdnimg.cn/direct/c5c38a12a3d441f99f85f805b4eee3cf.gif) 地面是静态的,所以是参与到烘焙GI中的,由于光照探针的作用,我们的Cube也可以有间接光的效果,所以Cube在红色地面上时,被地面的反射光染成了红色,在蓝色地面上时则被染成了蓝色。 #### 10.2.4 Ringing现象 有时,光照探针的使用可能会出一种我们不想看到的现象,官方文档将其命名为"Ringing"。如图: ![](https://img-blog.csdnimg.cn/direct/6f1c5d39660a4ffc8d43b4ad2fbfc501.png) 球体的背面出现了不该出现的亮光。这种情况通常发生在光照探针周围的光有显著差异的情况下,比如光照探针一侧有明亮的光线,而另一侧没有光线,此时暗面的光照强度就可能会"overshoot"情况,进而导致暗面产生光点。有几种方法来解决此问题: 1. 在Light Probe Group组件面板中,勾选"Remove Ringing"选项,Unity便会自动移除这些光点。但是,通常情况下这会导致光照探针功能不太准确,并会降低光线的对比度。 2. 在游戏中,不让玩家看到光点,哪些位置能看到光点,就不让玩家去哪些位置(笑)。 3. 避免烘焙直接光到光照探针上。直接光往往有明显的间断点(如阴影边缘),这使得它不适合光照探针。请使用Mixed模式光照,来烘焙间接光。 #### 10.2.5 放置位置 光照贴图在物体表面有连续的分辨率,与之不同,光照探针信息的分辨率取决于光照探针位置间的紧密度。为了防止光照探针存储较多数据,进行较多计算,我们应该尽可能减少光照探针的数量。但我们也应该根据实际情况来决定,毕竟光照探针越多所带来的的效果越好,比如从一个空间到另一个空间的光照变化效果。我们可以在具有复杂、高度对比的区域放置较为密集的光照探针,而在光线变化不大的区域放置分散的光照探针。 ![](https://img-blog.csdnimg.cn/direct/fa2869207c1246f8808ab5122d57ce4a.png) 如图所示,在房子周围有着较为密集的光照探针,因为这个区域会有更多的明暗对比与颜色变化。而在公路范围,光照探针分布较为分散,因为这里的光线变化不明显。 定位光探针最简单的方法是将它们排列成一个规则的3D网格模样。虽然这种方式简单有效,但它可能会消耗比所需更多的内存,并且可能会有许多冗余的光照探针。例如,在上面的图像中,如果沿着道路放置了许多光照探针,这将会浪费许多资源。光线沿着道路长度方向变化不大,所以许多光照探针将存储与相邻光照探针几乎相同的光照数据。在这种情况下,在更少、更分散的光照探针才更合适,在它们之间进行的光照数据插值操作才更有效。 单个光照探针不会存储大量的数据,从技术角度来看,每个探头都是采样点视图的球形全景HDR图像,使用spherical Harmonics L2编码,存储为27个浮点值。但是,在拥有数百个光照探针的大型场景中,它们可能会累积起来,而且不必要地密集放置光探针可能会导致游戏中大量内存的浪费。 #### 10.2.6 体积问题 即使我们的游戏玩法是在2D平面上进行,光照探针也必须分布为3D形式。这意味着光照探针组中应该有两个垂直的点"层"(可以理解为两个垂直面,反正能形成立体形状就行)。 如下图所示,左侧的光照探针只是分布在地面表面,这样的话就不会产生良好的光照效果,因为光照探针系统不能计算出合理的四面体体积。而右侧则是立体化的分布,这样就形成了一个由许多四面体组成的3D体积,具有较好的光照效果。 ![](https://img-blog.csdnimg.cn/direct/6b6de784094342549d340c0df1b7414a.png) #### 10.2.7 移动光源与光照探针 在有移动光源的情况下使用光照探针,想要取得较好的光照效果,需要的光照探针数量可能更多。 #### 10.2.8 放置位置错误情况 我们知道光线的计算是通过在光照探针之间进行插值计算的,如果光照探针没有很好的覆盖到光照的变化,就会出现问题。如图: ![](https://img-blog.csdnimg.cn/direct/36691809d519425eb6c68e9aa1763bfa.png) 在这个夜间场景中,两侧各有一个路灯,中间则是一块黑暗区域。而这里我们光照探针的分布就不太合适,光照探针只放置在了路灯附近,而黑暗区域内并没有光照探针,这就导致中间黑暗区域不会包含在插值计算中,在插值计算光线时,中间位置的光线强度会偏大。效果如图: ![](https://img-blog.csdnimg.cn/direct/459b7d2079ab4a579834dcc933b9e208.png) 如图,小车明明在暗处,结果身上却有着亮光,这就是问题体现。若是使用的烘焙GI baked模式,直接光、间接光全与光照探针配合,这种问题体现得会更明显。黄色线框的四面体表示了插值是以两个路灯附近进行的,具体是以这个四面体的四个点来插值。PS:每次的插值操作都是以四面体的四点进行,当物体移动时,将会更换合适的其他四面体。 若想解决这个问题,则需要重新调整光照探针的分布。如图: ![](https://img-blog.csdnimg.cn/direct/bb497e471f2148ec8a00c3abef9b9f4f.png) 在中间黑暗区域也放置一些光照探针,使插值操作更合理。这种分布情况下的光照效果如下: ![](https://img-blog.csdnimg.cn/direct/35ebaeea502040cbaa18527b29a86f1a.png) 中间的小车变暗了,符合我们的期望。 ### 10.3 脚本创建光照探针思路 在大型场景中放置光照探针是比较费时费力的,可以考虑使用脚本来创建光照探针,并放置在合适的位置。比如创建一个空对象,挂载一个光照探针组件,然后再去设置位置。[参考](https://docs.unity3d.com/6000.0/Documentation/Manual/LightProbes-Placing-Scripting.html "参考")。 ### 10.4 MeshRenderer Mesh Renderer组件上也有一些与光照探针相关的属性。 * Light Probes:默认设置为Blend Probes。其还可以设置为Off、Use Proxy、Volume、Custom Provided。 * Off:不使用光照探针。 * Blend Probes:游戏对象使用光照探针,并且若游戏对象改变的位置,则其将会使用最近的光照探针。 * Use Proxy Volume:使用"Light Probe Proxy Volume(LPPV)"组件,需要在与MeshRenderer所挂载的同物体上挂载一个LPPV组件。选择此属性时会出现一个"Proxy Volume Override"属性,可以将挂载LPPV组件的其他物体拖拽到此属性上,进而使用此物体上的LPPV组件。(更多内容见4.5 Light Probe Proxy Volume组件) * Custom Provided:自定义提供。【以后补充】 * Anchor Override:锚点覆盖。一个物体在使用光照探针时,会根据物体的锚点(拖拽它时,三色坐标轴所在位置)位置来计算应用哪些光照探针,我们可以此属性将锚点进行替换,影响光照探针的选择,不过不影响拖拽操作。演示如图: ![](https://img-blog.csdnimg.cn/direct/7f7d342cf8e048fda66212e475d413d2.gif) ### 10.5 场景加载 Unity会根据场景加载、卸载方式决定光照探针数据的更新。 Unity本身使用一个LightProbes C#对象来存储所有当前加载场景的光照探针(LightProbe)数据。LightProbes对象包含一个称为"tetrahedral tessellation"的内部数据结构。Unity在计算中使用tetrahedral tessellation来确定光照探针如何照亮游戏对象。当加载或卸载一个场景时,Unity会自动更新LightProbes对象,以包含所有当前加载场景中所有光照探针的位置和系数。然而,Unity是否更新tetrahedral tessellation取决于如何加载或卸载场景。 当使用Single模式加载一个新场景时,Unity将会自动更新tetrahedral tessellation,因为新的光照探针完全取代了旧的光照探针。 当使用Additive模式加载一个新场景,或是使用UnloadSceneAsync异步卸载场景时(这里应该是指在已有场景中加载或卸载额外场景得到情况),Unity不会自动更新tetrahedral tessellation,因为新加入的光照探针、删除的光照探针它们的数据都需要重新计算,这是非常耗时的,而且即使计算后,后面可能还会再加载或是卸载其它场景,还要重新计算,所以干脆不自动更新。 因此,Unity提供了needsRetetrahedralization事件,允许我们决定何时更新数据。比如我们要加载5个场景,我们不希望每加载一个场景就去更新tetrahedral tessellation,这时我们就可以在5个场景全都加载完毕后,再去计算。 如果Unity使用过时的tetrahedral tessellation执行计算,则结果不会考虑任何新加载或卸载的光照探针,光照效果可能不会符合预期效果。此时若调用LightProbes.CalculateInterpolatedLightAndOcclusionProbes()或LightProbes.GetInterpolatedProbe()可能得到无法预料的结果(这两个函数功能不在这里讨论)。 若想强制更新tetrahedral tessellation,我们可以调用LightProbes.Tetrahedralize()或LightProbes.TetrahedralizeAsync()。这些函数会让Unity使用所有当前已加载场景中的所有光照探针的数据更新tetrahedral tessellation。 更新tetrahedral tessellation是占用CPU的操作,对CPU的影响随着光照探针数量的增加而变大。如果我们正在加载或卸载多个场景,同时更新tetrahedral tessellation,那么将会产生性能问题。所以延迟更新操作是一个比较好的选择,在我们加载或卸载了一定量的内容后,或是等到对CPU的影响不会产应性能问题时,再进行更新操作。 ### 10.6 运行时移动光照探针 我们可能需要在运行时移动光照探针。例如,如果我们通过加载多个场景来创建一个世界,然后将每个场景移动到不同的位置,我们需要移动光探针及其相关的场景对象。 我们可以使用[LightProbes API](https://docs.unity3d.com/cn/2023.2/ScriptReference/LightProbes.html "LightProbes API")在运行时移动Light Probes。我们不能通过更新Light Probe Group对象的Transform组件来移动Light Probes,因为Transform只影响烘焙。另外,当我们使用API移动Light Probes时,只有位置改变,存储在Light Probes中的烘焙数据保持不变。 步骤: 1. 使用 [LightProbes.GetInstantiatedLightProbesForScene](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/LightProbes.GetInstantiatedLightProbesForScene.html "LightProbes.GetInstantiatedLightProbesForScene") API克隆加载场景使用的LightProbes数据。从现在起,加载的场景(场景对象)将使用这个克隆数据。 2. 使用 [GetPositionsSelf](https://docs.unity3d.com/cn/2023.2/ScriptReference/LightProbes.GetPositionsSelf.html "GetPositionsSelf") 和 [SetPositionsSelf](https://docs.unity3d.com/cn/2023.2/ScriptReference/LightProbes.SetPositionsSelf.html "SetPositionsSelf") APIs设置新位置。 3. 使用 [LightProbes.Tetrahedralize](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/LightProbes.Tetrahedralize.html "LightProbes.Tetrahedralize ")或 [LightProbes.TetrahedralizeAsync](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/LightProbes.TetrahedralizeAsync.html "LightProbes.TetrahedralizeAsync") API更新场景中光照探针数据的内部结构,以便对象使用正确的光照数据。 如果我们将场景与包含光照探针的其他场景一起烘焙,则返回的数据包含来自所有烘焙场景的光照探针。在多场景中使用光照探针,请参考[Light Probes and Scene loading](https://docs.unity3d.com/cn/2023.2/Manual/light-probes-and-scene-loading.html "Light Probes and Scene loading")。 Tetrahedralize API可能需要花费较长的时间。如果我们移动多个光照探针,最好在最后统一进行Tetrahedralize处理。 如果我们需要读取光照探针在场景中的位置,但不移动它们,则可以使用 [LightProbes.GetSharedLightProbesForScene](https://docs.unity3d.com/cn/2023.2/ScriptReference/LightProbes.GetSharedLightProbesForScene.html "LightProbes.GetSharedLightProbesForScene") API来代替。 示例: 下面的代码将当前场景中的光照探针每帧移动到一个新位置。将脚本附加到场景中的任何对象上。当我们运行项目时,选择任意一个使用光照探针的对象,这样就可以看到探针移动了。光照探针组对象不移动。 ```cs using UnityEngine; using UnityEngine.SceneManagement; public class LoadSingleSceneAndMoveProbes : MonoBehaviour { void Update() { // Get a copy of the Light Probes in the current scene LightProbes lightProbes = LightProbes.GetInstantiatedLightProbesForScene(SceneManager.GetActiveScene()); // Get the Light Probe positions Vector3[] positions = lightProbes.GetPositionsSelf(); // Update the positions for (int i = 0; i < positions.Length; i++) { positions[i].x = positions[i].x + 0.01f; } // Copy the new positions back to the Light Probes lightProbes.SetPositionsSelf(positions, true); // Tetrahedralize to update the data in the LightProbes object of the scene. LightProbes.Tetrahedralize(); } } ``` ### 10.7 Light Probe Proxy Volume组件 Light Probe Proxy Volume(LPPV)组件可以帮助我们对那些不能使用光照贴图的大型动态游戏对象使用更多光照信息。 由光照探针而来的光照有一个旋转梯度,因为它是使用球谐函数,但缺乏空间梯度。这在较大的游戏对象或粒子系统中更为明显。游戏对象的照明与锚点(锚点位置影响使用哪些光照探针,形成四面体围绕自己,在MeshRender组件中可以重定义锚点来覆盖旧锚点)的光照相匹配,如果游戏对象过大,导致跨越照明梯度,部分游戏对象的光照可能看起来不正确。 LPPV组件在一个边界体(Bounding Volume)内生成一个插值的光照探针3D网格。我们可以在组件的属性中指定该网格的分辨率。插值的光照探针的球谐函数(SH)系数被上传到3D纹理中。然后在渲染时对包含SH系数的3D纹理进行采样,以计算对漫射环境光照的贡献。这为光照探针所照亮的对象增加了空间梯度。 标准着色器支持这个特性。要将其添加到自定义着色器中,请使用ShadeSHPerPixel函数。如何实现这个功能,参考粒子系统样本着色器代码示例(4.5.4)。渲染管道支持相关内容参考[这里](https://docs.unity3d.com/2022.3/Documentation/Manual/render-pipelines-feature-comparison.html "这里")。 #### 10.7.1 组件使用前提 ![](https://img-blog.csdnimg.cn/direct/afc9cdee57784e9a86ba9c603b1ac041.png) 首先,要使用此组件,需要一个带有Renderer组件的对象,以Mesh Renderer为例,将其Probes属性下的Light Probes设置为Use Proxy Volume,之后下面会出现一个Proxy Volume Override属性,我们只需要再在此物体上挂载一个LPPV组件即可,此时这个物体就使用了这个LPPV组件。当然我们也可以在别的物体上挂载一个LPPV组件,然后把这个对象拖拽到Proxy Volume Override属性上,进而使用这个别的物体上的LPPV组件。 #### 10.7.2 组件属性 * Refresh Mode:刷新模式。Light Probe Proxy Volume刷新方式,枚举类型。 * Quality:质量。影响使用LPPV组件的渲染器(Renderer)的估算的球谐函数(SH)频带的总数。可选项有Low、Normal。Low:仅使用从LPPV纹理中采样的2个波段(L0和L1),此选项可以通过不破坏批处理来提高性能。Normal:使用所有波段来估算SH,L0和L1是从LPPV纹理中采样的,L2在每个渲染器中是常数。 * Data Format:数据格式。有Float、Half Float两种。Float:纹理使用32位浮点类型通道格式来存储球谐函数系数。Half Float:纹理使用16位浮点类型通道格式来存储球谐函数系数。Half Float的优点是大多数设备都支持Half Float线性纹理采样,并且这种格式与32位浮点通道格式之间的精度差异并不明显,同时,这种数据格式在GPU上的纹理采样性能更好。 * Bounding Box Mode:可选属性有Automatic Local、Automatic World、Custom。 * Automatic Local:计算一个局部空间边界框。插值后的光照探针位置在这个边界框内生成,如果Renderer组件没有附加到GameObject上,那么就会生成一个默认的边界框。计算出的边界框会包裹住当前Renderer和其层级下的所有Renderer,这些Renderer的Light Probes属性需为Use Proxy Volume。(与Automatic World相比,若对象层级较深,且使用了和父级相同的LPPV组件,则其计算边界框时会花费更多的资源,但生成的边界框尺寸可能会更小一些,所包含的光照数据也更紧凑。) * Automatic World:计算一个边界框。其会包裹住当前Renderer和其层级下的所有Renderer,这些Renderer的Light Probes属性需为Use Proxy Volume。 * Custom:使用自定义边界框。边界框在GameObject的本地空间中指定。边界框可以使用编辑工具控制大小位置,也可以通过修改相关属性参数来控制大小位置,类似BoxCollider。 * Proxy Volume Resolution:Proxy Volume分辨率。插值的光照探针的数量受此属性的影响。 * Resolution Mode:分辨率模式。分为Automatic、Custom。Automatic:每个轴上的分辨率是使用我们所指定的每个单位面积的插值光照探针的数量和边界框的大小来计算的。Custom:允许我们在每个轴上指定不同的分辨率。PS:每个轴上的最终分辨率必须是2的幂,分辨率的最大值为32。 * Density:每单位面积的光照探针数量。此属性在选则Automatic模式时才会出现。 * X:X轴的分辨率。此属性在选择Custom模式时才会出现。 * Y:Y轴的分辨率。此属性在选择Custom模式时才会出现。 * Z:Z轴的分辨率。此属性在选择Custom模式时才会出现。 * Probe Position Mode:光照探针位置模式。有Cell Corner、Cell Center两种模式。此属性指定插值的光照探针到区域中心的相对位置。若一些光照探针穿过墙壁或其他几何体造成漏光问题,此选项可能有用。下图展示了两种模式的差别: ![](https://img-blog.csdnimg.cn/direct/b861bb76eae04fa4ac8e30f57383c176.png) #### 10.7.3 案例 ![](https://img-blog.csdnimg.cn/direct/44f92872f3f04304ae660d876f63f4d0.png) 这里简单说下,先设置好Cube的Mesh Render的属性,然后添加LPPV组件,之后在Lighting面板进行实时GI烘焙,效果就如图所示。左侧是红色反光,右侧是蓝色反光,完美。另外,我们隐藏Cube的Mesh Renderder后可以发现插值的光照探针(小球体): ![](https://img-blog.csdnimg.cn/direct/7fa578447f964011a50b629dc4cd14e6.png) #### 10.7.4 粒子系统样本着色器代码示例 官方文档案例。 ```cs Shader "Particles/AdditiveLPPV" { Properties { _MainTex ("Particle Texture", 2D) = "white" {} _TintColor ("Tint Color", Color) = (0.5,0.5,0.5,0.5) } Category { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" } Blend SrcAlpha One ColorMask RGB Cull Off Lighting Off ZWrite Off SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_particles #pragma multi_compile_fog // Specify the target #pragma target 3.0 #include "UnityCG.cginc" // You must include this header to have access to ShadeSHPerPixel #include "UnityStandardUtils.cginc" fixed4 _TintColor; sampler2D _MainTex; struct appdata_t { float4 vertex : POSITION; float3 normal : NORMAL; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; UNITY_FOG_COORDS(1) float3 worldPos : TEXCOORD2; float3 worldNormal : TEXCOORD3; }; float4 _MainTex_ST; v2f vert (appdata_t v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.color = v.color; o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { half3 currentAmbient = half3(0, 0, 0); half3 ambient = ShadeSHPerPixel(i.worldNormal, currentAmbient, i.worldPos); fixed4 col = _TintColor * i.color * tex2D(_MainTex, i.texcoord); col.xyz += ambient; UNITY_APPLY_FOG_COLOR(i.fogCoord, col, fixed4(0,0,0,0)); // fog towards black due to our blend mode return col; } ENDCG } } } } ``` #### 10.7.5 硬件需求 该组件至少需要Shader Model 4图形硬件和API支持,包括支持32位或16位浮点格式和线性过滤的3D纹理。 若想正常使用该组件,场景内需要有光照探针。如果需求没有得到满足,Renderer或Light Probe Proxy Volume组件检查器将显示一条警告消息。 ## 11 反射探针 ### 11.1 背景 CG电影和动画通常包含高度逼真的光照反射,这可以增强场景中物体的连通性,提升整体画面。然而,实现如此高质量的反射效果是非常耗费处理器性能的。但这对于电影这类影视作品来说不是大问题,而对于游戏来说就是要考虑性能问题了。 传统上,游戏使用一种称为反射映射(reflection mapping)的技术来模拟物体的反射,同时将处理开销保持在可接受的水平。这种技术假定场景中的所有反射物体都能"看到"(并因此反射)完全相同的环境。这对于游戏主角(比如一辆闪亮的汽车)来说非常有效,但当角色进入不同的环境时,就可能产生不合理的情况。比如一辆汽车驶入隧道,但其外窗仍然可以反射天空的倒影。 Unity通过使用反射探针改进了基础的reflection mapping,允许我们在场景中的战略性点位对视觉环境进行采样(因为耗费性能,所以是战略性点位)。一般来说我们应该将反射探针放置在反射物体(能反射周围场景的物体,比如镜子)外观明显变化的位置(比如隧道,突然就变黑了,反射内容也变黑了;建筑物附近,多了建筑物的反射;地面颜色变化的地方,颜色变了表示反射的内容变化明显;即这些位置的反射内容与其他位置的反射内容会有明显的不同),当一个反射物体经过反射探针附近时,反射探针采样的反射内容可用于该物体的反射图,进而显示出周围景象。若附近有多个反射探针的话,Unity会在它们之间进行插值,让反射逐渐变化。因此,反射探针可以在可接受性能开销的范围下,提供不错的反射效果。 ### 11.2 工作原理 在场景中,一个点的视觉环境可以用一个Cubemap来表示。从概念上讲,就像一个盒子,在其盒子内部的六个面上画上六个方向的视觉平面图像。如图所示: ![](https://img-blog.csdnimg.cn/direct/bdd82005dea940eb99b6d1da1ec61b3a.png) 对于一个能够显示反射内容的物体,它的着色器(shader)必须能够访问到表示Cubemap的图像。物体表面的每个点都可以"看到"表面所面对方向(即表面法向量方向)的Cubemap上的一小块,着色器在这一点上使用Cubemap上这一小块上的颜色来计算物体表面的颜色。不同情况计算的颜色也不同,比如镜子材料可能会准确地反射出颜色,而光亮的汽车可能会褪色一些。 如上所述,传统的reflection mapping仅使用单个Cubemap来表示整个场景的周围环境。Cubemap可以由艺术家绘制,也可以通过在场景中的某一点拍摄六张"快照(snapshots)"来获得,一张快照对应Cubemap的一面。而反射探针改进了这一点,允许我们在场景中设置许多的预设点,在这些点的位置我们可以拍摄Cubemap的快照。因此我们可以在场景中的任何点位处记录周围视图,通常这些点位是反射内容有着明显变化的点位。 除了上述记录周围视图的视点之外,反射探针还有一个由场景中不可见的盒子形状定义的效果区域(或称作用范围)。通过此效果区域的具有反射能力的物体,其Cubemap由该反射探针临时提供。所以当这个对象从一个区域移动到另外一个区域时,其Cubemap会发生改变,反射的内容也会改变。这也是为什么前面一直在强调反射探针应该放在反射内容有着明显变化的位置。 ### 11.3 反射探针类型 反射探针有三种类型(Type),可以在其组件属性面板上设置。三种类型分别为Baked、Realtime、Custom。 #### 11.3.1 Baked反射探针 ##### 11.3.1.1 特点 Baked反射探针会在编辑器状态中存储生成的Cubemap,并在后续中使用。一旦存储完成,那么反射行为就会被"冻结",所以在运行时,Bake反射探针无法对动态物体在场景中造成的动态变化做出反应。但好处是这种方式性能消耗很低,而且在许多用途上都还不错。比如,整个场景中只有一个运动的反射物体,那么它只需要反射周围的静态环境即可(个人感觉只适合空旷环境)。 ##### 11.3.1.2 使用演示 首先要先创建一个反射探针,如图: ![](https://img-blog.csdnimg.cn/direct/f5c0a2b0bd3040cdaad96d179976b8e7.png) 紧接着需要把反射探针的Type设置为Baked,如图: ![](https://img-blog.csdnimg.cn/direct/35b27afbcea14188a5577ecf3b281dbb.png) 然后要选择哪些对象是可以被反射探针反射的,这些对象需要被设置为静态,具体为勾选"Reflection Probe Static",如图: ![](https://img-blog.csdnimg.cn/direct/64c164157b504abab4853e39498da5ca.png) 之后就是烘焙操作,有两种方式,一种是点击反射探针组件最下方的Bake按钮,一种是在Lighting窗口中点击"Generate Lighting"按钮(实时GI、烘焙GI是否勾选不影响)。 ![](https://img-blog.csdnimg.cn/direct/675a60cc116e4993959558a824a40452.png) ![](https://img-blog.csdnimg.cn/direct/bd119d83606f496cabecb4befd8713c5.png) 这里我搭建了一个场景,场景中创建了一个反射探针,一个小球,一个Cube。反射探针设置为Baked,小球使用反射材质,Cube给了个蓝色材质,如图: ![](https://img-blog.csdnimg.cn/direct/c755a20c10e94db9becc25c8f415a2d6.png) 想让一个材质变为反射材质,只需做如下操作: ![](https://img-blog.csdnimg.cn/direct/7c2e38d2c0874742a255bea65534f684.png) 将材质上的这两个属性全拉到1即可。之后烘焙,看下效果: ![](https://img-blog.csdnimg.cn/direct/a0343d7bdab5435d991a3c410866e684.png) #### 11.3.2 Custom反射探针 ##### 11.3.2.1 特点 Custom反射探针可以和Baked模式一样,在编辑器中通过烘焙方式来生成Cubemap(测试发现用Lighting面板中的生成按钮,不能反射动态物体,所以建议用组件的Bake按钮执行烘焙),但也有其他可选方式:我们可以提供自定义的Cubemap,来实现反射。此模式的反射探针无法在运行时自动更新。此模式还有一个单独的Dynamic Objects属性,勾选后可以让非静态物体也被反射探针反射。 ![](https://img-blog.csdnimg.cn/direct/6eee8daaf43f4d81af39e2f9c4099d97.png) 这里就不使用演示了。 #### 11.3.3 Realtime反射探针 ##### 11.3.3.1 特点 Realtime反射探针会在运行状态创建更新Cubemap,这意味着反射将会是实时改变的,且不再局限于静态对象。但这会花费较高的性能来实时更新反射探针的视图,所以需要谨慎选择。Unity也提供了在脚本中触发视图更新的方法,所以我们可以有针对性的选择视图更新时机,以缓解性能问题。此外该模式还提供了一个时机切片属性,可以将更新分布到几帧内执行。以上这些更新方法、时机切片,都可以在组件属性面板上调整。 ##### 11.3.3.2 使用演示 首先创建一个反射探针,然后将Type设置为Realtime。然后创建一个非静态的Cube,最后运行即可,运行后拖动移动非静态物体可以看到反射内容发生了变化: ![](https://img-blog.csdnimg.cn/direct/0299df2084594ecfaeff07dbd566efb0.gif) ### 11.4 反射探针的使用 #### 11.4.1 默认反射探针 当我们创建一个新的场景时,Unity会自动创建一个隐藏的默认反射探针,此反射探针会储存Unity内置默认天空盒的Cubemap。 此反射探针中的数据是固定的,比如我们此时在Lighting窗口中修改了天空盒材质,那么此反射探针中的数据并不会改变,在反射物体身上的环境光照射将保持不变(环境光这里由天空盒提供,这里是保持默认天空盒的光照)。 若我们不想使用此默认反射探针,可以在Lighting窗口中执行"Generate Lighting"或"Clear Baked Data"。然后Unity会创建并使用一个Baked反射探针,此反射探针是Project窗口中的一个资源文件,当我们执行"Generate Lighting"便会更新它。 #### 11.4.2 探针位置 反射探针是个组件,挂载在对象上,所以其位置就是对象位置。但我们可以设置探针的作用范围,如图: ![](https://img-blog.csdnimg.cn/direct/3cead852835e4f409d49b82535fc8169.png) 我们可以组件上的相关属性来修改此作用范围的尺寸、位置偏移。需要注意,此矩形范围,无法旋转。 反射探针适合放置在场景中大物体旁边,因为这些物体会被很明显的反射,比如墙壁旁,角落等。当然小物体若需要反射的话,自然也要在其旁边放置反射探针,比如篝火。 #### 11.4.3 重叠的作用范围 设置相邻的反射探针的作用范围的位置,同时让它们没有重叠到一起,这是非常困难,幸运的是我们没有必要这样做。这样的话就面临了在重叠区域使用哪个反射探针的问题。默认情况下,Unity会计算反光物体(之前都是反射物体,感觉有歧义,这里改下)的边框范围与每个重叠的作用范围的交集,哪个作用范围与边框范围的交集体积最大,这个作用范围所属的反射探针将会被选择使用。如图: ![](https://img-blog.csdnimg.cn/direct/d13a8bc03eff45edb0b6be1256dc2994.png) 这里会选择使用A探针,因为交集体积比较大。当然,我们也可以调整这些探针的使用优先级,使用探针的Inportance属性(Inspector面板中),值越大,优先级越高。 #### 11.4.4 混合 若想要启用反射探针的混合效果,则需要在Project Settings→Graph→Tier Settings中设置。Tier Settings设置只能在使用Unity内置渲染管线时可用。当混合启用后,反光物体从一个区域到另一个区域时,Unity会逐渐淡出一个探针的Cubemap,同时淡入另一个探针的Cubemap。这种渐变模式,避免了反射内容突兀变化的情况。设置如图: ![](https://img-blog.csdnimg.cn/direct/d021e24a3aff4cfcbc4ff73dc0389bce.png) ![](https://img-blog.csdnimg.cn/direct/f9f6a50623c7405b872b12af889a5ac7.png) 可以看到,默认状态下(Use Defaults),反射探针的混合是使用的,若不想使用,只需要取消勾选Use Defaults,然后自定义设置取消勾选Reflection probes Blending即可。 混合效果演示: ![](https://img-blog.csdnimg.cn/direct/d8e516409e4048be927f78f61b5d3fd8.gif) 如图,左右两侧各有两个反射探针,其区域在中间重叠(不重叠也可以,但反光物体不能有完全离开两个反射探针作用区域的情况,反光物体边框会同时在两个作用区域内),反光物体在从一个区域到另一个区域时发生渐变效果。 ##### 11.4.4.1 Mesh Renderer(混合控制) 可使用Mesh Renderer组件的Reflection Probes属性来控制混合。可选项如下: * Off:反射探针的混合被禁用,只有天空盒被用于反射。(也可以理解为我们自己创建的反射探针被禁用了) * Blend Probes:只混合相邻反射探针,而不混合天空盒。放在室内、洞穴等看不到天空的场景中的物体,应该使用此项。 * Blend Probes and Skybox:类似Blend Probes,但其会将天空盒也用于混合中。对于那些可以见到天空的物体,应该使用此项。 * Simple:当有两个重叠的反射探针时,禁用混合。 ##### 11.4.4.2 混合权值计算 当探针具有相等的Inportance属性值时,通过将给定探针区域与目标边界框的相交(体积)除以所有探针与该目标边界框的相交之和来计算给定探针区域的混合权重。例如,如果边界框与探针A的区域相交1.0立方单位,与探针B的区域相交2.0立方单位,则混合权重为: * 探头A: 1.0 / (1.0 + 2.0) = 0.33 * 探头B: 2.0 / (1.0 + 2.0) = 0.67 换言之,混合内容将包含探针A33%的反射和探针B67%的反射。 在一个探针完全包含在另一个探针内的情况下,计算的处理略有不同,因为内部区域完全与外部区域重叠。在这种情况下,如果对象的边界框完全在内部区域内,则该区域的混合权重为1.0(即,根本不使用外部区域)。当目标部分不在内区域时,其边界框与内区域的交点体积除以该边界框的总体积。例如,交集体积为1.0立方单位,边界框体积为4.0立方单位,则内探头的混合权重为1.0 / 4.0 = 0.25。然后将该值从1.0中减去,得到外部探针的权重,在本例中为0.75。 当混合中涉及的一个探针的Inportance值高于另一个探针时,那么更重要的探针通常会覆盖另一个探针。(PS:给我的感觉是值高的在过渡计算混合权重时,其反射占比就高。) ### 11.5 高级反射探针特性 还有两种可以提高反射探针反射效果的特性:互反射(Interreflections)、盒子投影(Box Projection)。 #### 11.5.1 互反射 我们可能见过这种情况:两个镜子相对放置得很近,两面镜子不仅反射对面的镜子,而且反射那面镜子产生的反射。结果是两者之间不断的反射,像这样的物体之间的反射被称为互反射。 反射探针通过在其位置获取视图快照来创建Cubemap,但是仅使用单个快照,视图不能显示互反射,因此必须为互反射序列中的每个阶段拍摄额外的快照。 反射可以在两个物体之间来回"反弹(Bounce参数)"的次数是在Lighting 窗口中控制的,其位置如图: ![](https://img-blog.csdnimg.cn/direct/a171daca2bed4abc93c0158ec65eea8b.png) 这里的设置是全局设置,应用于所有反射探针。比如我们将一个反光物体与反射探针放在一起,注意选中反射探针其也可以将其反射内容显示,在Bounces为1时,放光物体在反射探针内显示为黑色,因为只能反射一次,如图: ![](https://img-blog.csdnimg.cn/direct/019903e06c6e4c58ac8b25d3aa583255.png) 将那个Bounces设置为2后: ![](https://img-blog.csdnimg.cn/direct/56db756ca5f84e28a056a4c9c8a4c08a.png) 值越大,可见层越深。 注意,反射反弹次数也等于探针必须烘焙的次数,完成完全烘焙所需的时间相应增加。因此,应该谨慎使用此特性,比如只有在这种相互反射特性在视觉上较为明显时,才应该考虑使用此特性。 #### 11.5.2 盒子投影 通常,假设反射Cubemap与给定的物体之间的距离是无穷大,当物体旋转时,Cubemap不同角度会随着物体的旋转而可见,但物体与反射的事物的距离在反射内容中是保持不变的。这在室外场景中没什么影响,但在室内场景中却存在一定的局限性,因为在室内我们更容易靠近反射的物体,而我们靠近时,反射内容也应该变大。 Box Projection选项允许我们在距离反射探针有限距离的位置创建反射Cubemap,从而允许对象根据其与Cubemap墙壁的距离显示不同大小的反射。此Cubemap的大小有反射探针的作用区域决定,即由Box Size属性决定,这样的话,比如我们要对于一个房间内部设置反射探针,那么应该设置作用区域大小以匹配房间大小。 如何设置?在使用内置渲染管道的情况下,我们可以在Project Settings→Graph→Tier Settings中设置Reflection Probes Box Projection属性来设置是否启用全局性的盒子投影特性,这与设置是否启用混合功能的方式是一样的,具体可参考前面的混合部分内容。但是注意,在设置好后只是说给予了使用盒子投影的权限,具体是否使用还要去反射探针大的Inspector面板处通过是否勾选Box Projection属性来决定。当我们在全局设置中没有启用时,Inspector面板上的Box Projection属性会处于非勾选状态,且无法操作。 盒子投影修复视差问题对比展示: ![](https://img-blog.csdnimg.cn/direct/956002353dad45fb84ce20e26aebf3b0.png) ### 11.6 反射探针性能 渲染反射探针的Cubemap会占用大量的处理器时间,原因如下: 1. Cubemap的6个面都必须使用一个在探针位置的相机(Camera)单独渲染。 2. 在反射可以反弹(Bounce)时,探针需要为每个反弹的层级花费单独的时间进行渲染。可参考"高级反射探针特性"中的内容。 3. Cubemap的各个mipmap层级需要经过模糊处理,才能有光滑的反射。(mipmap不懂查一下就好) 渲染反射探针所花费的时间会影响编辑器中的烘焙工作流程,更重要的是,会影响玩家的运行时性能。下面将介绍一些将反射探测对性能的影响降到最低的技巧。 #### 11.6.1 通用优化 通用优化是通过通用属性来设置的。这些优化若没有特殊提及应用模式,则其可以同时影响Baked、Runtime两种模式下的性能。 ##### 11.6.1.1 Resolution (此属性可在反射探针Inspector面板中找到) Cubemap的分辨率(Resolution)越高,渲染的时间就会越长。我们可以在反射细节不太明显的地方设置较低的分辨率来节省性能。在细节明显的地方则是使用较高分辨率。 ##### 11.6.1.2 Culling Mask (此属性可在反射探针Inspector面板中找到) 此属性可以决定哪些对象能够在反射探针中进行渲染,哪些不被渲染。我们可以将无关紧要的对象剔除出去,不去渲染它们,以此来节省性能。比如小石头、植物等等,将它们统一放到一个层级(Layer)中,然后通过此Culling Mask属性进行剔除。 ##### 11.6.1.3 Texture compression 为了优化渲染时间和降低GPU内存消耗,可以使用纹理压缩。纹理压缩需要在Lighting窗口中设置(menu: Window \> Rendering \> Lighting),导航到nvironmental Lighting,在反射中使用压缩选项,如图: ![](https://img-blog.csdnimg.cn/direct/1de75cf9b9bf4436ad416517e44dc377.png) 注意,实时反射探针不会在内存中被压缩,它们在内存中的大小取决于分辨率和HDR设置。正因为如此,采样实时反射探针通常比采样烘焙反射探针更耗费资源。 #### 11.6.2 实时反射探针优化 实时探针的渲染开销通常比编辑器中的烘焙探针渲染开销要更大,如果管理不当,就会导致更新过于频繁,进而影响到帧率。所以实时探针也提供了一些自己的属性,来尽可能的优化自己的渲染效率。 ##### 11.6.2.1 Refresh Mode (此属性可在反射探针Inspector面板中找到,需要反射探针选择Realtime模式) 此属性可以决定如何更新反射探针,其可选项有On Awake、Every Frame、Via scripting三种。 就处理器耗费时间而言,每帧(Every Frame)模式是最耗费性能的,如果所有反射探针都使用这种更新模式,很有可能会出现性能。 如果模式设置为On Awake,则探针将在运行时更新,但仅在场景开始时更新一次。如果场景(或场景的一部分)是在运行时设置的,而且在其生命周期内没有发生变化,就可以使用这种模式。 Via scripting模式允许我们使用脚本来更新反射探针。虽然需要一些编写代码的过程,但这种方式是很适合于优化。比如我们可以根据反射探针所表现内容是否明显来决定是否更新探针,经过一个物体旁边,若此物体较小不明显就不更新,若较大明显则更新。 ##### 11.6.2.2 Time Slicing (此属性可在反射探针Inspector面板中找到,需要反射探针选择Realtime模式) 当我们选择Every Frame更新模式时,处理器的负载是很大的,Time Slicing可以让我们将更新分布到多帧中进行,以此来减少负载。这个属性有三种选项: * All Faces at Once:将6个Cubemap的面在同一帧内立即被渲染,但是随后的6个第一级mipmap的模糊操作将在单独的帧上进行,剩下的mipmap将在一帧上完成模糊操作,并且在另一帧上将此结果复制到Cubemap上。因此,完成整个更新需要9帧。 * Individual Faces:工作方式与All Faces at Once相同,不同之处在于Cubamap的6个面的初始渲染发生在它们自己单独的一帧中(不再是6个面都在第一帧中渲染)。因此,完成整个更新需要14帧。此选项对帧率的影响是最小的,但也因此需要较长时间来完成更新,有时会造成视觉上的问题,比如,当照明突然改变时(灯突然被打开),画面的更新可能就会出现明显的延迟。 * No Time Slicing:完全禁用Time Slicing操作。每个探针的完整更新都在一帧内完成,这确保了反射与周围物体的同步,但是在处理器性能消耗上通常是无法接受的。 与其他优化操作一样,我们应该在反射不太重要的地方使用低质量的选项来降低性能消耗,而在反射细节容易被注意到的地方使用高质量效果的选项(No Time Slicing)来提升视觉效果。 ### 11.7 反射探针组件 #### 11.7.1 概念 反射探针就想一台摄像机,可以从各个方向捕捉周围环境的球形视图,然后将捕获的图像存储为具有反射材质的物体可以使用的Cubemap。在给定的场景中可以使用多个反射探针,并且可以将物体设置为使用最近反射探针产生的Cubemap,物体上可以产生更真实的反射效果。 ![](https://img-blog.csdnimg.cn/direct/fe49e6a0b40e487a8bc95ddf70c8fa08.png) #### 11.7.2 属性与渲染管线 反射探针在Inspector面板中的属性会根据不同的渲染管线有所变化: * 如果项目使用通用渲染管线(URP),请参阅[URP包文档网站](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/ "URP包文档网站")。 * 如果项目使用高清晰度渲染管线(HDRP),请参阅[HDRP包文档网站](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@latest/ "HDRP包文档网站").。 * 如果项目使用内置渲染管线,后面将会介绍。 #### 11.7.3 属性 ##### 11.7.3.1 Type 可选项有Baked、Custom、Realtime。若选择了Baked,反射探针不会在运行时捕捉到被禁用了"Reflection Probe Static"标志(设置物体静态那里可设置此项)的游戏对象。若我们想使用一个Baked类型的反射探针捕获动态游戏对象,那么我们可以修改类型为Custom,并勾选"Dynamic Objects"属性。 * Dynamic Objects:在Type为Custom时显示。勾选后强制未标记为Static的对象被烘焙到反射中,即动态对象也能被反射探针反射。 * Cubemap:在Type为Custom时显示。设置反射探针的自定义Cubemap。 * Refresh Mode:反射探针的刷新模式。在Type为Realtime时显示。可选项有On Awake、Every Frame、Via Scripting。具有反射材质的物体所反射的内容会受到此属性的影响,比如物体移动时反射内容若需要变化则需要使用Every Frame来每帧更新。 * On Awake:仅在反射探针第一次被激活时渲染一次。 * Every Frame:每帧更新时都会渲染反射探针。此属性下可以选择使用Time Slicing属性来调整性能问题。 * Via Scripting:不会自动刷新反射探针,而是通过用户脚本中的命令来刷新。 * Time Slicing:时间切片。在Type为Realtime时显示。设置反射探针应该如何随时间来安排其更新,可选项有All Faces At Once、Individual Faces、No Time Slicing。 * All Faces At Once:将更新分布在9帧内完成。 * Individual Faces:将更新分布在14帧内完成。 * No Time Slicing:将更新在1帧内完成。 ##### 11.7.3.2 Runtime settings * Importance:重要性。指示反射探针的优先级。当一个对象在多个反射探针范围内时,Unity会把高优先级(大值)的反射探针渲染到低优先级的反射探针上。这个选项也会影响混合(Blending),更多详细细节可以去看前面"混合"部分的内容。 * Intensity:在着色器中应用到此反射探针纹理上的强度修饰符。可以理解为纹理色彩强度,若此值为0则反射内容为黑色,若此值过大反射内容则为纯白。 * Box Projection:勾选此属性,以启用反射UV映射的投影,即前面所提及的盒子投影。 * Box Size:调整反射探针作用范围的尺寸。 * Box Offset:调整反射探针作用范围的偏移值。 ##### 11.7.3.3 Cubemap capture settings * Resolution:捕获反射图像的分辨率。 * HDR:是否为Cubemap启用高动态范围(HDR)。这也决定了探针数据是以OpenEXR还是PNG格式保存。 * Shadow Distance:渲染反射探针时,绘制阴影的距离(可见范围的意思)。 * Clear Flags:指定如何填充Cubemap的空背景,选项有天空盒(Skybox)、纯色(Solid Color)。 * Background:反射Cubemap在渲染之前使用的背景颜色(影响上面的Solid Color)。 * Culling Mask:用于剔除或包含可应用反射的层级(Layer)。勾选的是包含的。 * Occlusion Culling:在烘焙反射探针时,是否使用遮挡剔除。 * Clipping Planes:将反射探针看作相机,其即为"相机"的近、远裁切面。在近、远之间的内容才能被观测到。 #### 11.7.4 细节 反射探针组件的Inspector面板上方有两个按钮,可以帮助调整"作用范围"Box的Size与Offset。 ![](https://img-blog.csdnimg.cn/direct/3c08a408dcad4a9a8dedc9925d341b1d.png) 反射探针的Type属性决定了如何创建和更新反射数据: 1. Baked探针存储了一个静态反射Cubemap,它是在编辑器中烘焙生成的。 2. Custom探针存储一个静态反射Cubemap,它可以通过烘焙生成,也可以由用户手动设置。 3. Realtime探针在运行时更新反射Cubemap,因此可以对场景中的动态对象做出反应。 若想使用反射Cubemap,其对象Mesh Renderer上的Reflection Probes必须是启用的,并且使用一个支持反射探测的着色器。当对象进入反射探针的作用范围时,便可将Cubemap应用到对象上。 我们也可以手动设置一个反射探针,通过使用Mesh Renderer上属性。选择Reflection Probes属性为Simple、Blend Probes、Blend Probes and Skybox中的一个(实际就是启用),然后将所选反射探针拖拽到Anchor Override属性上即可。拖上去后,对象的锚点就与所拖的反射探针一致了,所以就会处在其作用范围内,就可以使用其Cubemap了。注意,若对象本来用着多个探针的Cubemap(处在多个作用范围内),我们拖上指定探针后,之后只会使用指定探针的Cubemap。 ## 12 预计算的光照数据 预先计算的光照数据和烘焙的光照是不一样的。烘焙的光照数据是静态的,例如,游戏对象的外观可以随着它围绕光照探针移动而改变(如果该游戏对象启用了Contribute Global Illumination)。然而,光照探针本身的数据并没有改变。Unity可以将烘焙光照数据存储在光照贴图、光照探针和反射探针中。 Enlighten Realtime Global Illumination不依赖于烘焙光照。相反,它使用预先计算的能见度数据来加快确定光线实时移动如何影响其光线所能到达的表面的过程。(好,说得好啊,把烘焙GI和实时GI所使用的数据区分开了。) 本节包含以下内容: * 生成光照数据。 * 光照数据Asset文件(Lighting Data Assets)。 * GI缓存。 ### 12.1 生成光照数据 编辑器遵循不同的步骤来计算 Enlighten Realtime Global Illumination 和 Baked Global Illumination。进度条显示当前进程的信息。当流程运行时,我们可以继续在编辑器中工作,不会受到影响。 光照预计算的阶段如下: **Enlighten Realtime Global Illumination** 1. 创建几何(Create Geometry) 2. 布局系统(Layout Systems) 3. 创建系统(Create Systems) 4. 创建图集(Create Atlas) 5. 聚类(Clustering) 6. 可见性(Visibility) 7. 光传输(Light Transport) 8. Tetrahedralize探针(Tetrahedralize Probes) 9. 创建ProbeSet(Create ProbeSet) **Probes** 1. Ambient Probes 2. Baked/Realtime Ref. Probes #### 12.1.1 生成数据 Unity的预计算照明解决方案只考虑静态几何。要开始照明预计算过程,我们需要在场景中至少有一个静态GameObject。 当我们启动一个预计算,Unity编辑器评估和计算我们的场景光照的所有方面。如何启动?在Lighting窗口内通过Generate Lighting按钮启动,Unity会重新计算和烘焙反射探针。 #### 12.1.2 构建之前 在构建(build)游戏之前,为所有场景生成光照数据,以确保不会丢失任何光照数据。 当我们为场景生成光照时,Unity将会把光照数据保存为Asset文件,存放在我们的项目目录中。这确保了这些数据是构建的一部分。 ### 12.2 光照数据Asset文件 光照数据Asset在Unity编辑器中存储场景的预先计算的光照数据。由于工作流程的原因,光照数据Asset在项目中作为单独的文件存在。将预先计算的光照数据存储在单独的文件中意味着对预先计算的光照数据的更改不会导致对场景文件的更改。光照数据Asset并未打算提供给用户编辑。 当我们调用光照预计算时,Unity将预先计算的光照数据存储在光照数据Asset中,无论是通过使用Lighting窗口中的Generate Lighting按钮,还是通过使用[Lightmapping.Bake](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Lightmapping.Bake.html "Lightmapping.Bake")或[Lightmapping.BakeAsync](https://docs.unity3d.com/6000.0/Documentation/ScriptReference/Lightmapping.BakeAsync.html "Lightmapping.BakeAsync") APIs来调用。 光照数据Asset包含全局光照数据和为场景重新创建光照所需的所有支持文件。这个Asset引用了渲染器、实时光照贴图、烘焙光照贴图、光照探针、反射探针和存储这些元素之间关系的数据结构。它还包括所有预先计算的Enlighten Realtime Global Illumination数据,这些数据用于更新玩家的实时全局光照。 当我们改变场景时,例如,通过破坏标记为Contribute GI的GameObject上的预制体连接,Asset数据将过时,必须重新构建。 在光照构建过程中生成的中间文件,其不是生成Player构建所需要的,这些中间文件不是Asset的一部分,而是存储在GI Cache中的。(中间文件,存储在GI Cache中。) 光照数据Assets的构建时间可以有所不同。如果我们的GI Cache已经被填满了,也就是说,我们之前在机器上做过一次烘培(场景与当前状态相同),那么当前再次烘焙就会很快。如果我们将场景拉到具有空白缓存的机器上,或者由于缓存大小限制而删除了所需的缓存数据,则必须首先用中间文件填充缓存,这需要运行预计算和烘焙进程,这些步骤可能需要一些时间。 #### 12.2.1 默认Asset文件 当你添加一个新场景时,Unity使用default Lighting Data Asset,该Asset引用了一个隐藏的环境探针和一个隐藏的反射探针,它们从Unity内置的默认天空盒中捕获环境光照。此Asset不会出现在Project窗口中。 隐藏探针中的数据是固定的。例如,如果我们改变了Lighting窗口中的天空盒材质,探针中的数据是不会改变的,所以物体上的环境光照保持不变。 要停止使用Default Lighting Data Asset,只需要在Lighting窗口中点击Generate Lighting或其下拉框中的Clear Baked Data按钮即可。Unity便会创建并使用一个新的Default Lighting Data Asset和一个新的烘焙反射探针(环境反射探针)。新创建的这些Assets(包括光照数据资源与反射探针)会出现在Project窗口中,并在我们点击Generate Lighting按钮时进行更新。 ### 12.3 全局光照缓存 全局照明(GI)缓存是一个内部数据缓存,Unity编辑器在预计算Baked Global Illumination和Enlighten Realtime Global Illumination的光照数据时,使用它来存储中间文件。通过将这些数据存储在缓存中,Unity可以加速后续的预计算。 我们电脑上的所有Unity项目都共享缓存,所以具有相似内容以及相同lightmapping后端的项目可以使用相同的缓存文件。我们还可以在不同的机器之间复制GI Cache文件夹,这可以使你的灯光构建更迅速,因为Unity是从GI Cache文件夹中获取文件,而不用重新计算它们。 我们可以使用GI Cache preferences来管理缓存的大小、位置、压缩。更多信息可参阅[Preferences](https://docs.unity3d.com/6000.0/Documentation/Manual/Preferences.html#GI-Cache "Preferences")。 如果我们尝试在同一台计算机上运行多个编辑器实例,编辑器将显示以下警告信息:"The GI Cache is using increasing amounts of space on your hard drive to support concurrent lightmap generation. To prevent failed bakes, close all other instances of the Unity Editor."。(大值意思就是多开编辑器会使GI缓存使用越来越多的硬盘空间,为了支持并发生成光照贴图,为了防止烘焙失败,我们应该关闭其他编辑器,只留一个需要的。) #### 12.3.1 清理GI缓存 有关如何清除GI缓存的详细信息,请参阅[GI Cache Preferences](https://docs.unity3d.com/6000.0/Documentation/Manual/Preferences.html#GI-Cache "GI Cache Preferences")文档。我们不能通过在Lighting窗口中选择Clear Baked Data按钮来清除GI缓存。 清除GI缓存只能作为最后的手段。如果我们的项目强制我们清除GI缓存,那说明出BUG了([report it as a bug](https://unity.com/releases/editor/qa/bug-reporting "report it as a bug"))。 ## 13 调试光照的绘制模式 场景视图有几个调试绘制模式(Debug Draw Mode),可以帮助我们理解和调试场景中的灯光。使用[Scene view View Options toolbar](https://docs.unity3d.com/cn/2023.2/Manual/ViewModes.html "Scene view View Options toolbar")来选择调试绘制模式。如果我们使用的时通用渲染管线或高清渲染管线,则不会显示所有模式。我们可以使用Rendering Debugger窗口来调试这些管线中的光照。 选择调试绘制模式: ![](https://img-blog.csdnimg.cn/direct/ec897136047c490b911369073c33c178.png) ### 13.1 Lighting #### 13.1.1 Contributors / Receivers ![](https://img-blog.csdnimg.cn/direct/4a097ad9ab384bf8bbe2358a128568ee.png) 此模式根据对象是否贡献(contribute)和接收(receive)全局光照(GI)显示以下颜色: * 橙色表示没有Contribute GI。 * 绿色表示物体从光照贴图Receive GI。 * 蓝色表示物体从光照探针Receive GI。 使用[Preferences window](https://docs.unity3d.com/6000.0/Documentation/Manual/Preferences.html#colors "Preferences window")可自定义颜色。 #### 13.1.2 Shadow Cascades ![](https://img-blog.csdnimg.cn/direct/9870a4c3370843aab1ee79af208e6d79.png) 此模式为每个阴影级联显示不同的颜色。颜色与[Quality Settings window](https://docs.unity3d.com/6000.0/Documentation/Manual/class-QualitySettings.html#Shadows "Quality Settings window")的**Shadows**部分中的阴影级联颜色相匹配。 我们可以使用此模式来帮助调整阴影距离,级联计数和级联阴影分割。 此模式使用场景视图远平面而不是阴影距离。如果我们想将游戏中的相机行为与较小的远平面距离相匹配,可能需要减小阴影距离。 ### 13.2 Global Illumination 当我们使用 [Enlighten Realtime Global Illumination](https://docs.unity3d.com/6000.0/Documentation/Manual/realtime-gi-using-enlighten.html "Enlighten Realtime Global Illumination") 或 [Baked Global Illumination](https://docs.unity3d.com/6000.0/Documentation/Manual/Lightmapping.html "Baked Global Illumination")时,下面的模式可用。 #### 13.2.1 Indirect (Realtime Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/1efeb607151a4ce8abf82211603e9849.png) 此模式显示由Enlighten Realtime Global Illumination生成的实时光照贴图。 #### 13.2.2 Directionality ![](https://img-blog.csdnimg.cn/direct/bde08cd9e7de4572ba99adc352f0171b.png) 模式将主光方向显示为一种颜色。关于主方向光的相关信息,参阅[Directional Mode](https://docs.unity3d.com/6000.0/Documentation/Manual/LightmappingDirectional.html "Directional Mode")。 #### 13.2.3 Albedo ![](https://img-blog.csdnimg.cn/direct/af494c15e328422a94c6c3c556f556f6.png) 此模式显示材料的反照率(Albedo)颜色。 #### 13.2.4 Emissive ![](https://img-blog.csdnimg.cn/direct/e0ce23ded71f4c0a89ac82d74e0c60b7.png) 此模式显示发光(Emissive)材料的发光颜色。 #### 13.2.5 UV Charts ![](https://img-blog.csdnimg.cn/direct/722a1d8b1d9648df9038795637ca01d0.png) 此模式为每个UV chart(也称为UV island)显示不同的颜色。 我们可以使用这个模式来检查光照贴图在几何上的scale。可以使用[Lightmap Parameters Asset](https://docs.unity3d.com/6000.0/Documentation/Manual/class-LightmapParameters.html "Lightmap Parameters Asset")中的**Resolution** 属性来改变scale,或者是使用在单个Renderer上的**Scale in Lightmap**属性来改变scale。 #### 13.2.6 Systems (Realtime Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/e66c9b9b18e44a5cb2e48832ed8ec4c5.png) 此模式为每组集群(clusters)(系统)显示不同的颜色,这些集群(系统)是由Enlighten Realtime Global Illumination创建的,以生成实时光照贴图。 想了解clusters是什么或更多详细信息,参阅[How Enlighten Realtime Global Illumination works](https://docs.unity3d.com/6000.0/Documentation/Manual/realtime-gi-using-enlighten.html#how-enlighten-realtime-global-illumination-works "How Enlighten Realtime Global Illumination works")。 #### 13.2.7 Clustering (Realtime Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/aff153eb62684d98a6a4515edafd7584.png) 此模式为每组集群(clusters)显示不同的颜色,这些集群是由Enlighten Realtime Global Illumination创建的,以生成实时光照贴图。 大型场景可能会产生更多的集群,而超过Unity在内存中可以存储的容量。要减少集群的数量,可以使用Lightmap Parameters Asset中的Cluster Resolution属性来调整集群与几何形状的比例。 #### 13.2.8 Lit Clustering (Realtime Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/f606d75becf14dba9a50729d8dd165c3.png) 此模式为每组集群(clusters)显示不同的颜色,这些集群是由Enlighten Realtime Global Illumination创建的,以生成实时光照贴图。同时,此模式还会使用光照贴图中的颜色。 #### 13.2.9 Texel Validity (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/fe6bf8ab03e2485390beb18aa2d8c9e4.png) 此模式下,如果烘焙光照贴图texel是无效的,则其表面会显示红色。当光照映射过程从表面发射光线,并主要击中背面时,Unity将texel标记为无效。Unity会试图通过查看相邻的有效texel来为无效texel插入颜色。 要调整将texels标记为无效的阈值,需要使用 [Lightmap Parameters Asset](https://docs.unity3d.com/6000.0/Documentation/Manual/class-LightmapParameters.html "Lightmap Parameters Asset ")中的**Backface Tolerance**属性。 #### 13.2.10 UV Overlap (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/561a1baba5dc4c4f92d4bb2047a75d34.png) 此模式下,光照贴图的UV如果离得太近,重叠的地方就会标记为红色。重叠可能会导致混叠,像素化和其他问题。更多信息参考 [Fixing lightmap UV overlap](https://docs.unity3d.com/6000.0/Documentation/Manual/ProgressiveLightmapper-UVOverlap.html "Fixing lightmap UV overlap") 。 #### 13.2.11 Lightmap indices (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/1bfdee3be1584d09a77506ba2914436e.png) 此模式为每个baked lightmap显示不同的颜色。 #### 13.2.12 Light Overlap (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/306fe4799b7e45e49aa31f65e22d47d7.png) 此模式下,如果我们使用了 [Shadowmask Lighting Mode](https://docs.unity3d.com/6000.0/Documentation/Manual/LightMode-Mixed-Shadowmask.html "Shadowmask Lighting Mode"),如果光没有贡献baked shadow mask textures,则会显示一个红色的光体。这意味着有超过4个光体重叠,所以高亮的光必须回落到完全烘焙。更多信息请参考 [Light Mode: Shadowmask](https://docs.unity3d.com/6000.0/Documentation/Manual/LightMode-Mixed-Shadowmask.html "Light Mode: Shadowmask")。【后面高亮光那句没懂,好吧,Light Mode Shadowmask笔记关于这里提的内容也没懂....属于连锁反应了】 #### 13.2.13 Baked Lightmap (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/4bb32e4202584a7097dee3e07475f2d6.png) 此模式会显示由Baked Global Illumination生成的光照贴图。 #### 13.2.14 Shadowmask (Baked Global Illumination only) ![](https://img-blog.csdnimg.cn/direct/d3f4cdb1080e4b81be3bd1d610af6c12.png) 如果我们使用了 [Shadowmask Lighting Mode](https://docs.unity3d.com/6000.0/Documentation/Manual/LightMode-Mixed-Shadowmask.html "Shadowmask Lighting Mode"),此模式将显示baked shadow mask textures。更多信息请参考 [Light Mode: Shadowmask](https://docs.unity3d.com/6000.0/Documentation/Manual/LightMode-Mixed-Shadowmask.html "Light Mode: Shadowmask")。 ### 13.3 光照可视化悬浮窗口参考 光照可视化悬浮窗口(Lighting Visualization overlay)中的属性取决于我们选择的调试绘制模式。如图是Indirect模式下的悬浮窗口: ![](https://img-blog.csdnimg.cn/direct/b75d8b8b9e2140fcb655cb5545d90b58.png) 属性列表: * Lighting Data:选择Unity是在调试绘制模式中使用当前的baked lightmaps,还是在更新场景时Unity重新生成的临时lightmaps。更多信息可参考 [Preview lightmapping](https://docs.unity3d.com/6000.0/Documentation/Manual/Lightmapping-preview.html "Preview lightmapping") 。可用选项有:Baked、Preview,分别对应前面说的当前baked与临时lightmaps。 * Show Lightmap Resolution:使用棋盘图案来显示光照贴图的分辨率。每个方格图案是光照贴图中的一个像素。 * Highlight Back-Facing Geometry:显示背面几何图形为紫色。使用 [Preferences window](https://docs.unity3d.com/6000.0/Documentation/Manual/Preferences.html#colors "Preferences window") 改变颜色。 * Adjust Lightmap Exposure:提高或降低光照贴图颜色的亮度,来帮助使颜色范围的可视化效果更好。默认值为0。 ### 13.4 更多内容 更多内容请参阅Unity论坛中的[Lightmapping troubleshooting guide](https://forum.unity.com/threads/lightmapping-troubleshooting-guide.1340936/ "Lightmapping troubleshooting guide")。 ## 14 后记 终于写完了,看得头疼,有时候一句话看来看去看不懂。这次基本把文档的内容都给写上了,后续再看别的模块的时候考虑该丢的丢,不什么都往上面写了,不过也不好说,毕竟害怕遗漏什么。后续若有内容修改就直接在本篇修改。好了,光照篇到此结束。

相关推荐
晴空了无痕14 小时前
群体智能避障革命:RVO算法在Unity中的深度实践与优化
算法·unity·游戏引擎
EQ-雪梨蛋花汤18 小时前
【UnityEditor扩展】如何在 Unity 中创建棱柱体(用作VR安全区检测),同时在编辑器插件中实现与撤销/恢复功能
unity·编辑器·vr
BuHuaX19 小时前
C#的反射机制
服务器·unity·c#·游戏引擎·游戏程序
不吃斋的和尚21 小时前
Unity HDRP管线用ShaderGraph还原Lit,方便做拓展;
unity·游戏引擎
benben0442 天前
Unity3D仿星露谷物语开发34之单击Drop项目
游戏·ui·unity·游戏引擎
benben0442 天前
Unity3D仿星露谷物语开发33之光标位置可视化
游戏·ui·unity·游戏引擎
张屿秋2 天前
在Unity中,如果物体上的脚本丢失,可以通过编写一个自定义编辑器脚本来查找并删除这些丢失的组件
unity·编辑器·游戏引擎
闪电麦坤953 天前
Unity:Simple Follow Camera(简单相机跟随)
unity·游戏引擎
一个程序员(●—●)3 天前
xLua环境控制+xLua的Lua调用C#的1
开发语言·unity·c#·lua
帮帮志3 天前
05.unity 游戏开发-3D工程的创建及使用方式和区别
3d·unity·游戏引擎