【參與評論有獎】我在 Android 上做了一個 1 : 1 高達
theme: jzman highlight: androidstudio
注意:文末有驚喜,請務必看到最後!!
最近看到一個新聞,一個 1:1 的自由高達落戶在上海金橋。
作為高達愛好者,我一直想去現場感受一下真實高達的壓迫感,無奈一直沒機會成行。不過這難不倒我,我決定自己動手做一個 1:1 高達來體驗一番。
藉助 AR 技術我實現了這個效果, 怎麼樣,不比上海金橋的差吧 ~
什麼是 AR (Augemented Reality)
AR(增強現實)是近幾年新興的技術,他可以將3D模型等虛擬元素模擬模擬後應用到現實世界中,虛擬與現實,兩種資訊互為補充,從而實現對真實世界的“增強”。
不少人會將 AR(增強現實) 與 VR(虛擬現實)相混淆,兩者的區別在於虛擬元素的佔比: - VR:看到的場景和人物全是假的,是把你的意識代入一個虛擬的世界。 - AR:看到的場景和人物一部分是真一部分是假,是把虛擬的資訊帶入到現實世界中。
VR 技術的研究已經有30多年的歷史了,而 AR 則年輕得多,隨著智慧手機以及智慧穿戴裝置的普及而逐漸被人們熟知。相對於 VR,AR 的開發門檻低得多,只要有一臺智慧手機,藉助 Google 提供的 ARCore,人人都可以開發出自己的 AR 應用。
ARCore 是 Google 提供的 AR 解決方案,為開發者提供 API,可以通過 Android , iOS 等手機平臺感知周邊環境,打造沉浸式的 AR 體驗。
ARCore 為開發者提供了三大能力:
|ARCore 能力| 示意圖|
|:--:|:--:|
|動作追蹤
通過識別相機影象中的可視特徵點來跟蹤拍攝者的位置,從而決定虛擬元素的相對位置變化||
|環境理解
識別常見水平或垂直表面(如表格或牆壁)上的特徵點群集,還可以確定平面邊界,將虛擬物件放置在平面上||
|光線預測
預測當前場景的光照條件,可以使用此照明資訊來照亮虛擬 AR 物件,模擬物體在現實世界的影子||
ARCore 為 AR 提供了周邊環境的感知能力,但一個完整的 AR 應用還需要處理 3D 模型的渲染,這要藉助 OpenGL ES 來完成,學習成本很高。官方意識到這個問題,在 2017 年推出 ARCore 之後,緊跟著 2018 年的 IO 大會上推出了 Sceneform 這個在 Android 上的 3D 影象渲染庫。
.obj
, .fbx
或 .gltf
等常見的 3D 模型檔案格式,雖然可以在主流的 3D 軟體中通用,但在 Android 中,我們只能通過 OpenGL 程式碼對其進行渲染。而 Sceneform 可以將這些格式的模型檔案,連同所依賴的資原始檔(.mtl
, .bin
, .png
等) 轉換為 .sfa
和 .sfb
格式。 後者是可供 Sceneform 渲染的二進位制模型檔案, 前者是具有可讀性的摘要檔案,用來描述後者。
相比於 OpenGL , Sceneform 的使用要簡單得多,而且 sfb
還可以通過 Sceneform 提供的 AS 外掛在 IDE 中進行模型預覽。
接下來,通過 Sceneform 和 ARCore 來實現我的 1:1 高達
1. Gradle 新增依賴
新建 AndroidStudio 工程,在 root 的 build.gradle 中新增 Sceneform 外掛
groovy
dependencies {
classpath 'com.google.ar.sceneform:plugin:1.17.1'
}
接著在 app 的 build.gradle 中依賴 ARCore 和 Sceneform 的 aar
groovy
dependencies {
...
implementation 'com.google.ar:core:1.26.0'
implementation 'com.google.ar.sceneform.ux:sceneform-ux:1.17.1'
implementation 'com.google.ar.sceneform:core:1.17.1'
}
2. Manifest 申請許可權
```xml
```
3. 佈局檔案
ARFragment
可以用來承載 AR 場景、響應使用者行為,Android 中顯示虛擬元素的最簡答的方法就是在佈局中新增一個 ARFRagment :
```xml
```
4. 製作 sfb 模型檔案
3D 模型一般是通過 Maya 或 3D Max 等專業軟體製作的,不少 3D 建模愛好者會將自己的作品上傳到一些設計網站供大家免費或有償下載。
我們可以在網站上下載常見格式的 3D 模型檔案。以 .obj
為例,obj 檔案中描述了多邊形的頂點和片段資訊等, 此外還有顏色、材質等資訊儲存在配套的 .mtl
檔案中 , 我們將下載的 obj/mtl/png
等模型檔案拷貝到非 assets 目錄下,這樣可以避免打入 apk。
例如 app/sampledata
我們在 build.gtadle 通過 sceneform.asset(...)
新增 obj > sfb
的配置如下
groovy
sceneform.asset('sampledata/msz-006_zeta_gundam/scene.obj',
'default',
'sampledata/msz-006_zeta_gundam/scene.sfa',
'src/main/assets/scene')
sampledata/msz-006_zeta_gundam/scene.obj
是 obj 原始檔位置, src/main/assets/scene
是生成的 sfb 目標路徑,我們將目標檔案生成在 assets/
中,打入 apk ,便於在執行時載入。
gradle 配置完後,sync 並 build 工程,build 過程中,會在 assets/
中生成同名 sfb 檔案
5. 載入、渲染模型
```kotlin //MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
arFragment = supportFragmentManager.findFragmentById(R.id.ux_fragment) as ArFragment
arFragment.setOnTapArPlaneListener { hitResult, plane, motionEvent ->
if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING ||
state != AnchorState.NONE) {
[email protected]
}
val anchor = hitResult.createAnchor()
placeObject(ux_fragment, anchor, Uri.parse("scene.sfb"))
}
}
```
ARFragment 能夠響應在 AR 場景中的使用者點選行為,在點選的位置中新增虛擬元素,
Uri.parse("scene.sfb")
用來獲取 assets 中生成的模型檔案。
```kotlin private fun placeObject(fragment: ArFragment, anchor: Anchor, model: Uri) { ModelRenderable.builder() .setSource(fragment.context, model) .build() .thenAccept { addNodeToScene(fragment, anchor, it) } .exceptionally { throwable : Throwable -> Toast.makeText(arFragment.getContext(), "Error:$throwable.message", Toast.LENGTH_LONG).show(); [email protected] null } }
``
Sceneform 提供
ModelRenderable用於模型渲染。 通過
setSource` 載入 sfb 模型檔案
kotlin
private fun addNodeToScene(fragment: ArFragment, anchor: Anchor, renderable: Renderable) {
val anchorNode = AnchorNode(anchor)
val node = TransformableNode(fragment.transformationSystem)
node.renderable = renderable
node.setParent(anchorNode)
fragment.arSceneView.scene.addChild(anchorNode)
node.select()
}
ARSceneView
持有一個 Scene
, Scene 是一個樹形資料結構,作為 AR 場景的根節點,各種虛擬元素將作為其子節點被新增到場景中進行渲染
kotlin
val node = TransformableNode(fragment.transformationSystem)
node.renderable = renderable
node.setParent(anchorNode)
所以,渲染 3D 模型,其實就是新增一個 Node 併為其設定 Renderable
的過程。
hitResult
是使用者點選的位置資訊,Anchor
基於 hitResult 建立錨點,這個錨點作為子節點被新增到 Scene 根節點中,同時又作為 TransformableNode
的父節點。 TransformableNode 用來承載 3D 模型, 它可以接受手勢進行拖拽或者放大縮小, 新增到 Archor 就相當於把 3D 模型放置到點選的位置上。
6. 完整程式碼
```kotlin class MainActivity : AppCompatActivity() { lateinit var arFragment: ArFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (!checkIsSupportedDeviceOrFinish(this)) return
setContentView(R.layout.activity_main)
arFragment = supportFragmentManager.findFragmentById(R.id.ux_fragment) as ArFragment
arFragment.setOnTapArPlaneListener { hitresult: HitResult, plane: Plane, motionevent: MotionEvent? ->
if (plane.type != Plane.Type.HORIZONTAL_UPWARD_FACING)
[email protected]
val anchor = hitresult.createAnchor()
placeObject(arFragment, anchor, R.raw.cube)
}
}
private fun placeObject(arFragment: ArFragment, anchor: Anchor, uri: Int) {
ModelRenderable.builder()
.setSource(arFragment.context, uri)
.build()
.thenAccept { modelRenderable: ModelRenderable -> addNodeToScene(arFragment, anchor, modelRenderable) }
.exceptionally { throwable: Throwable ->
Toast.makeText(arFragment.getContext(), "Error:$throwable.message", Toast.LENGTH_LONG).show();
[email protected] null
}
}
private fun addNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: Renderable) {
val anchorNode = AnchorNode(anchor)
val node = TransformableNode(arFragment.transformationSystem)
node.renderable = renderable
node.setParent(anchorNode)
arFragment.arSceneView.scene.addChild(anchorNode)
node.select()
}
private fun checkIsSupportedDeviceOrFinish(activity: Activity): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Toast.makeText(activity, "Sceneform requires Android N or later", Toast.LENGTH_LONG).show()
activity.finish()
return false
}
val openGlVersionString = (activity.getSystemService<Any>(Context.ACTIVITY_SERVICE) as ActivityManager)
.deviceConfigurationInfo
.glEsVersion
if (openGlVersionString.toDouble() < MIN_OPENGL_VERSION) {
Toast.makeText(activity, "Sceneform requires OpenGL ES 3.0 or later", Toast.LENGTH_LONG)
.show()
activity.finish()
return false
}
return true
}
companion object {
private const val MIN_OPENGL_VERSION = 3.0
}
}
```
checkIsSupportedDeviceOrFinish
用來檢測可執行環境,通過實現可知, Sceneform 的執行條件是 AndroidN 以及 OpenGL 3.0 以上。
以上就是全部程式碼了,雖然程式碼很少,效果很哇塞
最後
Sceneform 配合 ARCore 可以快速搭建 AR 應用,除了載入靜態的 3D 模型以外,Sceneform 還可以載入帶動畫的模型。
隨著 “元宇宙” 概念的興起,Google,Facebook 等巨頭必將加大在 AR 乃至 VR 技術上的研究投入,虛擬現實技術或將成為移動網際網路之後的新一代社交、娛樂場景,想象空間巨大。
今天就寫到這裡吧, 我要和剛認識的小姐姐玩耍去了 🙈
最後推薦一個網站,大家可以在那裡下載一些有趣的 3D 模型 ~
https://sketchfab.com/
(完)
# 文末驚喜來了 !🎉🎉
衷心感謝各位讀者大大的關注,歡迎大家就本文內容進行評論和吐槽!🙏🙏
本人將選取最熱門的前兩條評論使用者,送出小禮品:掘金徽章一枚 🏅
- 熱門評論排序規則 :點贊數 + 評論數 (作者本人的評論除外)
- 統計截止時間: 9月10日 24點
之後我將聯絡獲獎使用者寄送禮物,感謝掘金平臺對本次活動的大力支援!
- Google I/O :Android Jetpack 最新變化(二) Performance
- Google I/O :Android Jetpack 最新變化(一) Architecture
- Google I/O :Android Jetpack 最新變化(四)Compose
- Google I/O :Android Jetpack 最新變化(三)UI
- 一文看懂 Jetpack Compose 快照系統
- 聊聊 Kotlin 代理的“缺陷”與應對
- AAB 扶正!APK 再見!
- 面試必備:Kotlin 執行緒同步的 N 種方法
- Jetpack MVVM 七宗罪之六:ViewModel 介面暴露不合理
- CreationExtras 來了,建立 ViewModel 的新方式
- Kotlin DSL 實戰:像 Compose 一樣寫程式碼
- 為什麼 RxJava 有 Single / Maybe 等單發資料型別,而 Flow 沒有?
- 使用整潔架構優化你的 Gradle Module
- 一道面試題:介紹一下 Fragment 間的通訊方式?
- 【程式碼吸貓】使用 Google MLKit 進行影象識別
- Kotlin 1.6 正式釋出,帶來哪些新特性?
- Android Dev Summit '21 精彩內容盤點
- @OnLifecycleEnvent 被廢棄,替代方案更簡單
- Jetpack Navigation 實現自定義 View 導航
- 實現一個 Coroutine 版 DialogFragment