如今,对于提供沉浸式虚拟现实或增强现实体验的Android应用程序有巨大的需求。 作为开发人员,可以使用许多不同的框架来创建此类应用程序。
但是,除非您也是熟练的3D美术师,否则如何创建将在这些应用程序中显示的3D对象? 您准备好花几个月的时间学习如何与3D建模程序(例如Blender或Maya)一起使用吗? 如果不是这样,则应考虑使用Google Poly ,这是一个在线存储库,其中包含数千种带有Creative Commons许可的3D资源。
您今天可以在Poly上找到的大多数资产都是具有简单材料的低多边形。 这是因为普通的移动GPU尚未强大到足以实时显示具有高多边形数的3D对象。
在本教程中,我将向您介绍Poly API。 我还将向您展示如何使用Android的Processing处理您下载的3D资源。
先决条件
要充分利用本教程,您需要:
- 最新版本的Android Studio
- 运行Android API级别21或更高版本的设备
- 和一个Google Cloud帐户
1.获取API密钥
您对Poly API发出的所有HTTP请求都必须带有属于您的API密钥。 要获取密钥,请先登录Google Cloud 控制台并导航至API仪表板。
接下来,按启用API和服务按钮,展开其他类别,然后选择Poly API 。
现在,您可以按启用按钮来启用Poly API。
启用API后,会自动为其生成一组API密钥。 您可以打开“ 凭据”选项卡来查看它们。
对于本教程,您只需要Android key即可 。 记下它,以便以后使用。
2.项目设置
由于Poly当前没有适用于Android平台的官方工具包,因此您必须直接通过其REST接口使用Poly API。 通过使用针对Kotlin语言优化的Fuel网络库,您可以节省大量时间和精力。 因此,在app
模块的build.gradle文件中添加以下implementation
依赖项 :
implementation 'com.github.kittinunf.fuel:fuel-android:1.13.0'
为了能够显示从Poly资料库下载的3D资源,您还需要一个渲染引擎。 Android的处理程序附带一个,因此将其添加为另一个依赖项。
implementation 'org.p5android:processing-core:4.0.1'
最后,不要忘记在项目的清单文件中请求INTERNET
权限。
<uses-permission android:name="android.permission.INTERNET"/>
3.上市资产
要下载Poly资产,您必须知道其唯一ID。 通过使用支持WebGL的浏览器,您可以轻松确定任何资产的ID。 在地址栏中。
但是,如果要允许用户在运行时动态确定他们要使用的资产,则可以使用assets.list
REST方法确定这些资产的ID。 该方法允许您使用各种参数(例如关键字,类别和3D文件格式)查找资产。
为了举例说明,现在让我们尝试查找属于animals
类别的一些资产的ID。 当然,您可以自由选择任何其他有效类别,例如architecture
, food
或people
。
在编写HTTP请求之前,最好将API密钥和Poly API的基本URL声明为活动中的常量。
companion object {
const val key = "Abcdefghabcdefgh1234567810"
const val baseURL = "https://poly.googleapis.com/v1"
}
使用基本URL,可以构建assets.list
REST方法的URL,如下所示:
val listURL = "$baseURL/assets"
此时,您可以通过调用httpGet()
方法并将API密钥和所需的类别作为查询参数传递给它来创建有效的HTTP GET请求。 (可选)您可以使用format
查询参数来指定所需的资产格式。 Poly支持OBJ,FBX,TILT和其他几种流行的3D格式。
因为该方法异步运行,并且其结果是JSON文档,所以您必须使用responseJSON()
方法将事件处理程序附加到该方法。 以下代码向您展示了如何:
listURL.httpGet(listOf(
"category" to "animals",
"key" to key,
"format" to "OBJ"
)).responseJson { _, _, result ->
// More code here
}
在事件处理程序内部,如果请求成功,则可以访问Asset
对象的列表。 每个此类对象的name
字段均指定其ID。
此外,每个对象都将具有诸如displayName
, license
和authorName
,您可能会发现它们有用。 现在,让我们简单地打印所有对象的name
和displayName
。 以下代码向您展示了如何:
result.fold({
// Get assets array
val assets = it.obj().getJSONArray("assets")
// Loop through array
for(i in 0 until assets.length()) {
// Get id and displayName
val id = assets.getJSONObject(i).getString("name")
val displayName =
assets.getJSONObject(i).getString("displayName")
// Print id and displayName
Log.d("POLY", "(ID: $id) -- (NAME: $displayName)")
}
}, {
// In case of an error
Log.e("POLY", "An error occurred")
})
如果您现在运行应用程序,则应该能够在Android Studio的Logcat窗口中看到以下输出。
4.下载资产
拥有资产的唯一ID后,您可以将其直接附加到Poly API的基本URL上以创建资产URL。
// some asset id
val assetID = "assets/3yiIERrKNQr"
// its url
val assetURL = "$baseURL/$assetID"
再次使用httpGet()
方法对资产URL进行HTTP GET请求时,您将获得一个仅包含一个Asset
对象的JSON文档。
assetURL.httpGet(listOf("key" to key))
.responseJson { _, _, result ->
result.fold({
val asset = it.obj()
// More code here
}, {
Log.e("POLY", "An error occurred")
})
}
正如您在上面的代码中可能已经注意到的那样,此请求也必须具有提及您的API密钥的查询参数。
在上一步中,您已经学习了如何使用Asset
对象中存在的某些字段。 现在,您所需要做的就是使用对象中存在的formats
数组确定与资产关联的文件的URL和名称。 数组中的每个项目将具有三个重要字段:
-
formatType
,它使您可以确定资产的类型 -
root
,其中包含与资产关联的主文件的名称和URL -
resources
,其中包含有关与资产关联的所有辅助文件的详细信息,例如材质和纹理
如果使用OBJ格式,则主文件将是包含顶点和面数据的.obj文件,而辅助文件通常将是包含有关所用材质数据的.mtl文件。 以下代码显示了如何确定主文件和辅助文件的URL:
var objFileURL:String? = null
var mtlFileURL:String? = null
var mtlFileName:String? = null
val formats = asset.getJSONArray("formats")
// Loop through all formats
for(i in 0 until formats.length()) {
val currentFormat = formats.getJSONObject(i)
// Check if current format is OBJ
if(currentFormat.getString("formatType") == "OBJ") {
// Get .obj file details
objFileURL = currentFormat.getJSONObject("root")
.getString("url")
// Get the first .mtl file details
mtlFileURL = currentFormat.getJSONArray("resources")
.getJSONObject(0)
.getString("url")
mtlFileName = currentFormat.getJSONArray("resources")
.getJSONObject(0)
.getString("relativePath")
break
}
}
在上面的代码中,除了.mtl文件的URL外,我们还使用relativePath
字段确定其名称。 这样做很重要,因为名称被硬编码到.obj文件的mtllib
元素中,并且不应更改。
一旦获得了两个文件的URL,就可以使用Fuel库的httpDownload()
方法下载它们。 将它们下载到应用程序的专用存储目录中的方法如下,其绝对路径可以使用filesDir
属性确定:
// download and store obj file as asset.obj
objFileURL!!.httpDownload().destination { _, _ ->
File(filesDir, "asset.obj")
}.response { _, _, result ->
result.fold({}, {
Log.e("POLY", "An error occurred")
})
}
// download and store mtl file without
// changing its name
mtlFileURL!!.httpDownload().destination { _, _ ->
File(filesDir, mtlFileName)
}.response { _, _, result ->
result.fold({}, {
Log.e("POLY", "An error occurred")
})
}
5.显示资产
您将需要3D画布来绘制下载的Poly资产。 要创建一个,您必须扩展Processing for Android库提供的PApplet
类。 但是,默认情况下,以这种方式创建的画布仅支持2D形状。 要将其配置为也绘制3D形状,请覆盖settings()
方法并将P3D
作为参数传递给fullScreen()
方法,这也会使画布与用户的屏幕一样大。
val canvas = object : PApplet() {
override fun settings() {
fullScreen(PConstants.P3D)
}
// More code here
}
接下来,在类内部创建一个新属性,将Poly资产表示为PShape
对象。
var myPolyAsset: PShape? = null
要初始化该属性,请覆盖setup()
方法并调用loadShape()
方法,将下载的.obj文件的绝对路径作为参数传递给它。
override fun setup() {
myPolyAsset = loadShape(File(filesDir, "asset.obj").absolutePath)
}
现在,您可以通过重写draw()
方法开始在画布上draw()
。 在该方法内部,您需要做的第一件事是调用background()
方法,以确保始终在空白画布上绘制。
override fun draw() {
background(0)
// More code here
}
直接绘制时,大多数Poly资产看起来很小而且倒置。 您可以通过使用自定义相机或使用画布转换来解决此问题。 为了使本教程简单直观,让我们使用画布转换。
要增加资产的大小,请使用scale()
方法并将较大的负值传递给它。 该值必须为负,以确保资产垂直翻转。 (可选)您可以使用translate()
方法沿X和Y轴调整其位置。 以下代码向您展示了如何:
scale(-50f)
translate(-4f,-14f)
现在,您可以继续通过调用shape()
方法来绘制资产。
shape(myPolyAsset)
画布当前不属于活动的视图层次结构的一部分。 因此,如果您尝试立即运行应用程序,将无法看到资产。 要解决此问题,请首先将一个新的FrameLayout
小部件添加到活动的布局XML文件中。
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/canvas_holder">
</FrameLayout>
然后,使用画布创建一个新的PFragment
实例,并将其指向FrameLayout
小部件。
val fragment = PFragment(canvas)
fragment.setView(canvas_holder, this)
此时,您可以再次运行该应用程序以查看资产。
结论
现在,您知道如何使用Poly API搜索和下载3D资源。 在本教程中,您还学习了如何使用Android的Processing处理和渲染这些资产。
值得注意的是,Poly上的许多资产都是使用Google Blocks创建的,Google Blocks是HTC Vive和Oculus Rift用户可以使用的应用程序。 如果您拥有这些VR耳机,请考虑创建并提交自己的模型。
要了解有关Poly API的更多信息,可以参考官方文档 。