随着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物体建模服务演示DEMO

华为图形引擎服务官网演示DEMO

华为音频服务官网演示DEMO

华为HMS Core官方论坛

解决集成问题请到Stack Overflow

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

足不出户,一探古今,打造线上3D数字博物馆!的更多相关文章

  1. Vue.js项目实战-打造线上商城

    首先上一下完成后的效果: 首页: 商品详情页: 购物车页(其实还有个订单页,只是和购物车页基本类似,所以就不截图啦): 开始项目: 由于涉及的是前后端分离,所以我们的后台数据就模拟存储于浏览器端(co ...

  2. “3D引擎和图形学技术点思路讲解”线上直播培训班报名开始啦(完全免费)

    大家好,我开了一个线上的直播课程培训班,完全免费,欢迎大家报名! 本课程重点教授"光线追踪"方面的实现思路. 我的相关经验 5年3D引擎开发经验 Wonder-WebGL 3D引擎 ...

  3. 【实时渲染】实时3D渲染如何加速汽车线上体验应用推广

    在过去,一支优秀的广告片足以让消费者对一辆汽车产生兴趣.完美的底盘线条或引擎的轰鸣声便会让潜在买家跑到经销商那里试驾.现在,广告还是和往常一样,并没有失去其特性,但86%的买家在与销售交流之前会在网上 ...

  4. CODING DevOps 助力中化信息打造新一代研效平台,驱动“线上中化”新未来

    中化信息技术有限公司,简称"中化信息",是世界 500 强企业中国中化控股有限责任公司(简称"中国中化")的全资直属公司,依托于中国中化的信息化建设实践,建立起 ...

  5. 用webgl打造自己的3D迷宫游戏

    用webgl打造自己的3D迷宫游戏 2016/09/19 · JavaScript · WebGL 原文出处: AlloyTeam    背景:前段时间自己居然迷路了,有感而发就想到写一个可以让人迷路 ...

  6. 蒲公英 · JELLY技术周刊 Vol.16 谷歌首个线上 Web 开发者大会

    蒲公英 · JELLY技术周刊 Vol.16 近期,谷歌有史以来的第一次线上谷歌 Web 开发者大会,Web Vitals.PWA.DevTools 和 Lighthouse 6.0 等一系列特性或产 ...

  7. 移动应用开发迎来哪些新技术?5月24日相约HDD·线上沙龙·创新开发专场

    HUAWEI Developer Day(简称HDD),是华为开发者联盟与广大开发者深度交流的平台.围绕移动终端的最新技术和产品形态,持续向广大开发者传递华为终端的最新产品和开放服务能力,结合最新的行 ...

  8. HDD线上沙龙·创新开发专场:多元服务融合,助力应用创新开发

    5月24日,由华为开发者联盟主办的HUAWEI Developer Day(华为开发者日,简称HDD)线上沙龙·创新开发专场在华为开发者学堂及各大直播平台与广大开发者见面.直播内容主要聚焦Harmon ...

  9. 线上Linux服务器运维安全策略经验分享

    线上Linux服务器运维安全策略经验分享 https://mp.weixin.qq.com/s?__biz=MjM5NTU2MTQwNA==&mid=402022683&idx=1&a ...

随机推荐

  1. Java真的是白天鹅

    前言 我最近越来越真切的感受到,Java真的是白天鹅. 这真的是一种羡慕嫉妒恨的感受. 今天和一个Java技术Leader聊天,我告诉他敏捷开发是以人为本,他居然跟我说敏捷开发在行业内有规范,规范是死 ...

  2. JVM-gcRoots 和 强引用,软引用, 弱引用, 虚引用, 代码演示和应用场景

    什么是垃圾? 什么是gcRoots, 谈谈你对 强, 软, 弱 , 虚引用的理解, 他们的应用场景 jvm采用可达性分析法: 从gcRoots集合开始,自上向下遍历,凡是在引用链上的对象,都不是垃圾, ...

  3. NX二次开发】Block UI 选择特征

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...

  4. 【NX二次开发】Block UI 指定平面

    属性说明 属性   类型   描述   常规           BlockID    String    控件ID    Enable    Logical    是否可操作    Group    ...

  5. Spring Boot WebFlux-导读

    背景 大家都知道,Spring Framework 是 Java/Spring 应用程序跨平台开发框架,也是 Java EE(Java Enterprise Edition) 轻量级框架,其 Spri ...

  6. 带你认真了解一下Java分布式系统的基本特性

    一般,分布式系统需要支持以下特性: 资源共享 开放性 并发性 可伸缩性 容错性 透明性 下面分别讨论. 容易理解的 资源共享 一旦授权,可以访问环境中的任何资源 资源:包括硬件(e.g. printe ...

  7. NOIP模拟测试30「return·one·magic」

    magic 题解 首先原式指数肯定会爆$long$ $long$ 首先根据欧拉定理我们可以将原式换成$N^{\sum\limits_{i=1}^{i<=N} [gcd(i,N)==1] C_{G ...

  8. echarts 根据geojson 数据绘制区域图(精确到镇)

    步骤:1)先获取区域(县.镇)json数据 :2)使用echarts 绘制地图: 先上一波效果图(昆明市东川区) 一.获取区域json数据 1.下载工具"水经微图", 2.下载东川 ...

  9. TCP/UDP/HTTP的区别和联系(转载)

    一.TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据. 关于TCP/IP和HTTP协议的关系,网络有一段比较容易理解的介绍:"我们在传 ...

  10. Leetcode No.121 Best Time to Buy and Sell Stock(c++实现)

    1. 题目 1.1 英文题目 You are given an array prices where prices[i] is the price of a given stock on the it ...