Spark AR ——动态实例化(一)【脚本】

456 阅读5分钟

本文已参与掘金创作者训练营第三期「高产更文」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力

Spark AR 是 Facebook 免费创作 AR 作品的平台,使用户能够为 Facebook 和 Instagram 创建交互式增强现实体验,超过 40 万名创作者,190个国家/地区,使用 Spark AR 来创作自己的AR作品

由于该软件无需任何编码知识即可使用,因此任何人现在都可以在 AR 世界中几乎没有经验地制作下一个疯狂式传播的 Instagram AR 特效,引领世界潮流。

专门的 AR 滤镜设计师单价甚至可达到 1000 美元到 3 万美元不等。

使用 Spark AR Studio 的动态实例化功能,你可以通过脚本在场景中动态地创建、销毁和重新排序对象。 场景对象、材质和块(Block)的动态创建和销毁都是通过调用它们各自的 API 来实现的。

这可以使得加载时间更少和更优的效果性能,因为内容可以只在需要时加载,而不是预先与场景的其他内容一起加载。 此外,内容可以在不使用时卸载,以释放有价值的资源。

动态实例化提供的额外控制还允许更容易地创建长格式(long-form)的交互体验,因此您可以选择是否以及何时在效果运行时显示某些内容。

当与可下载的块结合使用时,这尤其有效。

考虑一个具有两个端点(A 和 B)的故事驱动效果,每个端点都各自包含在一个块中。 如果用户通过效果的旅程将他们带到端点 A ,你可以动态实例化相应的块,而不必花费资源加载端点 B 的内容。

示例项目演示了如何动态实例化和销毁对象,但本文将提供更深入的代码示例。

使用动态模式

默认情况下,动态对象不会显示在 UI 中。相反,当脚本中调用动态 API 时,Spark AR Studio 会自动检测,并显示通知,提示切换到动态模式。

125417807_305790307090655_6937478289864958445_n.gif

在这种模式下,动态对象以紫色文本显示在场景和资产面板中,按调用脚本定义的层次结构顺序显示。

启用动态模式后,您可以很容易地将效果的当前状态可视化,因为动态对象(如销毁)的更改会实时反映在UI 中。

不能在 Spark AR Studio UI 中编辑动态对象。它们的属性只能通过脚本设置。

您可以通过点击底部状态栏或菜单栏中的 View 选项恢复到 Spark AR Studio 的正常模式。这将从 UI 中隐藏所有的动态对象。

创建动态对象

场景对象可以通过调用由 SceneModule API 公开的 create() 方法动态创建。块是通过调用由BlocksModule API 公开的 instantiate() 方法来实例化的。

这些方法调用需要在项目的属性(Properties)中启用脚本动态实例化功能(Scripting Dynamic Instantiation)。

动态对象必须在它们实例化的脚本中设置它们的属性,因为它们不能从 Spark AR Studio UI 中编辑。

为了在效果中渲染,动态创建的场景对象必须使用 SceneModule API 公开的 addChild() 方法作为场景层次结构中对象的子对象添加。这也适用于在运行时实例化的块。

下面的示例创建了动态实例化支持的每个动态对象的实例,并将它们添加为场景面板中的Focal Distance对象的子对象。

// Load in the required modules
const Scene = require('Scene');
const Blocks = require('Blocks');
const Reactive = require('Reactive');

// Enables async/await in JS [part 1]
(async function() {

    // Locate the focal distance object in the scene
    const focalDistance = await Scene.root.findFirst("Focal Distance");

    // Create a single instance of each scene object supported by dynamic instantiation
    const [dynamicPlane, dynamicCanvas, dynamicRectangle, dynamicAmbientLight, dynamicDirectionalLight, dynamicPointLight, dynamicSpotLight, dynamicParticleSystem, dynamicNull] = await Promise.all([
        Scene.create("Plane", {
            "name": "Plane",
            "width": 0.1,
            "height": 0.1,
            "y": -0.2,
            "hidden": false,
        }),
        Scene.create("Canvas", {
            "name": "Canvas",
        }),
        Scene.create("PlanarImage", {
            "name": "Rectangle",
        }),
        Scene.create("AmbientLightSource", {
            "name": "AmbientLight",
        }),
        Scene.create("DirectionalLightSource", {
            "name": "DirectionalLight",
        }),
        Scene.create("PointLightSource", {
            "name": "PointLightSource",
        }),
        Scene.create("SpotLightSource", {
            "name": "SpotLightSource",
        }),
        Scene.create("ParticleSystem", {
            "name": "Particle",
        }),
        Scene.create("SceneObject", {
            "name": "NullObject",
        }),
    ]);


    // Add the dynamic objects as children of objects in the Scene panel - they will not be rendered in the effect otherwise
    focalDistance.addChild(dynamicCanvas);
    dynamicCanvas.addChild(dynamicRectangle);
    focalDistance.addChild(dynamicAmbientLight);
    focalDistance.addChild(dynamicDirectionalLight);
    focalDistance.addChild(dynamicPointLight);
    focalDistance.addChild(dynamicSpotLight);
    focalDistance.addChild(dynamicParticleSystem);
    focalDistance.addChild(dynamicNull);


    // Create an instance of a downloadable Block
    // This assumes an existing block in the project called 'block0'
    Blocks.instantiate('block0').then(function(block) {
        focalDistance.addChild(block);
        block.addChild(dynamicPlane);
    });    
              
// Enables async/await in JS [part 2]
})();

通过调用 MaterialsModule API 公开的 create() 方法,也支持动态创建材质。与动态场景对象一样,动态材质的属性必须在脚本中设置。

然而,与动态场景对象不同,材质不需要成为场景面板中现有对象的子对象。

虽然下面的例子只动态创建一个默认材质和一个混合材质,但是下面所有的类型都是支持的:

  • DefaultMaterial
  • BlendedMaterial
  • ColorPaintMaterial
  • ComposedMaterial
  • CustomMaterial
  • MetallicRoughnessPbrMaterial
  • RetouchingMaterial
// Load in the required modules
const Scene = require('Scene');
const Materials = require('Materials');
const Textures = require('Textures');
              
// Enable the Touch Gestures > Tap Gesture capability 
// in the project's properties
const TouchGestures = require('TouchGestures');

// Enables async/await in JS [part 1]
(async function() {

    // Locate the focal distant object in the Scene and the two textures in the Assets panel
    const [focalDistance, textureOne, textureTwo] = await Promise.all([
        Scene.root.findFirst('Focal Distance'),
        Textures.findFirst('texture0'),
        Textures.findFirst('texture1'),
    ]);

    // Dynamically instantiate a plane and two materials
    const [dynamicPlane, defaultMaterial, blendedMaterial] = await Promise.all([

        Scene.create("Plane", {
            "name": "Plane",
            "width": 0.1,
            "height": 0.1,
            "y": -0.2,
            "hidden": false,
        }),

        Materials.create("DefaultMaterial", {
            "name": "Default Material",
            "blendMode": "ALPHA",
            "opacity": 1.0,
            "diffuse": textureOne,
        }),

        Materials.create("BlendedMaterial", {
            "name": "Blended Material",
            "opacity": 0.8,
            "diffuse": textureTwo,
        }),
    ]);


    // Assign the first of the dynamic materials to the created plane.
    dynamicPlane.material = defaultMaterial;

    // Add the Dynamic Plane as a child object of the Focal Distance object in the Scene panel so that it is rendered in the effect
    focalDistance.addChild(dynamicPlane);


    // Switch the plane's material between the two previously created dynamic materials, when the plane is tapped
    let isUsingMaterialOne = true;

    TouchGestures.onTap(dynamicPlane).subscribe(() => {
        if(isUsingMaterialOne){
            dynamicPlane.material = blendedMaterial;
            isUsingMaterialOne = false;
        } else {
            dynamicPlane.material = defaultMaterial;
            isUsingMaterialOne = true;
        }
    });
              
// Enables async/await in JS [part 2]
})();             

销毁动态对象

下回分解

拷贝材质

下回分解

调整父对象的对象(重新排序)

下回分解