Notes of Unity Perception Package

Perception 是Unity官方提供的,用以生成计算机视觉相关内容的包。Synthetichumans 是用来快速生成多个人物的资源包。

Perception

通过查阅官方文档完成设置。

  • Click on the plus (+) sign at the top-left corner of the Package Manager window and then choose the option Add package from git URL….
  • 🟢 Action: Enter the address com.unity.perception and click Add.

导入完成,并打开项目后需要更改项目相关设置:

Open Edit -> Project Settings -> Editor, and disable Asynchronous Shader Compilation.

Search for and select the asset named HDRP High Fidelity in your project, and set Lit Shader Mode to Both.

Synthetichumans

通过查阅官方文档完成设置。
因为整个包体积较大,实际采用先Clone后添加的到项目Package列表中的办法。

Add package from disk

在manifest.json中加入以下条目

1
2
"com.unity.cv.synthetichumans": "file:../../com.unity.cv.synthetichumans",
"com.unity.perception": "file:../../com.unity.perception",

Usage

Perception Camera

为相机添加Perception Camera组件,让此相机拥有相关能力。

Camera Labelers & Labeling

对于所需要的ground-truth数据,可以在Camera Labelers中注明。

labeler可以自己创建,Perception 包为我们已经为我们提供了以下常用的labelers:

keypoint labeling, 3D bounding boxes, 2D bounding boxes, object counts, object information (pixel counts and ids), instance segmentation, semantic segmentation, occlusion, depth, normals, and more.

对于一些labelers来说,可以在Unity中实时展示。Show Labeler Visulizations打上勾即可

我们需要告诉Perception Camera应该标记哪些物体。例如,我们想生成一个苹果的ground truth,我们就需要告诉Unity场景中的什么是苹果。场景中的苹果,应该带有”苹果“的标签。
image-20230227201610587 我们可以注意到,Camera Labelers中有ID Label Config字段,这是Perception Camera会去留意的标签的一些配置

创建一个ID Label Config

In the Project tab, right-click the Assets folder, then click Create → Perception → ID Label Config.
image-20230227202819009

我们可以将这个新创建的ID Label Config传给对应的Labeler

对于我们想要被标注的物体, 我们将为其添加Labeling组件。Labeling 表示此物体将携带一些与对应Label Config相联系的标签。可以看到,此处有Use Automatic Labeling字段,勾选时,将用一些规则(通常是此物体所在的文件夹名或资产名),使得此物体自动携带上标签。不勾选时,我们可以手动选择一些建议的标签,或者新建标签。在随后我们可以点击Add to Label Config将此标签添加到对应的Label Config

image-20230227203553862

每个物体都可以携带多个Labels,这样就可以实现不同的物体可以与不同的Camera Labelers交互。例如,我可以将苹果带上水果和苹果的标签,使得一种只关注水果的Camera Labelers获得信息,一种能够希望区分香蕉和苹果Camera Labelers获得相应的信息

我们可以使用Assets → Perception → Create Prefabs from Selected Models来快速导入.fbx的模型文件

Randomizers

随机器generation1

Inspect Synthetic Data

一般的,数据集将以SOLO的格式(数据的保存结构),通过一下方法,我们可以调节相关的设置

Open the Project Settings window, by selecting the menu Edit → Project Settings. Select Perception from the left panel. This will bring up the Perception Settings pane.

Introduction to VR Development

Virtual reality (VR) is a simulated experience that employs pose tracking and 3D near-eye displays to give the user an immersive feel of a virtual world.

The equipment I am using now is Pico Neo3 based on PICO Unity Integration SDK

对于使用Unity开发, 其 XR Interactioin Toolkit 包已经提供了相当实用强力的脚本与抽象。本文会给出一些踩的坑和常见用法。

Environment:

  1. Unity Editor 2021.3.5f1c1

  2. XR Interaction Toolkit | XR Interaction Toolkit | 2.2.0

  3. Preview Tool provided by Pico Official

Notice

  • 2.2.0中,XRKT提供了一些新的方法与属性, 一些旧版的被标记为弃用。本例中的代码可能有些无法在老版本中实现,但旧版一般也都提供了近似的方法与属性。

  • 推测在使用Preview Tool时(有线连接),尤其是在PC端提示绿标“连接成功” 时,PC端的以太网会连接到一个”NDIS”设备,导致一些暂时的网络异常(无法访问网页)。

Interaction Toolkit

这可以被理解为使用Unity进行XR开发的核心,进行相关的配置后,几乎一切目前的VR设备可以被抽象为Unity的Device。XR Origin管理着这些设备。

在本文的项目中,直接使用了示例场景中的Introduction to VR Development.prefab预制体,其已经配置好了XR Origin,Input Action Manager InteractionManager EventSystem 等等基础的交互组件。

624b6fa1d9d4a945c593557a04b5a698.png

624b6fa1d9d4a945c593557a04b5a698.png

Interactor & Interactable

XRTK提供的主要交互方式为Interactor,可以大致分为

RayInteractor,Direct Interactor,Teleport Interactor三种,分别可以理解为射线交互,近距离的抓取抛掷交互,传送交互。其均继承自XRBaseControllerInteactor

可以被交互的物体需要挂载Interactable组件,并合理配置其Layer与Interaction Layer

Layer & Raycast Mask

即Inspector中右上角的Layer概念,可以控制对射线的遮罩。

Interaction Layer

配置Interactor可以交互的层,只有Interactable的Interaction Layer被包括在了Interactor的Interaction Layer中,Interactor才能与之交互。

例如:

CUBE 物体挂载的 Interactable组件中,勾选Interaction Layer为Deployable。并且CUBE的Layer 被囊括在Interactor中的Raycast Configuration -> Raycast Mask 中。

对应RayInteractor中的Interaction Layer囊括了此Deployable,那么CUBE可以被此Interactor交互。

e320974c3f47a8dd692c384abe24a8d8.png

e320974c3f47a8dd692c384abe24a8d8.png

498a2344f6b99fd75cf255dc921c6923.png

498a2344f6b99fd75cf255dc921c6923.png

Events

Interactor 对物体的交互可以抽象为两种类型,即Hover(悬停,待选,摸)Select(选取,拿起,抓起)。每个类型又提供了EnteredExited两个事件。

Development Practice

Assign Interaction Layer

通过代码指定Interaction Layer,在以下情况下,分别代表1 - Default 2 - Deployable

d5223dbaf91397bab58910ed077ae74e.png

d5223dbaf91397bab58910ed077ae74e.png

1
2
XRGrabInteractable xRGrabInteractable = gameObject.GetOrAddComponent<XRGrabInteractable>();
xRGrabInteractable.interactionLayers = 2;

Bind Events and Handler Methods

绑定selectEnteredselectExited等等事件,Handler方法接受对应的EventArgs,以XRInteractorHoverEnteredHandler(HoverEnterEventArgs args)为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DragManager: SingletonForMonobehaviour<DragManager>
{
//首先声明Interactor类,并在U
public XRDirectInteractor leftDirectInteractor;
public XRRayInteractor leftRayInteractor;

private void InitXRInteractor()
{
leftRayInteractor.hoverEntered.AddListener(XRInteractorHoverEnteredHandler); }
}

private void XRInteractorHoverEnteredHandler(HoverEnterEventArgs args)
{
Debug.log("[Interactor]Enter Hover"):
}

}

获取当前交互的物体

1
2
3
4
5
private void XRInteractorHoverEnteredHandler(HoverEnterEventArgs args)
{
IXRHoverInteractable hoverComponent = args.interactableObject;
GameObject obj = hoverComponent.transform.gameObject;
}

UI Elements

在UI组件上挂载TrackedDeviceGraphicRaycaster,可以使得其接受对应的Hover、Select事件

Input Event

响应对应的按键。

Base Concepts & Models

PICO Neo3为例:

43cc2a7be65a3d7abe62537ceab2e0b1.png

43cc2a7be65a3d7abe62537ceab2e0b1.png

The table below describes the mappings between PICO controller buttons and Unity keys:

Button Unity Keys
Menu CommonUsages.menuButton: represents whether the Menu button has been activated (pressed).
Trigger - CommonUsages.triggerButton: represents whether the Trigger button has been activated (pressed).
- CommonUsages.trigger: represents the degree to which the Trigger button was pressed. For example, in an archery game, it represents how full the bow has been drawn.
Grip - CommonUsages.gripButton: represents whether the Grip button has been activated (pressed).
- CommonUsages.grip: represents the degree to which the Grip button was pressed. For example, in an archery game, it represents how full the bow has been drawn.
Joystick - CommonUsages.primary2DAxisClick: represents whether the Joystick has been activated (pressed).
- CommonUsages.primary2DAxis: represents whether the Joystick has been moved upward, downward, leftward, or rightward.
X/A CommonUsages.primaryButton: represents whether the X/A button has been activated (pressed).
Y/B CommonUsages.secondaryButton: represents whether the Y/B button has been activated (pressed).

Development Practice

下面给出了代码中获取到输入、事件的用法。

也许2.2.0的VRKT提供了相应事件,可以自行搜索用法。此处给出的代码参考PicoXR中的输入事件_窗外听轩雨的博客-CSDN博客 的内容。

Get Input Device

通过XRNode获取设备

1
2
3
4
5
6
//XRNode为枚举变量
//常用的有 Head LeftHand RightHand
//根据这些枚举可以轻松获得指定的头盔,左手柄,右手柄
InputDevice headController = InputDevices.GetDeviceAtXRNode(XRNode.Head);
InputDevice leftHandController = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
InputDevice rightHandController = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);

Try Get Input Value

1
2
3
4
5
6
7
8
9
10
InputDevice device;
//省略device的获取 使用前要先获取
public void Test()
{
bool isDown; //记录是否按下
if(device.TryGetFeatureValue(CommonUsages.triggerButton,out isDown) && isDown)
{
//xxxxx 处理逻辑
}
}

InputEvent.cs

将输入转为事件,分离事件与处理逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using Common;
/// <summary>
/// 提供各种输入事件
/// </summary>
public class InputEvent:MonoSingleton<InputEvent>
{
//*************输入设别**************************
InputDevice leftHandController;
InputDevice rightHandController;
InputDevice headController;

//**************对外提供公开事件******************
#region public event

public Action onLeftTriggerEnter;
public Action onLeftTriggerDown;
public Action onLeftTriggerUp;

public Action onRightTriggerEnter;
public Action onRightTriggerDown;
public Action onRightTriggerUp;

public Action onLeftGripEnter;
public Action onLeftGripDown;
public Action onLeftGripUp;

public Action onRightGripEnter;
public Action onRightGripDown;
public Action onRightGripUp;

public Action onLeftAppButtonEnter;
public Action onLeftAppButtonDown;
public Action onLeftAppButtonUp;

public Action onRightAppButtonEnter;
public Action onRightAppButtonDown;
public Action onRightAppButtonUp;

public Action onLeftJoyStickEnter;
public Action onLeftJoyStickDown;
public Action onLeftJoyStickUp;

public Action onRightJoyStickEnter;
public Action onRightJoyStickDown;
public Action onRightJoyStickUp;

public Action<Vector2> onLeftJoyStickMove;
public Action<Vector2> onRightJoyStickMove;

public Action onLeftAXButtonEnter;
public Action onLeftAXButtonDown;
public Action onLeftAXButtonUp;

public Action onLeftBYButtonEnter;
public Action onLeftBYButtonDown;
public Action onLeftBYButonUp;

public Action onRightAXButtonEnter;
public Action onRightAXButtonDown;
public Action onRightAXButtonUp;

public Action onRightBYButtonEnter;
public Action onRightBYButtonDown;
public Action onRightBYButtonUp;

#endregion

//提供状态字典独立记录各个feature的状态
Dictionary<string, bool> stateDic;

//单例模式提供的初始化函数
protected override void Init()
{
base.Init();
leftHandController = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
rightHandController = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
headController = InputDevices.GetDeviceAtXRNode(XRNode.Head);
stateDic = new Dictionary<string, bool>();

}
//*******************事件源的触发**************************

/// <summary>
/// 按钮事件源触发模板
/// </summary>
/// <param name="device">设备</param>
/// <param name="usage">功能特征</param>
/// <param name="btnEnter">开始按下按钮事件</param>
/// <param name="btnDown">按下按钮事件</param>
/// <param name="btnUp">抬起按钮事件</param>
private void ButtonDispatchModel(InputDevice device,InputFeatureUsage<bool> usage,Action btnEnter,Action btnDown,Action btnUp)
{
Debug.Log("usage:" + usage.name);
//为首次执行的feature添加bool状态 -- 用以判断Enter和Up状态
string featureKey = device.name + usage.name;
if(!stateDic.ContainsKey(featureKey))
{
stateDic.Add(featureKey, false);
}

bool isDown;
if(device.TryGetFeatureValue(usage,out isDown) && isDown)
{
if(!stateDic[featureKey])
{
stateDic[featureKey] = true;
if(btnEnter != null)
btnEnter();
}
if(btnDown!=null)
btnDown();
}
else
{
if(stateDic[featureKey])
{
if(btnUp!=null)
btnUp();
stateDic[featureKey] = false;
}
}
}

/// <summary>
/// 摇杆事件源触发模板
/// </summary>
/// <param name="device">设备</param>
/// <param name="usage">功能特征</param>
/// <param name="joyStickMove">移动摇杆事件</param>
private void JoyStickDispatchModel(InputDevice device,InputFeatureUsage<Vector2> usage,Action<Vector2> joyStickMove)
{
Vector2 axis;
if (device.TryGetFeatureValue(usage, out axis) && !axis.Equals(Vector2.zero))
{
if(joyStickMove!=null)
joyStickMove(axis);
}
}

//******************每帧轮询监听事件***********************
private void Update()
{
ButtonDispatchModel(leftHandController, CommonUsages.triggerButton, onLeftTriggerEnter, onLeftTriggerDown, onLeftTriggerUp);
ButtonDispatchModel(rightHandController, CommonUsages.triggerButton, onRightTriggerEnter, onRightTriggerDown, onRightTriggerUp);

ButtonDispatchModel(leftHandController, CommonUsages.gripButton, onLeftGripEnter, onLeftGripDown, onLeftGripUp);
ButtonDispatchModel(rightHandController, CommonUsages.gripButton, onRightGripEnter, onRightGripDown, onRightGripUp);

ButtonDispatchModel(leftHandController, CommonUsages.primaryButton, onLeftAXButtonEnter, onLeftAXButtonDown, onLeftAXButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.primaryButton, onRightAXButtonEnter, onRightAXButtonDown, onRightAXButtonUp);

ButtonDispatchModel(leftHandController, CommonUsages.secondaryButton, onLeftBYButtonEnter, onLeftBYButtonDown, onLeftBYButonUp);
ButtonDispatchModel(rightHandController, CommonUsages.secondaryButton, onRightBYButtonEnter, onRightBYButtonDown, onRightBYButtonUp);

ButtonDispatchModel(leftHandController, CommonUsages.primary2DAxisClick, onLeftJoyStickEnter, onLeftJoyStickDown, onLeftJoyStickUp);
ButtonDispatchModel(rightHandController, CommonUsages.primary2DAxisClick, onRightJoyStickEnter, onRightJoyStickDown, onRightJoyStickUp);

ButtonDispatchModel(leftHandController, CommonUsages.menuButton, onLeftAppButtonEnter, onLeftAppButtonDown, onLeftAppButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.menuButton, onRightAppButtonEnter, onRightAppButtonDown,onRightAppButtonUp);

JoyStickDispatchModel(leftHandController, CommonUsages.primary2DAxis, onLeftJoyStickMove);
JoyStickDispatchModel(rightHandController, CommonUsages.primary2DAxis, onRightJoyStickMove);
}
}

Bind Events and Handler Methods

将对应的输入事件与处理事件想绑定,此处给出了EnterDownUp三个事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void BindXRInputEvent()
{
InputEvent.Instance.onLeftAXButtonEnter += AXButtonEnterHandler;
InputEvent.Instance.onLeftAXButtonUp += AXButtonUpHandler;

InputEvent.Instance.onLeftBYButtonEnter += BYButtonEnterHandler;
InputEvent.Instance.onLeftBYButonUp += BYButtonUpHandler;
}


private void AXButtonEnterHandler()
{
Debug.Log("[Input] LeftAXButtonEnter is called");
if (!hasStarted) return;
UIManager.Instance.OpenPanel(eUIPanelType.ChooseBluePrintPanel);
}

private void AXButtonUpHandler()
{
Debug.Log("[Input] LeftAXButtonUp is called");
if (!hasStarted) return;
UIManager.Instance.PopPanel();
}

Build

由于Neo3是安卓设备,在Unity打包时会遇到一些打安卓包的坑,主要原因是外网的一些资源不能正常访问。主要的解决办法时换源或者使用代理。

HDRP光照系统讲解

场景中的GameObejct挂载Volume组件,生成对应的Profile资源,生成的资源文件会被自动放在Scene路径下与场景名相同的文件夹中。为了使得HDRI Sky和阴影配置真正生效,需要在Light界面中将 Scene Settings Profile 这个Volume资源与之关联。

Volume

Directional Light

平行光灯的Mode为Realtime,所以它提供直接照明。只有光线照到之处才变亮。因为没有间接光照信息。ReflectionHDRP 中 的反射层级先后顺序是:先看场景中有没有Screen Space Reflection(简称SSR,屏幕空间反射),如果没有再找Reflection Probe(反射探针),如果还没有就反射天空。

Ambient Occlusion

通过明暗(光影变换)表示几何细节
c2cf9d78072781eb5db20b4055aa7d61.png
2f6fdb8fd17b1c798280345947236d70.png

环境光遮蔽针对当前镜头中全屏画面进行计算。通过计算场景中的灯光和物体周围环境,让裂缝,孔洞和物体之间交接处等区域的颜色变得更黑。在真实世界中,这些地方通常因为受到周围物体的遮蔽而变得更黑。

Anti-Aliasing

在HDRP中,我们可以使用Camera组件为画面添加抗锯齿效果。FXAA对性能的消耗少画面会变糊,其次是TAA,性能消耗最大的是SMAA(Enhanced Subpixel Morphological Antialiasing)抗锯齿效果最好的是TAA,其次是SMAA(对静态锯齿效果很好),抗锯齿效果最差的是FXAA。

Physical Light Units and Intensities

Visual Environment 组件中选择了HDRI作为天空的类型

Lux光照强度模式,此处的“光强”可以理解为整个场景的明暗,和直接光照导致的高光、暗部是有区别的。

Lux 50000:

9dee749b5fd822c3a38634ea79c95fbb.png

Lux 10000:

54bb782be1a213c6cc7a1488d263113f.png

Lux 1000 + Directional Light

b9bb9307d901fde73d660ac611b36c66.png

Lux 5000 + Directional Light + Indirect Multiplier > 1

6ac525aec2a04ab7e897678176f7415c.png

Lux光照单位表示的光照信息只有方向没有强度衰减信息,也就是说此单位适合用于表示太阳光这一类对于地球来说只有角度变化,没有强度变化的光照信息。

Light Probe

光照探针被认为是一种价廉物美,为动态物体提供间接光照信息和生成动态阴影的高性能解决方案

Bake

确保所有的静态物体被标记为Static

d196d81482cdb93b85c8ea6ef4923e58.png

对于光照烘培来说,只要确保勾选Contribute GI(贡献全局关照)和 Reflection Probe Static 选项即可。

Environment(HDRP)

可以指定所用的环境光Volume,并指定相关联的Sky作为环境光的来源。

Mixed Lighting

要想烘培全局光照,必须在这里勾选Baked Global Illumination(烘培的全局光照) 选项,然后选择光照模式: Baked Indirect 或者 Shadowmask

Baked Indirect:仅仅烘培间接光照信息

Shadowmask

  • 提供最逼真的阴影效果。不仅烘培间接光照信息,也会在Lightmap(光照贴图)中把阴影烘培进去。

  • 适用于远处存在可见物体的场景,比如在高端机器上运行的开放世界游戏。

Lightmapping Settings

Filtering

可以选择一种Denoiser(降噪算法),可以分为Direct Denoiser、Indirect Denoiser和Ambient Occlusion Denoiser选择不同的降噪算法。

一般建议是根据不同的GPU平台为这三项选择同一种降噪算法。

NVIDIA :Optix

AMD:Radeon Pro

Intel:OpenImageDenoise,基于CPU的降噪算法,可适用于所有平台。

Lightmap Padding

控制光照贴图中,按照UV分开的光照贴图区域之间的间隔太小。

如果此间隔太小,有可能造成光照贴图边缘的颜色渗透。

但是如果间隔太大,又会浪费光照贴图空间,增加贴图数量,导致光照贴图在内存中的占用增大。

Indirect Lighting Controller of Volume

间接光照信息被烘培进光照贴图中后,可以使用此Override增强整个场景的间接光照强度。

Indirect Diffuse Intensity 间接光漫反射强度

HDRP将会由烘培获得的光照贴图和光照探针上的数据乘以这个值。‘

如果值为0,那么

IDI = 4 ISI = 1551d8ea8b610f375ac4db13b9ce24126.pngValue = 16 ISI = 1ee8062c2fc3e5aec87042648581efc43.png

Indirect Specular Intensity 间接光高光强度

HDRP会将任何类型(Baked、Custom或者Realtime)的反射探针(Reflection Probe)上的数据乘以这个值。

IDI = 16 ISI = 1(same as above one)ee8062c2fc3e5aec87042648581efc43.png

IDI  =16 ISI = 104316863f946868cdff2b03003ddc6ce9.png

IDI = 0 ISI = 0

该画面中没有任何来自间接光照的漫反射和高光,没有被平行光找到的地方漆黑一片。9199da6a2fe393974a58302059e378af.png

IDI = 0 ISI = 1

该画面中只有来自间接光照的高光,没有漫反射。

d6c0d281c4393b981101c601e0de04c9.png

IDI = 1 ISI = 0

该画面中只有来自间接光照的漫反射,没有高光。

047cd7d712df383557a4a5cde4f694bf.png

IDI = 1 ISI = 1

该画面中只有来自间接光照的漫反射,也有高光。

b454a52aa49bac9b8c93ba787bda6005.png

处理阴影与环境光遮蔽

Directional Light

Volume 框架

Volume 框架是HDRP最重要的组成部分之一,其作用如下:

  • 为场景设置来自天空盒的环境光照

  • 设置各种阴影效果,包括场景中的Contact Shadow 和 Micro Shadow

  • 设置场景中的雾效

  • 设置基于屏幕空间的反射(SSRelection)和折射(SSRefraction)、环境光遮蔽(Ambient Occlusion)

  • 设置后处理效果

  • 设置实时光线追踪

在同一个场景中可以添加多个带Volume组件的GameObject。每个Volume的模式可以按照需要设置成Global 或是 Local

Global意味着这个Volumn针对整个场景生效。不管相机朝向哪里,Global模式的Volumn都会被计算在内。

Local模式的Volumn则只能在它的碰撞体作用区域内生效。一般Box Collider即可满足碰撞体需要。

Exposure

曝光指的是相机感光元器件接受的光线强度。曝光的数值越高,相机接受的光线强度越大。

参数说明

Exposure重载包括四种模式:Fixed、Automatic、Curve Mapping 和 Use Physical Camera

Fixed 固定曝光

用于曝光变化不大的场景,也就是场景中的各个区域有类似的曝光强度。

可以使用多个被设置为Local模式的Volume,通过Blend Distance(混合距离)来融合切换不同的Volume的曝光。

由于HDRP的光照系统是完全基于物理的,所以可以参考物理世界真实的曝光值。

5cfd281ebd4bad3543fc5a737f12e351.png

Automatic 自动曝光

根据当前相机的位置信息和相关场景信息来自动调节曝光。非常适用于曝光随位置不同而发生变化的场景,例如从光照强烈的户外进入阴暗的室内或者从山洞中移动到山洞外

可以模拟现实中人眼适应不同曝光的情况。

Limit Min & Limit Max

分别用于控制场景自动曝光的最小值和最大值。可以用这两个值分别调节当相机处于场景的暗部和亮部时整体的曝光。

99b37196985c18f0e7285b3578469b4e.png

当画面靠近场景中的亮部,周围的环境会变暗,但是环境也许不应该“那么”暗。此时可以将Limit Max 调低,以提升整个画面的曝光。f688c683062bbe0c79c0bb2e09b5814d.png

当画面处于场景的暗部,比如在黑暗的角落朝外看,那么外部环境会变亮,可能导致画面亮部曝光过度(月光即使很亮也不应该像白天)此时可以将LImit Min 调高,降低整个画面的曝光。

Curve Mapping 曲线控制曝光

X轴代表目前场景的曝光

Y轴代表我们想要的曝光

专家使用。

Use Physical Camera 物理相机控制曝光

通过物理相机的Iso、Shutter Speed 和 Aperture 参数来控制曝光。

Volume 界面中只剩下Compensation(补偿)这一项。

Metering Mode

用于控制如何采样用于曝光的计算场景中的灯光。

  1. Average 针对整个画面进行采样,结果稳定

  2. Post 针对画面中央区域采样。如果画面中央的采样区域较暗,整个画面可能曝光过度。

  3. Center Weighted

Visual Environment