跳转到内容

昼夜

一天就是太阳走过的一条路径,以及灯光、天空和雾因为它在哪里而成为的样子。TimeOfDay 用四行就能写下的天文公式放置太阳—— 时角、纬度、季节——所以三个人人能懂的数,就能得到正确的一天的形状(夏天又长又高的弧线、冬天又短又低的弧线、日出偏东)。 其余的和 天气 是同一个思路:场景已有设定的倍率,同一个钟点适配任何项目。

给某个实体加一个 TimeOfDay 组件。

字段 作用
hour 钟点,0–24(12 是正午)。游戏运行时自行推进;在编辑器里设置它可以预览某个钟点。
daySeconds 游戏运行时一整天持续多少秒。0 表示钟点停住。
latitude 在星球上的位置,单位度(正为北):正午的太阳升多高。
declination 季节,−23.5 到 23.5:正值是北半球的夏天。
north 世界相对罗盘正北(+Z)朝 +X 转了多少度——太阳从哪边升起。
drive 时间驱动什么:灯光、天空、雾、曝光、反射。

选中该实体,Details 面板显示的是一天,而不是一串数字:24 小时里太阳的高度,按这个组件自己的纬度和赤纬画出(冬天是低而短的弧,极地夏天太阳不落),还有一根可以拖动来设置时刻的指针——也可以点一下表盘用方向键调整,每次 15 分钟,按住 Shift 为一小时。旁边是该时刻太阳的高度、日照和主光的缩放,下面是它驱动的每一项的开关。你设置的时刻就是编辑器预览的时刻,只有运行时才会推进。

  • 主光。 场景里的方向光(跟随全景图太阳的、投射阴影的,否则取第一盏)对准太阳,并按白天的曲线缩放:贴近地平线时偏暖偏弱, 落到地平线以下一点就熄灭。太阳落下后它对准月亮,强度是太阳的几百分之一,带蓝色,所以夜里的阴影背着月亮倒下。 交接发生在两者都很暗的地方,不会突变。
  • 环境光被缩放并染色——夜里偏蓝、黄昏偏暖——烘焙反射探针被调暗并褪色,因为它们是在白天的天空下烘焙的。
  • 天空。 Sky Atmosphere 特效让太阳光穿过一颗行星的大气层发生散射——瑞利散射给出空气的蓝, 米氏散射给出雾霾和太阳周围的眩光——所以正午的蓝、日落的红和日落之后的余晖出自同一个计算。它画出太阳(头顶是白的、贴着地平线是红的)、月亮、 星星,以及被太阳照亮、被风吹动的体积云。它的太阳取自时钟,所以不会把月光下的主光当成太阳。
  • 雾。 Height Fog 的颜色随白天而被照亮(夜里暗、黄昏偏橙),辉光跟随主光。
  • 曝光。 夜里 Auto Exposure 的补偿被抬高,让画面保持可读(没有它的场景改加在 Colour Grade 上)。

在编辑器里场景的灯光保持不动——和天气一样,预览一个钟点永远不会改动你要保存的场景——所以跟随太阳的特效(天空、雾)改为被告知 时钟的太阳在哪里。灯光在游戏运行时才会移动。

两者缩放的是同一批灯,各自从作者设定的值出发,所以可以叠加:雨中的黄昏就是黄昏的颜色乘上雨的强度。天气还会把天空压灰 (overcast),天阴下来时太阳、月亮和星星随之隐去。

import { timeOfDayAt, currentTimeOfDay, TimeOfDay } from 'esengine';
// 北纬 51 度、仲夏、18:30 时太阳在哪里。
const evening = timeOfDayAt(18.5, 51, 23);
evening.sunDir; // 指向太阳的单位向量
evening.daylight; // 夜里 0,白天 1
// 设置钟点,剩下的交给系统。
world.insert(clockEntity, TimeOfDay, { hour: 6, daySeconds: 600 });

烘焙光照和烘焙反射探针只对应某一个时刻:烘焙的是白天的天空,所以不来自灯的那部分光照(光照贴图、探针体)不会随太阳移动。 要走完一整天的场景,应让太阳和天空是实时的——由灯光照亮的场景,或者烘焙内容主要是天空柔光的场景——上面的调暗与染色会做其余的事。 窗户和路灯不会在夜里自己亮起来:给它们自发光材质,或一盏用 currentTimeOfDay().daylight 来开关的灯。