Android 平台的 Sceneform 维护版 SDK
⚠️ 本框架已不再更新。请考虑在您的项目中使用 Sceneview 替代。
Sceneform 维护版是一个以 Google Filament 为 3D 引擎的 ARCore Android SDK。这是 已归档的 Sceneform 的延续
Android 增强现实库:AR 模型查看器、增强图像、增强人脸、视频、深度、云锚点、即时放置、光线估计等,支持 Kotlin 和 Java
与我们交流
(请不要在此提出问题,请转到 GitHub Issues 区域)
Sceneform 维护版和后继者:
- Sceneform - Java 延续版:在此
- SceneView - Kotlin 后继者:SceneView/sceneview-android
与 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 SDK 和 Google Filament
- 最新的 gradle 依赖,包括 AndroidX、LifecycleScope/Coroutines(仅限 SceneView)等
- 可作为
mavenCentral()
依赖使用 - 支持增强图像
- 支持增强人脸
- 使用
glb
或gltf
作为 3D 模型(支持动画) - 使用
hdr
或ktx
作为环境(间接光 + 天空盒) - 支持纯 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" />
编辑您的 Activity
或 Fragment
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")
}
示例
带动画的 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()
})
})
}
深度遮挡
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
}
}
增强图像
增强人脸
这里提供了一个可用于创建自己模型的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)
)
})
}
环境光照
// 如果你希望你的对象更接近真实效果,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.REALISTIC
// 如果你希望你的对象效果更加壮观,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.SPECTACULAR
// 如果你只想应用ARCore光照的颜色和强度,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.AMBIENT_INTENSITY
// 如果你想禁用所有ARCore光照估计,请使用此模式
arSceneView.lightEstimationConfig = LightEstimationConfig.DISABLED
视频纹理
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))
})
}
动态材质/纹理
非AR用途
演示
模拟器
已知可用配置
贡献 - 提交拉取请求
复刻仓库或在 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.OnTapArPlaneListener
的 onTapPlane
函数:
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());
多个模型的多个动画
对于同步动画集,如动画化包含多个模型的完整场景,请考虑使用带有每个步骤参数化的ModelAnimator
的AnimatorSet
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隐私政策管理"。