Project Icon

sceneform-android

Android增强现实开发框架Sceneform

Sceneform-android是基于ARCore的Android增强现实开发框架,采用Google Filament作为3D渲染引擎。该框架支持AR模型查看、增强图像、人脸特效、视频纹理、深度感知、云锚点、即时放置和光线估计等功能。作为Google Sceneform的延续项目,它集成了最新的ARCore SDK和Filament,支持直接使用gltf和glb格式的3D模型。Sceneform-android简化了OpenGL开发难度,同时兼容Java和Kotlin语言,为开发者提供便捷的AR应用开发体验。

Maven Central Discord

Android 平台的 Sceneform 维护版 SDK


⚠️ 本框架已不再更新。请考虑在您的项目中使用 Sceneview 替代。


Sceneform 维护版是一个以 Google Filament 为 3D 引擎的 ARCore Android SDK。这是 已归档的 Sceneform 的延续

Android 增强现实库:AR 模型查看器、增强图像、增强人脸、视频、深度、云锚点、即时放置、光线估计等,支持 Kotlin 和 Java


与我们交流

Discord 频道

(请不要在此提出问题,请转到 GitHub Issues 区域)


Sceneform 维护版和后继者:

与 Google Sceneform(1.15.0、1.16.0、1.17.0 和 1.17.1)的区别

  • 无需插件:直接使用 assets、res/raw、本地文件或 http/https 链接中的 gltf 和 glb 3D 模型文件,代替 sfa、sfb、fbx、obj 等
  • 最新版本的 ARCore SDKGoogle Filament
  • 最新的 gradle 依赖,包括 AndroidX、LifecycleScope/Coroutines(仅限 SceneView)等
  • 可作为 mavenCentral() 依赖使用
  • 支持增强图像
  • 支持增强人脸
  • 使用 glbgltf 作为 3D 模型(支持动画
  • 使用 hdrktx 作为环境(间接光 + 天空盒)
  • 支持纯 3D 使用,无需 ARCore 的单一依赖(仅限 SceneView
  • VideoNode 用于 MediaPlayer(mp4、avi 等)视频 3D 节点
  • 水平/垂直平面放置
  • 深度遮挡和放置(仅限 SceneView
  • 即时放置仅限 SceneView
  • HDR 光线估计,可在更加壮观或真实之间调整
  • 简单的模型查看器,仅需 ArSceneView 参数即可实现基本用途(仅限 SceneView
  • 减少对 OpenGL 知识的需求

本仓库最初是 Sceneform 的一个分支。版权所有 (c) 2021 Google Inc. 保留所有权利。

依赖

app/build.gradle

dependencies {
     implementation "com.gorisse.thomas.sceneform:sceneform:1.23.0"
}

更多...

使用方法(简单模型查看器)

更新您的 AndroidManifest.xml

AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />

<application>
    ...
    <meta-data android:name="com.google.ar.core" android:value="optional" />
</application>

更多...

View 添加到您的 layout

res/layout/main_activity.xml

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/arFragment"
    android:name="com.google.ar.sceneform.ux.ArFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

示例...

编辑您的 ActivityFragment

src/main/java/.../MainActivity.java


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    // 从 assets 文件夹或 http 链接加载 model.glb
    (supportFragmentManager.findFragmentById(R.id.arFragment) as ArFragment)
        .setOnTapPlaneGlbModel("model.glb")
}

src/main/java/.../MainFragment.java

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    // 从 assets 文件夹或 http 链接加载 model.glb
    (childFragmentManager.findFragmentById(R.id.arFragment) as ArFragment)
        .setOnTapPlaneGlbModel("https://storage.googleapis.com/ar-answers-in-search-models/static/Tiger/model.glb")
}

Kotlin 示例...

Java 示例...

示例

带动画的 glTF

完整视频
完整视频...

arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
    // 创建锚点
    arFragment.arSceneView.scene.addChild(AnchorNode(hitResult.createAnchor()).apply {
        // 创建可变换模型并将其添加到锚点
        addChild(TransformableNode(arFragment.transformationSystem).apply {
            renderable = model
            renderableInstance.animate(true).start()
        })
    })
}

Kotlin 示例项目...

Java 示例项目...

深度遮挡

深度遮挡 01深度遮挡 02深度遮挡 03
arFragment.apply {
    setOnSessionConfigurationListener { session, config ->
        if (session.isDepthModeSupported(Config.DepthMode.AUTOMATIC)) {
            config.depthMode = Config.DepthMode.AUTOMATIC
        }
    }
    setOnViewCreatedListener { arSceneView ->
        // 可用模式:DEPTH_OCCLUSION_DISABLED, DEPTH_OCCLUSION_ENABLED
        arSceneView.cameraStream.depthOcclusionMode =
            CameraStream.DepthOcclusionMode.DEPTH_OCCLUSION_ENABLED
    }
}

文档...

示例项目...

增强图像

增强图像 01

示例项目...

增强人脸

增强人脸 01增强人脸 02增强人脸 03

这里提供了一个可用于创建自己模型的Blender文件增强人脸模板:点击这里

骨骼中的四个骨头在运行时会移动到相应的ARCore姿势。可以将物体附加到骨头上或为顶点分配权重,以根据ARCore姿势定位物体或修改更大的网格。Blender文件中骨头的位置给出了最终结果的大致外观,因此附加对象的相对变换很重要。

图片

面部纹理应作为单独的文件准备。可以使用此模板创建面部纹理:canonical_face.png

你还可以根据骨骼和分配给顶点的权重来改变模型形状,移动头部周围的一些小物体,缩放它们等...

你甚至可以为所有这些制作动画。

示例项目...

云锚点

图片图片
// 创建一个新的锚点 = ARCore将尝试使用ARCore云锚点服务和提供的cloudAnchorId来解析其姿态
sceneView.session?.resolveCloudAnchor(cloudAnchorId)?.let { resolvedAnchor ->
  sceneView.scene.addChild(AnchorNode(resolvedAnchor).apply {
      addChild(VideoNode(context, MediaPlayer.create(context, R.raw.restaurant_presentation).apply {
                  this.start()
              },null)
      )
  })
}

环境光照

环境光照 01环境光照 02环境光照 03
环境光照 04环境光照 05环境光照 06

https://user-images.githubusercontent.com/83349532/144654379-a4476baf-8a22-45aa-8088-4f5e3fc7384b.mp4

// 如果你希望你的对象更接近真实效果,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.REALISTIC
// 如果你希望你的对象效果更加壮观,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.SPECTACULAR
// 如果你只想应用ARCore光照的颜色和强度,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.AMBIENT_INTENSITY
// 如果你想禁用所有ARCore光照估计,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.DISABLED

示例项目...

视频纹理

视频纹理 01视频纹理 02视频纹理 03
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
    // 创建锚点
    arFragment.arSceneView.scene.addChild(AnchorNode(hitResult.createAnchor()).apply {
        addChild(VideoNode(context, MediaPlayer.create(context, R.raw.video).apply {
            start()
        }, chromaKeyColor, null))
    })
}

示例项目...

动态材质/纹理

动态材质 01动态材质 02

示例项目...

非AR用途

非AR用途 01

示例项目...

演示

在Google Play上获取

模拟器

已知可用配置

图片

更多...

贡献 - 提交拉取请求

复刻仓库或在 Discord 上申请贡献者权限

图片

安装 GitHub Android Studio 插件

图片

直接从 Android Studio 创建 Pull Request

图片

更多...

深入探讨

AR 必需 vs AR 可选

如果你的应用需要 ARCore(AR 必需)而不仅仅是(AR 可选),请使用此清单来表明该应用需要 Google Play Services for AR(AR 必需),这将导致该应用仅在支持 ARCore 的设备上的 Google Play 商店中可见:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.ar" android:required="true"/>

<application>
    ...
    <meta-data android:name="com.google.ar.core" android:value="required" />
</application>

更多...

节点

要在用户点击平面时向场景添加一个或多个节点,你可以重写 BaseArFragment.OnTapArPlaneListeneronTapPlane 函数:

arFragment.setOnTapArPlaneListener(::onTapPlane)
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
    // 创建锚点
    arFragment.arSceneView.scene.addChild(AnchorNode(hitResult.createAnchor()).apply {
        // 创建可变换模型并将其添加到锚点
        addChild(TransformableNode(arFragment.transformationSystem).apply {
            renderable = model
            renderableInstance.animate(true).start()
            // 添加相对于父模型的子模型
            addChild(Node().apply {
                // 定义相对位置
                localPosition = Vector3(0.0f, 1f, 0.0f)
                // 定义相对缩放
                localScale = Vector3(0.7f, 0.7f, 0.7f)
                renderable = modelView
            })
        })
    })
}

示例...

移除或隐藏节点

从场景中移除锚点节点

anchorNode.anchor = null

从场景中移除模型节点、视频节点、增强人脸节点等

node.parent = null

显示/隐藏节点 = 不渲染它

node.enabled = false

文档...

帧率(FPS 限制)

上限

渲染的更新率受 ARCore 使用的相机配置限制。对大多数智能手机来说是 30 fps,对 Pixel 智能手机来说是 60 fps。用户可以手动更改这个值(你应该知道你在做什么)。

arFragment.setOnViewCreatedListener { arSceneView ->
    // 设置更高的帧率上限
    arSceneView.setMaxFramesPerSeconds(60)
}

默认值是 60

文档...

动画

到目前为止,只有 RenderableInstance 是可动画的。下面的 model 对应于从 node.getRenderableInstance() 返回的 RenderablaInstance

基本用法

对于一个非常基础的 3D 模型,比如一个单一的无限旋转球体,你可能不需要使用 ModelAnimator,而只需调用:

model.animate(repeat).start();

单一模型与单一动画

如果你想将单个模型动画化到特定的时间线位置,请使用:

ModelAnimator.ofAnimationFrame(model, "AnimationName", 100).start();
ModelAnimator.ofAnimationFraction(model, "AnimationName", 0.2f, 0.8f, 1f).start();
ModelAnimator.ofAnimationTime(model, "AnimationName", 10.0f)}.start();

我在哪里可以找到 "AnimationName"?

动画名称是在 3D 模型级别定义的。 你可以将其比作播放与模型中特定行为相对应的轨道。

例如,在 Blender 中,"AnimationName" 可以对应:

  • 非线性动画视图端口 中定义的动作
  • 时间线视图端口 中的单个对象行为

要知道 glb/gltf 文件的实际动画名称,你可以将其拖到 glTF 查看器上,比如这里,然后在动画列表中找到它。

  • 单一的时间、帧或分数值将从实际位置到达所需值
  • 两个值意味着从值1到值2
  • 超过两个值意味着从值1到值2然后到值3

单一模型与多个动画

如果模型是一个角色,例如,可能有一个用于行走循环的 ModelAnimation,第二个用于跳跃,第三个用于横向移动,等等:

顺序播放

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(ModelAnimator.ofMultipleAnimations(model, "walk", "run"));
animatorSet.start();

自动取消

这里你可以看到不需要调用animator.cancel(),因为animator.setAutoCancel(boolean)默认设置为true

ObjectAnimator walkAnimator = ModelAnimator.ofAnimation(model, "walk");
walkButton.setOnClickListener(v -> walkAnimator.start());

ObjectAnimator runAnimator = ModelAnimator.ofAnimation(model, "run");
runButton.setOnClickListener(v -> runAnimator.start());

多个模型的多个动画

对于同步动画集,如动画化包含多个模型的完整场景,请考虑使用带有每个步骤参数化的ModelAnimatorAnimatorSet

AnimatorSet completeFly = new AnimatorSet();

ObjectAnimator liftOff = ModelAnimator.ofAnimationFraction(airPlaneModel, "FlyAltitude",0, 40);
liftOff.setInterpolator(new AccelerateInterpolator());

AnimatorSet flying = new AnimatorSet();
ObjectAnimator flyAround = ModelAnimator.ofAnimation(airPlaneModel, "FlyAround");
flyAround.setRepeatCount(ValueAnimator.INFINITE);
flyAround.setDuration(10000);
ObjectAnimator airportBusHome = ModelAnimator.ofAnimationFraction(busModel, "Move", 0);
flying.playTogether(flyAround, airportBusHome);

ObjectAnimator land = ModelAnimator.ofAnimationFraction(airPlaneModel, "FlyAltitude", 0);
land.setInterpolator(new DecelerateInterpolator());

completeFly.playSequentially(liftOff, flying, land);

变形动画

假设一个角色对象有一个骨骼,一个关键帧轨道可以存储下臂骨骼随时间变化的位置数据,另一个轨道存储同一骨骼的旋转变化数据,第三个轨道存储另一个骨骼的位置、旋转或缩放,以此类推。显然,ModelAnimation可以作用于许多这样的轨道。

假设模型有变形目标(例如一个变形目标显示友好的面部表情,另一个显示愤怒的面部表情),每个轨道都保存了在动画片段执行过程中某个变形目标的影响如何变化的信息。

在glTF上下文中,这个{@link android.animation.Animator}根据glTF动画和蒙皮定义更新矩阵。

ModelAnimator可用于两个用途

  • 根据模型动画定义更新TransformManager组件中的矩阵。
  • 更新RenderableManager组件中的骨骼矩阵。

动画

每个修改动画时间位置的PropertyValuesHolder必须使用ModelAnimation.TIME_POSITION而不是自己的Property,以便可能取消在同一ModelAnimation上操作时间修改的任何ObjectAnimator。

更多...

许可证

请查看LICENSE文件。

品牌指南

Sceneform商标是Google的商标,不受GitHub上Apache 2.0许可的Sceneform存储库中包含的版权或专利许可授权的约束。除这些指南中允许的使用外,任何使用Sceneform商标的行为都必须事先得到Google的批准。

品牌指南的目的

这些指南的存在是为了确保Sceneform项目可以在开源许可下共享其技术,同时确保"Sceneform"品牌作为一个有意义的来源标识符得到保护,符合商标法。通过遵守这些指南,你有助于促进使用和开发高质量Sceneform技术的自由。

可接受的使用

由于我们开源了Sceneform技术,你可以在未经事先书面许可的情况下使用Sceneform商标来指代该项目。这些批准的引用示例包括:

  • 指代Sceneform项目本身;
  • 指代GitHub上Sceneform存储库共享的未经修改的源代码或其他文件;
  • 准确地表明你的设计或实现是基于、用于或与Sceneform技术兼容。

示例:

  • "[你的产品]用于Sceneform。"
  • "[你的产品]是Sceneform项目的一个分支。"
  • "[你的产品]与Sceneform兼容。"

使用指南

  • Sceneform名称绝不能以可能导致对Google赞助、附属或认可产生混淆的方式使用或注册。
  • 不要将Sceneform名称或类似的容易混淆的术语用作你的公司名称、产品名称、域名或社交媒体资料的一部分。
  • 除这些指南允许的情况外,Sceneform名称不应与其他商标、术语或来源标识符组合使用。
  • 不要删除、扭曲或更改Sceneform名称。这包括修改Sceneform名称,例如通过连字符、组合或缩写。不要缩短、缩写或创造Sceneform名称的首字母缩写词。
  • 不要以与周围文本不同的样式、颜色或字体显示Sceneform名称。
  • 不要将Sceneform一词用作动词,也不要使用其所有格形式。

条款和条件

通过下载Android版Sceneform SDK,你同意Google API服务条款管理你对其的使用。

用户隐私要求

你必须在应用程序中突出显示并方便用户访问Google Play Services for AR (ARCore)的使用情况以及它如何收集和处理数据。你可以通过在主菜单或通知屏幕上添加以下文字来实现这一点:"本应用程序运行在Google Play Services for AR (ARCore)上,该服务由Google LLC提供,并受Google隐私政策管理"。

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号