场景节点图与蓝图

十六名骑士围成一圈站在雕像周围,每人都用剑指着它——这不是手工放置十六次导入,而是一个节点图:一次导入、一圈副本、一个朝向目标,再加一点变化,让任意两个都不完全相同。把十六改成二十四,只需改一个数字。

新项目的场景节点图:「导入」节点连接到「输出」,左侧是节点库

场景步骤的 场景节点图面板中就是这张节点图。它与风格步骤的风格节点图是同一种画布——方框、连线、设置写在节点内——但连线类型不同,因此外观节点不能放进来,反之亦然。注释可以在上面添加注释,与风格节点图中一样:在节点后面的方框里写下你的文字,移动方框时里面的节点也随之移动。

从未打开它、也没有向场景添加任何东西的项目,不保存任何节点图,只渲染它导入的内容。

你添加到场景中的一切都是这个节点图中的节点——另一个模型(物品)、骨骼上的道具、弹簧、形状、图像、文字、体积、灯、效果,以及世界的地形、草、水和天空(D49)。大纲中的 + 会在链的末尾添加节点;大纲列出节点图中的对象,行与其节点是同一个东西:选中任一个就会选中另一个,检查器显示它的全部控件,删除任一个就会同时删除两者。从节点图自己的「添加」菜单添加的节点,也会立即成为大纲中的一行。组内的对象(套件中的灯笼)列在该组的行下;选中它会就地展开该组并选中其中的节点。

这些节点的每项设置都是一个属性(参见节点):左侧带端口的一行,可以在节点上设置,也可以从值连线。把一个数值同时接入「环」和「行」的数量,两者就会一起增长。世界的节点(地形、水、天空、云、风…)也有属性;不是单个数值或词的内容——一块地是否有自己的颜色、风还吹动什么、时段——仍是节点上的控件。

节点

节点 作用
导入 项目导入的内容及其位置:模型及其道具。
物体 模型及其道具,以及它的位置。没有被任何节点引用的内容不会被渲染。
物品 导入模型旁边的另一个模型,来自它自己的文件:站立位置、转向和缩放,以及它自己的动画片段。添加方式与形状相同:把列表传下去,并添加一个自己的副本(--item)。
道具 导入模型的某根骨骼所持的模型——一把剑、一支火把——带有它在骨骼上的位置、转向和缩放,以及骨骼握住的节点(--add-prop)。它是模型的一部分:导入的每个副本都带着它,因此它不会自己添加副本,而是把列表原样传下去。
弹簧 一根骨骼及其下属的一切随运动摆动——头发、披风、尾巴——带有刚度、阻尼和重力(--spring)。与道具一样,它是模型的一部分,存在于模型的每个物体中。
形状 Pixor 自带的形状之一——盒子、圆柱、字母——位置、尺寸、转角、颜色和倒角都在节点上设置。它把列表传下去,并加入一个自身的物体;没有任何连接时,它会开始一个列表,所以形状连入环形就是由该形状组成的一圈。
图像、文字 平面上的一张图像,或卡片上的像素字体文字(图像与文字),添加方式与形状相同。
体积 烟、火或液体(体积):到达输出的节点中每个物体一个,由它放置。
灯 有位置的光源:每个到达「输出」的物体各一盏,所以灯之后接一个「行」就是一排灯。没有接入任何节点的灯不会照亮任何东西。
效果 粒子——爆炸、火花、雨、萤火虫——每个到达「输出」的物体对应一个。
环 围成一圈的物体:数量、半径、第一个的位置,以及是否朝向中心。
行 排成一行的物体,间隔一步。
网格 排成网格的物体,横向若干个,纵向若干个。
散布 在一个盒子内带种子地散布物体,并设定任意两个物体之间的最小距离。
看向 让每个物体朝向一个点。再转一些可让武器不在前向轴上的模型对准目标。
变化 根据每个物体的编号和种子,给它各自的片段相位、转向和尺寸。
播放 这些物体播放的片段,与精灵表其余部分播放的内容无关:比如正在攻击的骑士旁边一棵摇曳的树。它们在每个动作中都播放这个片段,覆盖该动作的所有帧——静止房屋旁转动的风车,是在动作步骤中新建一个动作,再给风车加一个「播放」节点。动作在那里制作,而不是这里。
可见于 只有某一台摄像机能看到的物体,以及帧(见下文)。
合并 两组物体合为一组。
移动、镜像 平移、旋转和缩放每个物体;添加每个物体关于某个平面的镜像。
细分 把每个物体的每个三角形细分为四个,一次或最多四次,让「置换」有可移动的点。
置换 用带种子的噪声把每个物体的表面向内或向外推,最大幅度以米为单位,起伏按尺寸间隔分布。每个副本都有自己的起伏,所以一排盒子就是一排各不相同的岩石。
高度场 用从上方看到的带种子高度场把每个物体的表面垂直抬升:在细分之后,平面会变成起伏的地面。地面只会升高。
倒角 以 45 度切掉每个物体盒子的边缘。
落到地面 把每个物体垂直落到其下方最高的表面上:场景中的其他物体(或其中之一),以及链中位于它之前的地形,可设置上方抬升,并按你要求的程度顺着坡度倾斜。
在表面上 把传入的物体按面积和种子散布到某个物体的表面上——只在顶面,或所有地方。
在点上 在物体的每个顶点上放置一个所接收的物体。
差值 第一个输入中的物体,在原地挖去第二个输入中的物体:墙上的窗洞、木箱上的缺口。第二个输入只提供洞的形状,不会被渲染。
连接 把两个物体列表作为一个部件绘制,因此接缝处不画线:一摞方块看起来就是一座建筑。
设置形状 把每个物体形状的一项设置——尺寸(倍数)、宽度、高度、深度、分段、倒角或色相——设为某个属性乘以一个数再加上一个数。
属性 给每个物体写入一个数值,供后面的节点使用(见下文)。
选择 保留属性通过测试的物体。
输出 站在摄像机前的内容:节点图唯一的结果。

世界的节点——地形、小枝、植被、水、云、天空、风、光束、时段和区块——都在同一个「添加」菜单中;参见世界。数值节点(数字、开关、角度、文字以及它们之间的运算)为属性提供输入。

布局节点会对接收到的每个副本各运行一次。由行组成的环,就是环的每个副本各有一行。嵌套就是这么回事——没有第二条规则。

变化只来自副本的编号和节点图的种子,绝不来自别处,因此同一个节点图每次都会得到同样的人群,无论在哪台机器上、用哪种后端。

十六名骑士

添加环,把数量设为 16、半径设为 3,关闭面向中心,然后添加看向,把朝向设为雕像。添加变化并给片段相位设为 1,让它们都处于行走的不同阶段。画布底部的数字显示节点图生成了多少个副本,旁边的精灵会显示它们。

它们旁边的雕像是一个物品节点:另一个模型,来自它的文件,立在放置的位置。和所有实体节点一样,它会传递收到的内容,并添加一个自身的副本,因此它在链中的位置决定了什么会重复它:位于环之后的物品是十六个骑士中的一座雕像,位于环之前则会变成十六座雕像。在应用中,大纲的 + › 一个物品 › 从模型文件…会把它放在链的末端,即环之后。

在脚本中,一次一个节点:

pxr project scene game.pixor ring --count 16 --radius 3 --face-out
pxr project scene game.pixor look-at --at 0,0.8,0
pxr project scene game.pixor vary --phase 1 --seed 3
pxr project scene game.pixor --set ring.count=24
pxr project scene game.pixor            # prints the graph and the count

pxr graph add 和 pxr project scene 把节点放在链的末端。自己生成副本、不接收输入的节点(物体、导入)会代替光秃秃的导入成为链的起点,在其他节点之后添加的则放在链旁边,与链合并,因此已经渲染的内容不会丢失。--alone 添加一个不连接任何东西的节点,--in 则把另一个节点接入它的某个输入。

pxr graph add game.pixor --which scene --node object --set of=model           # the knight alone
pxr graph add game.pixor --which scene --node ring --set count=16 --set radius=3
pxr project set game.pixor --item statue.glb@0,0                            # an Item after the ring

一个单独放置、不连接任何东西的物品(--alone)会开始一个自己的列表,由「合并」「散布」或「可见于」接收。物体只指定模型:物品、形状和图像各自都是独立的节点,用「物体」指定它们会报错。

每台摄像机看到的内容

摄像机会框住节点图放在它面前的一切。可见于为一台摄像机提供其他摄像机都看不到的副本,因此一个有主摄像机和肖像摄像机的项目可以在一张精灵表中显示整个环,在另一张中显示雕像:把单独立着的雕像的「物品」通过一个「可见于」portrait,再用「合并」与其余部分连接。肖像摄像机框住雕像以及所有摄像机都能看到的内容;主摄像机则根本看不到雕像。

pxr project new game.pixor --model knight.glb --force
pxr project scene game.pixor ring --count 16 --radius 3             # node 2
pxr project set game.pixor --add-camera portrait:three-quarter:64   # NAME:VIEW:SIZE[:ANGLE]
pxr graph add game.pixor --which scene --node item --set model=statue.glb --alone                   # node 3
pxr graph add game.pixor --which scene --node seen-by --set camera=portrait --alone --in objects=3   # node 4
pxr graph add game.pixor --which scene --node merge --in b=4        # the ring, and the statue for the portrait
pxr project scene game.pixor                                        # 17 objects

这里雕像的物品节点是用 --alone 添加的,所以它不在环形链的末尾(pxr graph list 会打印编号);合并节点放在链的末尾,所以环形进入它的第一个输入。pxr render 写出 game.png(只有环形)和 game_portrait.png(环绕雕像的环形)。

作为单个节点使用的节点图

围绕雕像的一圈骑士、一排市场摊位、带变化的人群:场景节点图可以保存为一个组,在另一个项目中使用。在没有选中组时,工具栏上的保存套件…会把整个节点图写成一个套件(将场景节点图保存为套件;它的「输出」节点成为组的输出);选中组时则写出该组(将组保存为套件);画布添加菜单中的使用套件…(将套件添加为组)会把一个套件作为组放入。它是一个方框,以自己的名称命名,点击它上面的箭头(或双击)会就地展开,查看或修改它由什么组成;再点箭头即可折叠。其中的输入节点代表一个输入,因此保存的环可以接收要排成环的副本。

pxr graph save market.pixor --which scene -o stalls.pixorkit --name "Row of stalls"
pxr graph add town.pixor --which scene --kit stalls.pixorkit
pxr graph flatten town.pixor --which scene          # the nodes it stands for

组是数据,不是代码:它只能做其内部节点能做的事,渲染结果也与这些节点完全相同。

一次渲染生成多张精灵表

写出什么不归这张节点图管。链条末端的输出节点就是站在摄像机前的内容,而且只有这一个;第二个文件——一套图标、一张宣传图、一张只有线条的精灵表——是资产节点图的输出,它取用这张节点图的副本被渲染成的帧。

由多个模型组成的动画场景

物品也可以使用片段,以及一个用于推移片段的相位:

pxr project new patrol.pixor \
  --item knight.glb@-0.9,0.2,-20:walk \
  --item knight.glb@0.9,0.2,-20:walk,0.45 \
  --item chest.glb@-1.8,-1.6,25,1.2

带片段的物品会带来它的绑定并播放片段;不带片段的则在原地烘焙,开销更小。片段来自模型自身的文件,或来自与它同级的 name@clip 文件,与 Pixor 中其他地方一样。

放置、变形和选择

场景节点图的节点不只是用来排列。移动会平移、旋转和缩放每个副本;镜像会添加每个副本关于某个平面的镜像。

属性给每个副本写入一个数值——它在列表中的编号、所在位置、旋转方向、离中心的距离,或带种子的噪波——选择则保留属性通过测试的副本,去掉其余的。两者结合,就能表达只取后排的那些,并缩小一半,而无需指明后排是哪些(z 朝向摄像机,所以后排是数值较小的一端):

pxr project new town.pixor --model chest.glb --force
pxr project scene town.pixor row --count 6 --step 1,0,0.5
pxr project scene town.pixor attribute --name depth --from z
pxr project scene town.pixor select --name depth --how below --at 1
pxr project scene town.pixor move --size 0.5

细分和置换会重塑副本自身的表面:盒子变成岩石,光滑的悬崖变得粗糙。它们是副本携带并用于构建部件的设置,而不是手工编辑的三角形,因此同一个节点图在任何地方都会得到相同的岩石:

pxr project new yard.pixor --add-shape rock=box:0.5 --size 64
pxr project scene yard.pixor row --count 3 --step 1.4,0,0
pxr project scene yard.pixor subdivide --times 3
pxr project scene yard.pixor displace --amount 0.12 --bumps 0.35 --seed 4

落到地面、在表面上和在点上会读取节点图摆放物体的场景。当盒子中的散布后面接一个「落到地面」、且地形在其列表中时,盒子中的散布就变成了山坡上的散布:

pxr project new hill.pixor --model chest.glb --terrain 12:24:2                       # the terrain is node 2
pxr graph add hill.pixor --which scene --node object --set of=model --alone          # node 3
pxr graph add hill.pixor --which scene --node scatter --alone --in objects=3 --set count=14
pxr graph add hill.pixor --which scene --node merge --in b=4
pxr graph add hill.pixor --which scene --node drop-to-ground

而用蜡烛的「形状」节点加上皇冠的在点上,就能让蜡烛立在皇冠的每个角上。画布会在构建任何东西之前统计「在表面上」的副本数量;「在点上」的数量则要等场景构建好才能得知。

差值用第二个输入的物体在其所在位置进行切割:一个小方块排成三个一行,穿过一面长墙,就是一面有三扇窗的墙。洞的内壁使用该部件大部分区域的颜色。绑定到骨架的部件(蒙皮模型、骨骼上的形状或带关键帧的形状)不会被切割,因为切割会改变绑定所用的顶点;超过 5,000 个三角形的部件也不会。

pxr project new house.pixor --add-shape wall=box:4,2,0.2 --size 96      # the wall, node 2
pxr graph add house.pixor --which scene --node shape --set name=window --set size=0.4,0.5,0.4 --set at=-1.2,0.1,0 --alone   # node 3
pxr graph add house.pixor --which scene --node row --alone --in objects=3 --set count=3 --set step=1.2,0,0                 # node 4
pxr graph add house.pixor --which scene --node difference --in "cut by=4"
pxr render house.pixor -o out/house.png

形状以其 at 为中心放置,因此第一扇窗位于墙中间左侧 1.2 m 处,「行」把另外两扇放在相距 1.2 m 的位置,三扇都在 4 m 宽的墙内。

Pixor 构建的形状有自己的设置,设置形状可以从节点图中驱动它们:一排木箱,每个都比前一个高一些,在色轮上也再转过一点——这就是木箱的「形状」节点、一个「行」、一个表示每个副本在行中位置的「属性」,以及两个「设置形状」。

pxr project new crates.pixor --add-shape 'crate=box:0.5:0,0,0:#c86432' --size 64   # an orange crate alone
pxr project scene crates.pixor row --count 5 --step 1.1,0,0
pxr project scene crates.pixor attribute --name x --from x
pxr project scene crates.pixor set-shape --setting height --from x --times 0.15 --plus 0.5
pxr project scene crates.pixor set-shape --setting hue --from x --times 40
pxr project scene crates.pixor bevel --width 0.06
pxr render crates.pixor -o out/crates.png

木箱位于 0 到 4.4 m 处,因此它们从 0.5 m 增长到约 1.2 m,颜色从橙色经黄色、绿色变为青色。色相改变的是颜色,所以只在有颜色的形状上可见;灰色的形状仍是灰色。

无论节点的顺序如何,形状的设置都会在任何「细分」或「置换」之前应用,因为根据设置重新构建的形状是从它自己的表面开始的。只有独立放置的形状才会应用这些设置;挂在骨骼上的形状属于模型的一部分。

绑定到蒙皮或带有形态键的部件会被置换但不会被细分,因为在边的中间新增的点没有可靠的权重;随风摇摆的部件也是如此。

一个副本与相邻副本的区别在于它的编号、属性和种子,或者根本没有区别——绝不会因为被命名而不同,因为给某个副本命名会让节点图变成场景文件。

蓝图

项目指向的是这一个模型。蓝图则是同样的东西,只是把路径提取出来,并赋予名称和类型——外观、上面的节点图、片段和导出——因此一份配方可以在整个文件夹的模型上运行,而不必复制二十次再逐个修改。

项目 › 另存为蓝图…会写出一个 .pixorblueprint。打开蓝图(项目 › 打开项目…)时会先询问它的输入,之后就和普通项目一样;打开的结果是未命名的,因此保存时不会覆盖原来的配方。

在命令行中:

pxr project blueprint game.pixor -o four-way.pixorblueprint
pxr run four-way.pixorblueprint --in model=hero.glb -o out/hero.png
pxr batch models/ --blueprint four-way.pixorblueprint --out out/
pxr run four-way.pixorblueprint --in model=hero.glb --set ring.count=12
pxr graph set four-way.pixorblueprint --which style --node 0 --set dither=0.5   # change the recipe itself

--set NODE.SETTING=VALUE 修改某个节点的一项设置:如果场景节点图中有该节点,就在场景节点图中修改,否则在所有包含该节点的资产管线和风格中修改(--set hold.times=4:每个资产管线的「停留」节点)。--set PIPELINE/NODE.SETTING=VALUE 只在该管线中修改。节点以其标签的小写形式命名,单词之间用连字符连接(「环」为 ring,「看向」为 look-at:pxr project scene 会列出它们);标签本身("Look at")也可以。每个风格的调色板文件都是一个独立的槽位:第一个风格为 palette,其他风格为 palette:NAME。槽位缺失或类型错误时,会在加载任何内容之前指出该槽位及其所需的类型。

Pixor 在 assets/blueprints/ 中附带了几个:四朝向角色精灵表、背包图标和宣传静帧。

自上次构建以来的变化

每次导出都带有配方(精灵表是如何生成的),所以无需构建数据库也能比较两次构建:

pxr diff out-yesterday/ out-today/

它会列出哪些文件是新增的、哪些消失了、哪些不同以及相差多少像素——以及是哪项设置、哪个输入或哪个蓝图导致的:

changed  ring.png     36598 of 82944 pixels (44.12%)
         scene.nodes[3].settings.count: 16 -> 12

--exact 在出现任何变化时以 1 退出,用于需要可复现的构建。

导出写出的图层

纸娃娃配件、阴影表和视差图层是单独的文件。在资产步骤的 2D 视图中,图层选择框中的全部,叠放会像引擎那样把精灵表的各个图层从后往前叠放:每个图层一个开关,还有一行说明每个图层是否与底图大小相同。不需要先导出:图层由写出它们的代码,根据已经渲染好的帧绘制,并按所选精灵表输出的布局排列——最终精灵(base)、它的阴影、线条、平涂颜色、法线、深度、自发光,以及风格节点图的额外输出。开始时只打开精灵和它的阴影;精灵上方的法线贴图会把它挡住。来自文件则读回导出实际写出的内容——也就是引擎将要加载的文件,包括纸娃娃配件和视差图层。

本页介绍的内容属于完整版;免费的浏览器演示版只提供默认外观和 PNG 精灵表。

在浏览器中试用 获取 Pixor