4

足不出户,一探古今,打造线上3D数字博物馆!

 2 years ago
source link: https://my.oschina.net/u/4956408/blog/5148616
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

随着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 'https://developer.huawei.com/repo/' }
    }
    ...
}

allprojects {
 repositories {
  ...
  maven { url 'https://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="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://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 =
        "https://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 =
        "https://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远程教学。

欲了解更多详情,请参阅:

>>华为3D建模服务官网 、 开源仓库
>>华为图形引擎服务官网 开源仓库
>>华为音频服务官网 开源仓库
>>华为HMS Core官方论坛
>>解决集成问题请到Stack Overflow

点击关注,第一时间了解HMS Core最新技术~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK