足不出戶,一探古今,打造線上3D數字博物館!
- 2021 年 7 月 27 日
- 筆記
隨着3D技術的不斷革新,為了讓更多的用戶領略歷史之美,越來越多的博物館開始舉辦線上展覽。通過模擬不同的環境、燈光投影、360°無死角放大縮小展品,觀眾可以享受到身臨其境的沉浸體驗。不僅如此,給展品加上BGM或者語音解說,幫助觀眾更加了解展品的詳細背景,讓演示場景更有代入感。
效果示意
看完如此逼真的效果展示,是不是想知道究竟是怎麼實現的呢?
通過Android Studio的Kotlin工程實現 3D場景構建、物品展示以及聲音播放功能,就可以做到。
一、準備3D模型
華為移動服務最新開放的3D物體建模服務(3D Modeling Kit),助力輕鬆建模。我們只需使用手機相機,通過拍攝物體的不同角度圖像,便可實現物體的3D幾何模型和紋理的自動化生成,為應用提供3D模型構建、預覽等能力。具體操作指導可參考《5分鐘給商品建立3D模型,我是如何做到的?》
二、製作3D物體視圖
接下來我們將準備好的展品3D模型,通過華為圖形引擎服務創建一個可交互的3D物體視圖,如圖所示:
↓↓↓
↓↓↓
集成華為圖形引擎服務
軟件要求:JDK1.7及以上版本
• minSdkVersion :設置為19或以上
• targetSdkVersion:設置為19或以上
• compileSdkVersion:設置為19或以上
• Gradle 3.5及以上版本
在build.gradle文件中配置以下內容:
buildscript {
repositories {
...
maven { url '//developer.huawei.com/repo/' }
}
...
}
allprojects {
repositories {
...
maven { url '//developer.huawei.com/repo/' }
}
}
在應用級build.gradle文件中配置以下內容:
dependencies {
...
implementation 'com.huawei.scenekit:full-sdk:5.1.0.300'
}
示例工程使用了Kotlin的viewBinding功能從而略過了視圖初始化樣板代碼。可在應用級build.gradle文件里加入如下代碼來啟用viewBinding功能:
android {
...
buildFeatures {
viewBinding true
}
...
}
build.gradle文件同步完成後,就能在工程中使用圖形引擎服務了。
本文中,我們僅需要使用該服務即可展示物品的3D圖像,並且與之進行交互。如果還需要使用其他功能,可以參閱華為圖形引擎服務官方文檔。
創建3D視圖
創建自定義視圖的目的很簡單,確保視圖初始化完成後,第一個模型能自動加載到視圖裡。通過默認的SceneView手動實現模型加載,如下所示:
import android.content.Context
import android.util.AttributeSet
import android.view.SurfaceHolder
import com.huawei.hms.scene.sdk.SceneView
class CustomSceneView : SceneView {
constructor(context: Context?) : super(context)
constructor(
context: Context?,
attributeSet: AttributeSet?
) : super(context, attributeSet)
override fun surfaceCreated(holder: SurfaceHolder) {
super.surfaceCreated(holder)
loadScene("qinghuaci/scene.gltf")
loadSpecularEnvTexture("qinghuaci/specularEnvTexture.dds")
loadDiffuseEnvTexture("qinghuaci/diffuseEnvTexture.dds")
}
}
展示物品需添加相關模型文件,打開工程文件夾,在「src/main」路徑下創建「assets」文件夾,將3D模型文件保存,比如:
surfaceCreated中的loadScene()、loadSpecularEnvTexture()和loadDiffuseEnvTexture()方法用於加載物品。創建surface後,第一個物品將加載到surface中。
接下來,打開用於展示3D模型視圖的XML文件,本工程中為activity_main.xml。在該文件中,創建剛才構造的CustomSceneView。下方代碼使用了箭頭圖片用以在不同的物品模型間切換。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="//schemas.android.com/apk/res/android"
xmlns:app="//schemas.android.com/apk/res-auto"
xmlns:tools="//schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.sceneaudiodemo.CustomSceneView
android:id="@+id/csv_main"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<ImageView
android:id="@+id/iv_rightArrow"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="12dp"
android:src="@drawable/ic_arrow"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/iv_leftArrow"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_margin="12dp"
android:rotation="180"
android:src="@drawable/ic_arrow"
android:tint="@color/white"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
一切準備就緒,應用打開之後就能看到第一個展品:青花瓷花瓶了。
增加切換功能
現在,我們通過切換功能來查看多個展品3D模型。
在MainActivity中,配置如下信息:
private lateinit var binding: ActivityMainBinding
private var selectedId = 0
private val modelSceneList = arrayListOf(
"qinghuaci/scene.gltf",
"tangyong/scene.gltf",
)
private val modelSpecularList = arrayListOf(
"qinghuaci/specularEnvTexture.dds",
"tangyong/specularEnvTexture.dds",
)
private val modelDiffList = arrayListOf(
"qinghuaci/diffuseEnvTexture.dds",
"tangyong/diffuseEnvTexture.dds",
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.ivRightArrow.setOnClickListener {
if (modelSceneList.size == 0) return@setOnClickListener
selectedId = (selectedId + 1) % modelSceneList.size // 確保ID處於模型列表的範圍內。
loadImage()
}
binding.ivLeftArrow.setOnClickListener {
if (modelSceneList.size == 0) return@setOnClickListener
if (selectedId == 0) selectedId = modelSceneList.size - 1 // 確保ID處於模型列表的範圍內。
else selectedId -= 1
loadImage()
}
}
private fun loadImage() {
binding.csvMain.loadScene(modelSceneList[selectedId])
binding.csvMain.loadSpecularEnvTexture(modelSpecularList[selectedId])
binding.csvMain.loadDiffuseEnvTexture(modelDiffList[selectedId])
}
在onCreate()中,創建了一個簡單的邏輯,查看下一個/上一個模型。物品文件路徑以字符串的形式保存於各個硬編碼列表中。可以自行修改這個邏輯,使模型呈現更富動態。其中selectedId表示正在展示的物品模型ID。
這樣,就實現了利用SceneView來展示3D模型,效果如下:
三、為展品增加講解詞
在加載不同的3D模型時,我們可以通過華為音頻服務播放該展品對應的講解詞,為用戶提供展品詳細介紹。
集成華為音頻服務
軟件要求:
• JDK版本1.8.211及以上版本
• minSdkVersion:設置為21
• targetSdkVersion:設置為29
• compileSdkVersion:設置為29
• Gradle 4.6及以上版本
可以看到,音頻服務相較於圖形引擎服務軟件要求更高,所以我們需要確保滿足音頻服務的使用要求。
首先,打開應用級build.gradle文件,添加音頻服務的相關配置。
dependencies {
...
implementation 'com.huawei.hms:audiokit-player:1.1.0.300'
...
}
之前在配置圖形引擎服務時,已經添加了必要的庫,所以項目級build.gradle不需要改動。
在activity_main.xml文件中,添加一個簡單的播放按鈕。
<Button
android:id="@+id/btn_playSound"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Play"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
這個按鈕可以用來為展示中的物品播放聲音。
然後,在MainActivity中添加以下配置:
private var mHwAudioManager: HwAudioManager? = null
private var mHwAudioPlayerManager: HwAudioPlayerManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
...
initPlayer(this)
binding.btnPlaySound.setOnClickListener {
mHwAudioPlayerManager?.play(selectedId) //創建播放列表實例。selectedId:播放曲目的參數。
}
...
}
private fun initPlayer(context: Context) {
val hwAudioPlayerConfig = HwAudioPlayerConfig(context)
HwAudioManagerFactory.createHwAudioManager(hwAudioPlayerConfig,
object : HwAudioConfigCallBack {
override fun onSuccess(hwAudioManager: HwAudioManager?) {
try {
mHwAudioManager = hwAudioManager
mHwAudioPlayerManager = hwAudioManager?.playerManager
mHwAudioPlayerManager?.playList(getPlaylist(), 0, 0)
} catch (ex: Exception) {
ex.printStackTrace()
}
}
override fun onError(p0: Int) {
Log.e("init:onError: ","$p0")
}
})
}
fun getPlaylist(): List<HwAudioPlayItem>? {
val playItemList: MutableList<HwAudioPlayItem> = ArrayList()
val audioPlayItem1 = HwAudioPlayItem()
val sound = Uri.parse("android.resource://yourpackagename/raw/soundfilename").toString() // soundfilename不包含文件擴展名。
audioPlayItem1.audioId = "1000"
audioPlayItem1.singer = "Taoge"
audioPlayItem1.onlinePath =
"//lfmusicservice.hwcloudtest.cn:18084/HMS/audio/Taoge-chengshilvren.mp3" //此處Demo使用歌曲示意
audioPlayItem1.setOnline(1)
audioPlayItem1.audioTitle = "chengshilvren"
playItemList.add(audioPlayItem1)
val audioPlayItem2 = HwAudioPlayItem()
audioPlayItem2.audioId = "1001"
audioPlayItem2.singer = "Taoge"
audioPlayItem2.onlinePath =
"//lfmusicservice.hwcloudtest.cn:18084/HMS/audio/Taoge-dayu.mp3"//此處Demo使用歌曲示意
audioPlayItem2.setOnline(1)
audioPlayItem2.audioTitle = "dayu"
playItemList.add(audioPlayItem2)
return playItemList
}
上述配置添加完成後,就能為展品播放講解詞了。本工程使用的聲音音頻為線上資源。如果需要播放本地音頻,可以參考官網指導。這樣,就能導入音頻文件,為物品播放聲音了。
至此,我們就可以創建一個360°可旋轉、放大縮小、帶有音效的展覽場景了。
最後,除了3D文物展示等應用場景,我們還可以把這些能力應用到很多相關行業,比如:
線上社交領域中的臉萌、視頻表情包、視頻虛擬背景;
電商購物領域的3D商品展示、家裝場景渲染、AR試穿;
影音領域的3D解鎖屏保/手機主題、3D特效渲染、直播表情包;
教育領域的3D教學、3D書籍、VR遠程教學。
欲了解更多詳情,請參閱:
解決集成問題請到Stack Overflow
點擊關注,第一時間了解HMS Core最新技術~