点亮 WebXR 场景
因为 WebXR Device API 依赖于其他技术(即 WebGL 和基于它的框架)来执行场景的所有渲染、纹理和照明,所以相同的通用照明概念适用于 WebXR 设置或场景,就像适用于任何其他 WebGL 生成的显示一样。
然而,在创建照明代码时,需要牢记一些问题和细节,尤其是对于增强现实 (AR) 应用程序。本指南讨论这些主题。虽然本文简要提醒了照明的一般工作原理,但它绝不是照明教程,也不是关于如何创建正确照明的 3D 场景的指南。
回顾:3D 中的模拟照明
尽管本文不是 3D 场景照明的综合指南,但简要提醒照明的一般工作原理是很有用的。从根本上说,在虚拟场景中模拟照明涉及计算来自每个光源的光线在与场景中每个对象交互并反射后,眼睛接收到的光量。
光的反射
图:显示反射角如何与入射角对应的图表。
我们所看到的每一个物体,我们之所以能看到,是因为物体要么发光,要么反射光(或两者兼而有之)。入射光线(入射光线)以一个称为入射角的角度到达。入射角 Θᵢ 是入射光线与表面法向量之间的夹角。
对于粗糙表面,光线向各个方向均匀反射。然而,光滑、镜面般的表面将大部分光线反射到其反射角 Θᵣ 等于入射角的方向,只是它在法向量的另一侧。那么,反射光线就会沿着法向量离开。这就是反射定律。这是场景着色所涉及的大部分内容的基础,并影响不同类型光源的行为方式。
当然,反射光线的颜色可能会因光线与表面的相互作用而改变强度和/或色调,但角度始终相同。
光源的组成部分
光源有三个主要组成部分;每个组成部分本质上是一种类型的光
有三种光可以影响对象及其像素在查看者屏幕或头戴设备上显示的颜色和亮度。
环境光
环境光是不来自特定光源的光,而是遍布整个场景的光。这种光以相同的强度从各个方向到达场景中的每个表面,然后向各个方向均匀反射。因此,环境光施加的效果在整个场景中普遍相同。
图:一个只有环境光照的球体。请注意,完全没有阴影来指示球体的深度。 
环境光的效果是通过将光源强度乘以像素位置处表面的反射率来计算的。场景中每个像素的颜色和强度都以完全相同的方式受到影响,无论它在场景中的位置或朝向。环境光通常存在以防止阴影区域变得太暗,尽管它会影响整个场景;但是,场景中的环境光量应该非常低。
由于光的反弹和散射在实时计算中可能非常昂贵,尤其是涉及多个光源时,通常使用环境光来模拟场景中所有其他光源引起的散射光,而不是实际计算光散射的真实效果。然而,在这样做时,需要注意尽量使环境光与场景照明的真实效果相匹配。
环境光还可以用于为场景应用颜色着色;例如,在一个玩家戴着一副黄色眼镜的游戏中,你可以添加黄色环境光。
漫射光
漫射光是均匀地、有方向地从表面发出或反射的光。这是我们通常看到的大部分光线。漫射光来自特定的位置或方向并投射阴影。由于其方向性,面向漫射光源的对象表面将比其他表面更亮。
图:土星的第五大卫星土卫五,沐浴在阳光中,阳光来自左下方。 
由于漫射光的强度取决于入射角(表示光线从哪个方向到达表面的向量与表面法向量或垂直于表面的向量之间的夹角),因此物体反射光的强度或亮度会根据表面相对于光源的方向而变化。
镜面光
镜面光是构成反光物体上高光的光,例如宝石、眼睛、闪亮的杯子和盘子等。镜面光往往以亮点或方块的形式出现在光源最直接照射到表面的位置。
图:NASA 的卡西尼号飞船拍摄的照片,显示土星卫星土卫六表面液态甲烷湖的镜面反射光。 
每个光源都由环境光、漫射光和/或镜面光的某种组合表示。WebGL 着色器程序获取每个光源的颜色、方向性、亮度和其它因素,并计算每个像素的最终颜色。
光源类型
有四种基本光源类型。每一种都涉及一个虚拟光源,其与所绘制对象的距离以及光波的方向性导致光源呈现出特定的特征。在大多数情况下,任何真实世界的光源都可以使用这些光源类型中的一种或多种来模拟。
环境光源
环境光源是描述场景中环境光强度和颜色的光源。虽然场景中可能存在多个这样的光源,但您可以通过将它们组合成一个来稍微提高性能,因为每个光源无论如何都会均匀地影响每个像素。
环境光源通常不对应场景中的任何物体,也没有真实世界的对应物。
定向光源
定向光源是一种来自特定方向但并非来自特定光源的光源,因此其发射的光线彼此平行。此外,光的强度不随距离变化。这意味着定向光投射的阴影非常锐利,光照和阴影之间几乎是瞬时过渡。
图:地球和月球都被太阳的定向光照亮一半。 
定向光最常见的例子是太阳。虽然太阳实际上是一个单一的(大)物体,但它距离非常遥远,所以从它发出的光线基本上是平行的。虽然阳光的强度确实会随距离衰减,但变化率非常低,只有在很远的距离才能察觉到,因此阳光强度变化率通常对渲染 3D 场景无关紧要。
点光源
点光源是位于特定位置、向各个方向均匀辐射的光源。灯泡、蜡烛等都是点光源的例子。物体离点光源越近,它投射到该物体上的光就越亮。点光源亮度衰减的速率称为衰减,是 WebGL 和其他照明系统中光源的可配置特性。
在反射定律和光线亮度随距离减小的作用下,点光源发出并反射回来的光线往往在离光源最近的点最亮,距离越远越暗。即使表面是平坦的,离光源最近的点是中心,随着偏离法线的角度变化,光线会越来越长。
聚光灯光源
聚光灯光源(或聚光灯)是一种位于特定位置的光源,以其方向向量的方向发出一个光锥。锥形衰减参数定义了光锥边缘光的亮度衰减速度,并且,与点光源一样,衰减参数控制光线随距离的衰减。
图:夜间聚光灯照射在灰泥墙上的照片。 
在光锥的边缘,光线完全不再影响表面。
光照的计算成本
要可见,场景必须包含某种光照,因此所有或几乎所有场景都至少会有一个光源,并且可能有很多光源。每个光源都会大幅增加计算每个显示像素的最终颜色和亮度所需的计算量。对这些光源类型中的每一种执行着色比前一种更具计算密集性;因此,环境光是最便宜的,其次是定向光源、点光源,最后是聚光灯。
此外,光照设计得越精确,计算成本就越高。增加阴影细节、体积光(即,在空气中可以看到的光,例如阳光或天空中聚光灯的光束)以及其他光照效果可以为您的场景增加真实感和美感,但需要注意确保场景不会使 GPU 不堪重负。
计算受光像素的颜色
虽然有些图形库支持光源对象并自动为您计算和应用光照效果,但 WebGL 不支持。幸运的是,在您自己的顶点和片段着色器中应用光照并不太困难。
对于场景中的每个多边形,顶点着色器程序确定顶点颜色,然后片段着色器通过将分配纹理中相应的纹素、任何颜色着色或效果以及其他视觉数据组合起来,生成多边形中的每个像素。此时,场景的光照被考虑并适当地应用于像素,然后像素被存储到帧缓冲区中。
最终渲染场景中每个像素的颜色是使用一些复杂的数学计算的,这些数学考虑了以下因素:
- 与屏幕像素对应的纹理元素(映射到对象上的纹理中的像素;也称为纹素)的颜色,考虑到对象几何形状、查看者相对于每个多边形的位置和方向等。
- 观看者位置和距离。
- 表面材料和反射率。
- 目标位置表面凹凸性。
- 场景中每个光源的位置、颜色、方向和亮度。
- 场景中任何环境光的颜色和亮度;这种光均匀地应用于整个场景,没有光源,因此没有阴影或亮度变化。
- 场景中其他表面反射光线的效果;反射光的颜色、方向和亮度将影响光线接触到的像素的颜色。
您可以在WebGL 中的光照文章中了解更多关于如何在 WebGL 中执行光照的信息。
混合现实内容的照明问题
除了在照亮场景时需要处理的常见问题之外,VR 或 AR 用例在编写着色器时还会增加额外的关注领域。在本节中,我们提供了一些基本的混合现实照明指南,供您在构建、渲染和照亮场景时考虑。虽然其中一些在任何其他 3D 环境中也很有用,但大多数是虚拟现实特有的,在某些情况下甚至更是增强现实特有的。
由于您的场景旨在代表一个人或其化身可以存在的环境,因此您应该尝试在光源的定位和呈现方面实现一定程度的一致性和真实感。显然,此指南也有例外情况,例如当您的场景代表超凡脱俗或外星环境时,或者当您的目标是创建令人不安的视觉效果时。
光源放置的真实感
在可能的情况下,您应该尝试使您的虚拟光源与实际存在的物体相对应。如果您的虚拟房间需要头顶照明,请在光源位置放置一个吸顶灯模型。也有例外,例如为您的设置添加基本照明量的环境光,以及太阳,它是一种定向光(即,光线都是平行且从天空中某个地方发出并最终到达场景中某个地方的光源)。
尝试将光源放置在与您想要创建的场景和情绪相符的逼真位置。一个旨在营造自然光照、真实世界感觉的场景不应有演播室灯光。它有阳光,可能还有场景中物体或水反射的光线,等等,但不是直接照射到场景中物体或人物面部的灯光。
玩家与光线互动的真实感
如果您的光源位于场景中,您可能应该尝试确保观看者的化身不能与光源物理相交。结果可能会很奇怪。
如果观看者的化身被设计为具有实体形态,即使观看者永远无法看到它,也应该有一个模型,以便光线与化身正确互动。最低限度,这意味着化身应该投射适当的阴影;但是,根据诸如化身是否可见以及其模型的材料、纹理和其他属性(尤其是其反射率)等因素,化身可能还需要反射光线,并可能影响反射光的颜色。
增强现实中的真实感
增强现实为您的对象照明引入了额外的复杂性,因为您的虚拟对象需要存在于一个拥有自己光源的物理世界中。因此,您应该尽可能将您的照明与真实世界的物理光源匹配。这可以通过一种称为照明估算的技术来完成。
反过来说,您应该尽量避免虚拟物体本身成为光源,除非您准备好创建可以将光线投射到真实世界环境中的代码。将光线投射到真实世界的物体上本质上与投射阴影相反。这可以做到,但尚未广泛实现。
照明估算
照明估算是增强现实平台使用的一种技术,旨在将场景中虚拟物体的照明与观察者周围真实世界的照明相匹配。这涉及收集可能来自各种传感器(包括加速度计和指南针,如果可用)、摄像头以及其他可能来源的数据。其他数据通过 Geolocation API 收集,然后所有这些数据通过算法和机器学习引擎生成估算的照明信息。
目前,WebXR 不支持照明估算。然而,W3C 正在起草一份规范。您可以在规范的 GitHub 仓库中包含的解释性文档中了解有关提议的 API 以及照明估算概念的很多信息。
本质上,照明估算收集有关光源以及场景中对象的形状和方向的信息,以及有关它们所制成的材料的信息,然后返回可用于创建与真实世界照明近似匹配的虚拟光源对象的数据。
照明估算具体如何工作,尤其是在拟议的 API 上下文中,目前超出了讨论范围。一旦 API 稳定,我们将更新此文档以包含详细信息。
安全和隐私问题
为了使用真实世界数据生成和应用照明到虚拟对象,收集所有这些数据会带来许多潜在的安全问题。
当然,许多 AR 应用都非常清楚用户所在的位置。如果用户正在运行一个名为《卢浮宫之旅》的应用,那么用户很可能位于法国巴黎的卢浮宫博物馆。但浏览器被要求采取一系列措施,在未经用户同意的情况下,使其难以物理定位用户。
环境光传感器 API
使用 环境光传感器 API 收集光照数据会带来各种潜在的隐私问题。
- 光照信息可能会泄露用户周围环境和设备使用模式的网站信息。此类信息可用于增强用户画像和行为分析数据。
- 如果两个或更多设备访问使用相同第三方脚本的内容,该脚本可用于关联光照信息及其随时间的变化,以尝试确定设备之间的空间关系;例如,这理论上可以表明这些设备位于同一大致区域。
浏览器如何缓解这些问题
为了帮助缓解这些风险,WebXR 照明估算 API 规范要求浏览器报告的照明信息与真实值有所出入。有许多方法可以做到这一点。
球谐函数精度
浏览器可以通过降低球谐函数的精度来降低指纹识别的风险。在执行实时渲染时(虚拟或增强现实应用程序都是如此),球谐函数照明用于简化和加速生成高度逼真的阴影和着色过程。通过改变这些函数的准确性,浏览器使数据的一致性降低,并且重要的是,即使在相同的设置下,两台计算机生成的数据也会有所不同。
将方向与照明解耦
在利用地理定位来确定方向和潜在位置信息的 AR 应用中,避免让该信息直接与照明状态相关联是浏览器保护用户免受指纹识别攻击的另一种方式。通过确保指南针方向和光线方向在用户附近(或声称附近)的每个设备上都不相同,可以消除根据周围照明状态找到用户的能力。
当浏览器提供关于一个非常明亮的定向光源的详细信息时,该光源很可能代表太阳。这个明亮光源的方向性与一天中的时间结合起来,可以用来确定用户的地理位置,而无需涉及地理定位 API。通过确保 AR 场景的坐标不与指南针坐标对齐,并通过降低太阳光角度的精度,就无法再使用这种技术准确估算位置。
时间和空间滤波
考虑一种攻击,它使用建筑物的自动化照明系统以已知模式快速开关灯。如果没有适当的预防措施,照明估算数据可能被用来检测这种模式,从而确定用户位于特定位置。这可以通过远程完成,或者由位于同一房间但想确定另一个人是否也在同一房间的攻击者执行。
照明估算可在未经许可的情况下获取用户信息的另一个场景:如果光传感器足够靠近用户显示器,可以检测到由显示器内容引起的光照变化,则可以使用算法来确定用户是否正在观看特定视频——甚至可能识别用户正在观看的多个视频中的哪一个。
照明估算 API 规范规定所有用户代理都必须执行时间滤波和空间滤波,以模糊数据,从而降低其用于定位用户或执行旁道攻击的实用性。