Poly API : VR 및 AR Android 앱의 3D 자산 검색

작가: Peter Berry
창조 날짜: 14 팔월 2021
업데이트 날짜: 8 할 수있다 2024
Anonim
How to Build a Cloud-Connected 3D/AR/VR App
동영상: How to Build a Cloud-Connected 3D/AR/VR App

콘텐츠


VR (Virtual Reality) 또는 AR (Augmented Reality) 모바일 앱에 대한 좋은 아이디어가 있지만 비전을 실현시키는 방법을 모릅니다.

경험이 풍부한 3D 아티스트이기도 한 Android 개발자가 아니라면 몰입 형 360도 경험을 제공하는 데 필요한 모든 자산을 만드는 것은 어려운 과정이 될 수 있습니다.

3D 모델을 만드는 데 필요한 시간, 리소스 또는 경험이 없기 때문에 하지 않습니다 훌륭한 VR 또는 AR 모바일 앱을 만들 수 없다는 것을 의미합니다. 월드 와이드 웹에는 다양한 3D 리소스가 무료로 제공되며 Android 애플리케이션에서 이러한 자산을 다운로드하여 렌더링하는 데 필요한 모든 API, 프레임 워크 및 라이브러리가 있습니다.

다음 읽기: 이제 Daydream VR을 사용하여 모든 웹 사이트를 방문 할 수 있습니다. 그조차도.

이 기사에서는 수천 개의 3D 에셋을 손에 넣는 온라인 저장소 및 API 인 Poly를 살펴 보겠습니다. 이 기사를 마치면 런타임에 3D 폴리 자산을 검색 한 다음 인기있는 Android 용 프로세싱 라이브러리를 사용하여 렌더링하는 앱을 만들게됩니다.

Poly를 사용하여 3D 자산 표시

Unity 개발에 어려움을 겪었다면 Poly 저장소는 Unity Asset Store와 유사합니다. Poly의 모든 것이 무료라는 점만 다릅니다!

많은 Poly 3D 모델은 Creative Commons 라이센스에 따라 게시되므로 제작자에게 적절한 크레딧을 제공하는 한 이러한 자산을 자유롭게 사용, 수정 및 리믹스 할 수 있습니다.

Poly의 모든 3D 모델은 Daydream 및 ARCore와 같은 Google의 VR 및 AR 플랫폼과 호환되도록 설계되었지만 원하는 곳 어디에서나 사용할 수 있습니다. 잠재적으로 Apple의 ARKit에서도 사용할 수 있습니다!


폴리 자산 검색 및 표시와 관련하여 두 가지 옵션이 있습니다. 먼저 자산을 컴퓨터에 다운로드 한 다음 Android Studio로 가져 와서 애플리케이션과 함께 제공하고 APK 크기에 기여하거나 Poly API를 사용하여 런타임에 이러한 자산을 검색 할 수 있습니다.

크로스 플랫폼의 REST 기반 Poly API는 Poly의 방대한 3D 모델 모음에 프로그래밍 방식의 읽기 전용 액세스를 제공합니다. 이는 애셋을 APK에 번들로 묶는 것보다 복잡하지만 런타임에 Poly 애셋을 검색하면 몇 가지 이점이 있습니다. 특히 APK 크기를 제어하여 애플리케이션을 다운로드하는 사람들의 수에 영향을 줄 수 있습니다.

예를 들어 모바일 게임을 개발하는 경우 사용자가 다양한 캐릭터 모델 중에서 선택할 수 있도록 Poly API를 사용하여 사용자에게 더 많은 선택권을 부여 할 수 있습니다.

폴리 모델을 자유롭게 수정할 수 있기 때문에 머리카락이나 눈 색깔을 변경하거나 다른 무기와 갑옷과 같은 다른 폴리 자산과 결합하여 사용자가 선택한 캐릭터를 조정할 수 있습니다. 이러한 방식으로 Poly API를 사용하면 경험을 개인화 할 수있는 다양한 범위와 비교적 적은 작업으로 다양한 3D 자산을 제공 할 수 있습니다. 사용자는이 3D 모델을 모두 세 심하게 제작하여 많은 시간을 보냈다는 확신을 갖게됩니다!

3D 모델링 프로젝트 생성

응용 프로그램을 처음 시작할 때 특정 Poly 자산을 검색 한 다음 사용자 요청에 따라 해당 자산을 전체 화면 모드로 표시하는 응용 프로그램을 만들 것입니다.

이 자산을 검색 할 수 있도록 Kotlin 및 Android 용 HTTP 네트워킹 라이브러리 인 Fuel을 사용합니다. 원하는 설정으로 새 프로젝트를 만드는 것으로 시작하지만 프롬프트가 표시되면 "Kotlin 지원 포함"을 선택하십시오.

Poly API에 대한 모든 호출에는 앱을 식별하고 사용 제한을 시행하는 데 사용되는 API 키가 포함되어야합니다. 개발 및 테스트 중에 무제한 API 키를 사용하는 경우가 많지만이 앱을 출시 할 계획이 있으면 Android 제한 API 키를 사용해야합니다.


제한된 키를 만들려면 프로젝트의 SHA-1 서명 인증서를 알아야하므로 다음 정보를 얻으십시오.

  • Android Studio의 'Gradle'탭 (커서가 다음 스크린 샷에 위치)을 선택하십시오. "Gradle 프로젝트"패널이 열립니다.

  • 'Gradle 프로젝트'패널에서 두 번 클릭하여 프로젝트의 '루트'를 펼친 다음 '작업> Android> 서명 보고서'를 선택하십시오. 그러면 Android Studio 창의 맨 아래에 새 패널이 열립니다.
  • '작업 실행 / 텍스트 모드 전환'버튼 (커서가 다음 스크린 샷에 위치)을 선택하십시오.

“실행”패널이 SHA-1 지문을 포함하여 프로젝트에 대한 많은 정보를 표시하도록 업데이트됩니다.

Google Cloud Platform 계정 만들기

필요한 API 키를 얻으려면 Google Cloud Platform (GPC) 계정이 필요합니다.

계정이없는 경우 무료 클라우드 플랫폼 사용해보기 페이지로 이동하여 지침을 따라 12 개월 무료 평가판에 가입 할 수 있습니다. 신용 카드 또는 직불 카드가 필요하지만 자주 묻는 질문 (FAQ) 페이지에 따르면이 정보는 신원을 확인하는 데만 사용되며 "무료 평가판 기간 동안 요금이 청구되거나 청구되지 않습니다".

Poly API 키 받기

모두 가입하면 Poly API를 활성화하고 키를 만들 수 있습니다.

  • GCP 콘솔로 이동하십시오.
  • 왼쪽 상단에서 줄이 그어진 아이콘을 선택하고 "API 및 서비스> 대시 보드"를 선택하십시오.
  • "API 및 서비스 사용"을 선택하십시오.
  • 왼쪽 메뉴에서 "기타"를 선택하십시오.
  • "Poly API"카드를 선택하십시오.
  • "사용"버튼을 클릭하십시오.
  • 잠시 후 새 화면으로 이동합니다. 사이드 메뉴를 열고 "API 및 서비스> 자격 증명"을 선택하십시오.

  • 다음 팝업에서“제한 키”를 선택하십시오.
  • 키에 고유 한 이름을 지정하십시오.
  • '애플리케이션 제한 사항'에서 'Android 앱'을 선택하십시오.
  • "패키지 이름 및 지문 추가"를 선택하십시오.
  • 프로젝트의 SHA-1 지문을 "서명 인증서 지문"필드에 복사하여 붙여 넣습니다.
  • 프로젝트의 패키지 이름을 입력하십시오 (매니페스트와 모든 클래스 파일 상단에 나타남).
  • “저장”을 클릭하십시오.

이제 방금 만든 Poly-enabled API 키를 포함하여 모든 API 키 목록이 포함 된 프로젝트의 "Credentials"화면으로 이동합니다.

프로젝트 의존성 : 연료, P3D 및 Kotlin 확장

Poly 애셋을 검색하고 표시하려면 몇 가지 추가 라이브러리에서 도움을 받아야합니다.

  • 연료. 현재 Poly에는 공식 Android 툴킷이 없으므로 REST 인터페이스를 사용하여 API로 직접 작업해야합니다. 이 과정을 단순화하기 위해 Fuel HTTP 네트워킹 라이브러리를 사용하겠습니다.
  • 안드로이드에 대한 처리. 이 라이브러리의 P3D 렌더러를 사용하여 폴리 자산을 표시하겠습니다.

프로젝트의 build.gradle 파일을 열고 다음 두 라이브러리를 프로젝트 종속성으로 추가하십시오.

의존성 {구현 fileTree (include :, dir : libs) 구현 "org.jetbrains.kotlin : kotlin-stdlib-jre7 : $ kotlin_version"구현 com.android.support:appcompat-v7:27.1.1 // 연료 라이브러리 추가 / / implementation com.github.kittinunf.fuel : fuel-android : 1.13.0 // Android 엔진 처리 추가 // 구현 org.p5android : processing-core : 4.0.1}

코드를 더 간결하게 만들기 위해 Kotlin Android 확장도 사용하겠습니다. 따라서 build.gradle 파일을 연 상태에서이 플러그인을 추가하겠습니다.

플러그인 적용 : kotlin-android-extensions

마지막으로, 인터넷에서 자산을 검색하기 때문에 앱에 인터넷 권한이 필요합니다. 매니페스트를 열고 다음을 추가하십시오.

API 키 추가

앱이 Poly에서 자산을 요청할 때마다 유효한 API 키를 포함해야합니다. 자리 표시 자 텍스트를 사용하고 있지만 곰팡내 나게 하다 응용 프로그램이 작동하려면이 자리 표시자를 고유 한 API 키로 바꾸십시오.

또한 "INSERT-YOUR-API-KEY"텍스트를 바꾸는 것을 잊었을 때 응용 프로그램에 경고가 표시되도록 확인 표시를 추가하고 있습니다.

import android.os.Bundle import android.support.v7.app.AppCompatActivity 클래스 MainActivity : AppCompatActivity () {companion object {const val APIKey = "INSERT-YOUR-API-KEY"} fun override fun onCreate (savedInstanceState : 번들?) { super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) // API 키가 "INSERT"로 시작하는 경우 ... // if (APIKey.startsWith ( "INSERT")) {// 다음 토스트를 표시합니다… .// Toast.makeText (이, "API 키를 업데이트하지 않았습니다", Toast.LENGTH_SHORT) .show ()} else {... ... ...

자산 검색

Google 폴리 사이트에서 자산을 선택할 수 있지만이 지구 모델을 사용하겠습니다.

URL 슬러그 끝에 표시되는 ID를 사용하여 자산을 검색합니다 (이전 스크린 샷에서 강조 표시됨). 이 자산 ID를 Poly API 호스트 인 "https://poly.googleapis.com/v1"과 결합합니다.

import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.http 가져 오기 kotlinx.android.synthetic.main.activity_main. * import java.io.File class MainActivity : AppCompatActivity () {companion object {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL ="https://poly.googleapis.com/v1/assets/94XG1XUy10q "} fun 재미 재정의 onCreate (savedInstanceState : Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ( "INSERT")) {Toast.makeText (this, "API 키를 업데이트하지 않았습니다", Toast.LENGTH_SHORT) .show ()} else {

다음으로, httpGet () 메소드를 사용하여 자산 URL에 GET 요청을해야합니다. 또한 응답 유형이 JSON이어야한다고 지정하고 있습니다.

import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.http 가져 오기 kotlinx.android.synthetic.main.activity_main. * import java.io.File class MainActivity : AppCompatActivity () {companion object {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL ="https://poly.googleapis.com/v1/assets/94XG1XUy10q "} fun 재미 재정의 onCreate (savedInstanceState : Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ( "INSERT")) {Toast.makeText (this, "API 키를 업데이트하지 않았습니다", Toast.LENGTH_SHORT) .show ()} else {// 서버 호출을 한 다음 "listOf"메소드 // assetURL.httpGet (listOf ( "key"to APIKey)). responseJson {요청, 응답, 결과-> // response // result.fold ({val 설정 = it.obj ()

자산에는 OBJ, GLTF 및 FBX와 같은 여러 형식이있을 수 있습니다. 자산이 OBJ 형식인지 확인해야합니다.

이 단계에서는 다운로드해야하는 모든 파일의 이름과 URL도 검색합니다.
에셋의 기본 파일 ( "루트")과 관련 재질 및 텍스처 파일 ( "자원")을 포함합니다.

애플리케이션이 자산을 올바르게 검색 할 수없는 경우 사용자에게 알림을 표시합니다.

import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.http 가져 오기 kotlinx.android.synthetic.main.activity_main. * import java.io.File class MainActivity : AppCompatActivity () {companion object {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL ="https://poly.googleapis.com/v1/assets/94XG1XUy10q "} fun 재미 재정의 onCreate (savedInstanceState : Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ( "INSERT")) {Toast.makeText (this, "API 키를 업데이트하지 않았습니다", Toast.LENGTH_SHORT) .show ()} else {// 자산 URL // assetURL에 GET 요청을합니다. httpGet (listOf ( "key"to APIKey)). responseJson {요청, 응답, 결과-> // 응답 // result.fold ({val asset = it.obj () var objectURL : String? = null var materialLibraryName : 문자열? = null var materialLibraryURL : 문자열? = null //“formats”배열을 사용하여 자산의 형식을 확인합니다 .// val assetFormats = asset.getJSONArray ( "formats") // for 모든 형식을 반복합니다 // (i는 0까지 assetFormats.length ()까지) { val currentFormat = assetFormats.getJSONObject (i) // formatType을 사용하여이 자원의 형식 유형을 식별하십시오. 형식이 OBJ… .// if (currentFormat.getString ( "formatType") == "OBJ") {// ...이 자원의 '루트'파일, 즉 OBJ 파일 // objectURL = currentFormat을 검색합니다. getJSONObject ( "root") .getString ( "url") // 모든 루트 파일의 종속성 검색 // materialLibraryName = currentFormat.getJSONArray ( "resources") .getJSONObject (0) .getString ( "relativePath") materialLibraryURL = currentFormat.getJSONArray ( "resources") .getJSONObject (0) .getString ( "url") break}} objectURL !!. httpDownload (). destination {_, _-> File (filesDir, "globeAsset.obj")} .response {_ , _, result-> result.fold ({}, {// OBJ 파일을 찾거나 다운로드 할 수없는 경우 오류 // // Toast.makeText (this, "자원을 다운로드 할 수 없음", Toast.LENGTH_SHORT ) .show ()})} materialLibraryURL !!. httpDownload (). destination {_, _-> 파일 (filesDir, materialLibraryName)} .response {_, _, result-> result.fold ({}, {Toast. makeText (this, "리소스를 다운로드 할 수 없습니다", Toast.LENGTH_SHORT) .show ()})}}, { Toast.makeText (this, "리소스를 다운로드 할 수 없습니다", Toast.LENGTH_SHORT) .show ()})}}}

이 시점에서 Android 스마트 폰이나 태블릿 또는 AVD (Android Virtual Device)에 프로젝트를 설치하면 자산이 성공적으로 다운로드되지만 실제로는 앱이 표시되지 않습니다. 이 문제를 해결하겠습니다!

두 번째 화면 만들기 : 탐색 추가

애셋을 전체 화면 모드로 표시 할 것이므로 main_activity.xml 파일을 업데이트하여 탭하면 전체 화면 액티비티를 시작하는 버튼을 포함시켜 보겠습니다.

이제 MainActivity.kt 파일 끝에 onClickListener를 추가해 보겠습니다.

import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Toast import com.github.kittinunf.fuel.android.extension.responseJson import com.github.kittinunf.fuel .httpDownload import com.github.kittinunf.fuel.http 가져 오기 kotlinx.android.synthetic.main.activity_main. * import java.io.File class MainActivity : AppCompatActivity () {companion object {const val APIKey = "INSERT-YOUR-API -KEY "val assetURL ="https://poly.googleapis.com/v1/assets/94XG1XUy10q "} fun 재미 재정의 onCreate (savedInstanceState : Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_main) if ( APIKey.startsWith ( "INSERT")) {Toast.makeText (this, "API 키를 업데이트하지 않았습니다", Toast.LENGTH_SHORT) .show ()} else {assetURL.httpGet (listOf ( "key"to APIKey)). responseJson {요청, 응답, 결과-> result.fold ({val asset = it.obj () var objectURL : String? = null var materialLibraryName : 문자열? = null var materialLibraryURL : Str 노래? = null val assetFormats = asset.getJSONArray ( "formats") for (i ~ assetFormats.length ()까지) {val currentFormat = assetFormats.getJSONObject (i) if (currentFormat.getString ( "formatType") == "OBJ" ) {objectURL = currentFormat.getJSONObject ( "root") .getString ( "url") materialLibraryName = currentFormat.getJSONArray ( "resources") .getJSONObject (0) .getString ( "relativePath") materialLibraryURL = currentFormat.getJSONArray ( "resources" ) .getJSONObject (0) .getString ( "url") break}} objectURL !!. httpDownload (). destination {_, _-> File (filesDir, "globeAsset.obj")} .response {_, _, 결과 -> result.fold ({}, {Toast.makeText (this, "리소스를 다운로드 할 수 없습니다", Toast.LENGTH_SHORT) .show ()})} materialLibraryURL !!. httpDownload (). destination {_, _-> 파일 (filesDir, materialLibraryName)} .response {_, _, result-> result.fold ({}, {Toast.makeText (this, "리소스를 다운로드 할 수 없음", Toast.LENGTH_SHORT) .show ()})}}, {Toast.makeText (이, "리소스를 다운로드 할 수 없습니다", Toast.LENGTH_SHORT) .sh ow ()})} // 버튼 구현 //DisplayButton.setOnClickListener {val intent = Intent (this, SecondActivity :: class.java) startActivity (intent); }}}

3D 캔버스 제작

이제 애셋을 전체 화면 모드로 표시 할 액티비티를 만들어 보겠습니다.

  • 프로젝트의 MainActivity.kt 파일을 Control- 클릭하고“신규> Kotlin 파일 / 클래스”를 선택하십시오.
  • '종류'드롭 다운을 열고 '클래스'를 선택하십시오.
  • 이 클래스에 "SecondActivity"라는 이름을 지정한 다음 "확인"을 클릭하십시오.

3D 객체를 그리려면 3D 캔버스가 필요합니다! PApplet 클래스를 확장하고 settings () 메서드를 재정의 한 다음 P3D를 fullScreen () 메서드에 인수로 전달하는 것을 의미합니다. 또한 폴리 자산을 PShape 객체로 나타내는 속성을 만들어야합니다.

private fun displayAsset () {val canvas3D = object : PApplet () {var polyAsset : PShape? = null 재정의 재미 설정 () {fullScreen (PConstants.P3D)}

다음으로 setup () 메서드를 재정의하고 loadShape () 메서드를 호출 한 다음 .obj 파일의 절대 경로를 전달하여 PShape 개체를 초기화해야합니다.

fun 설정 무시 () {polyAsset = loadShape (File (filesDir, "globeAsset.obj"). absolutePath)}

P3D의 캔버스에 그리기

이 3D 캔버스에 그리려면 draw () 메서드를 재정의해야합니다.

fun ()를 재정의 (draw {) {background (0) shape (polyAsset)}}

기본적으로 Poly API에서 검색된 많은 자산이 더 작은쪽에 있으므로이 코드를 지금 실행하면 화면 구성에 따라 자산이 표시되지 않을 수도 있습니다. 3D 장면을 만들 때 일반적으로 사용자가 장면을 탐색하고 3D 자산을 360도 전체에서 볼 수 있도록 맞춤형 카메라를 만듭니다. 그러나이 기사의 범위를 벗어나므로 화면에 편안하게 맞도록 자산의 크기와 위치를 수동으로 변경하겠습니다.

scale () 메서드에 음수 값을 전달하여 자산의 크기를 늘릴 수 있습니다.

규모 (-10f)

translate () 메소드와 다음 좌표를 사용하여 가상 3D 공간에서 자산의 위치를 ​​조정할 수 있습니다.

  • 엑스. 가로 축을 따라 자산을 배치합니다.
  • 와이. 자산을 세로 축을 따라 배치합니다.
  • 지. 이것은 "깊이 / 높이"축으로 2D 객체를 3D 객체로 변환합니다. 양수 값은 객체가 자신쪽으로 향하는 느낌을 만들고 음수 값은 객체가 몸에서 멀어지는 느낌을 만듭니다.

변환은 누적되므로 함수 이후에 발생하는 모든 것이 효과를 누적합니다.

나는 다음을 사용하고 있습니다 :

번역 (-50f, -100f, 10f)

완성 된 코드는 다음과 같습니다.

fun ()을 재정의 함 draw () {background (0) scale (-10f) translate (-50f, -100f) // shape () 메서드를 호출하여 자산을 그립니다 // shape (polyAsset)}}

다음으로 3D 캔버스를 FrameLayout 위젯으로 추가 할 해당 레이아웃 파일을 만들어야합니다.

  • 프로젝트의 "res> layout"폴더를 control- 클릭하십시오.
  • “레이아웃 리소스 파일”을 선택하십시오.
  • 이 파일 이름을 "activity_second"로 지정한 다음 "확인"을 클릭하십시오.

이제 우리는“asset_view”FrameLayout을 가지고 있으며 SecondActivity에 알려야합니다! SecondActivity.kt 파일로 되돌아 가서 새 PFragment 인스턴스를 만들고 "asset_view"위젯 방향으로 지정하십시오.

import android.os. 번들 import android.support.v7.app.AppCompatActivity import kotlinx.android.synthetic.main.activity_second. * 가져 오기 processing.android.PFragment 가져 오기 processing.core.PApplet 가져 오기 processing.core.PConstants 가져 오기 처리 .core .PShape import java.io.File 클래스 SecondActivity : AppCompatActivity () {override fun onCreate (savedInstanceState : Bundle?) {super.onCreate (savedInstanceState) setContentView (R.layout.activity_second) displayAsset ()} private fun displayAsset () {val canvas3D = 객체 : PApplet () {var polyAsset : PShape? = null 대체 재미 설정 () {fullScreen (PConstants.P3D)} 대체 재미 setup () {polyAsset = loadShape (File (filesDir, "globeAsset.obj"). absolutePath)} 대체 재미 draw () {배경 (0) 스케일 (-10f) translate (-50f, -100f) shape (polyAsset)}} // 다음 // val assetView = PFragment (canvas3D) assetView.setView (asset_view, this)}} 추가

마지막 단계는 매니페스트에 SecondActivity를 추가하는 것입니다.

// 다음을 추가 //

프로젝트 테스트

이제 완성 된 프로젝트를 테스트 할 준비가되었습니다! Android 기기 또는 AVD에 설치하고 인터넷에 연결되어 있는지 확인하십시오. 앱이 시작 되 자마자 자산을 다운로드 한 다음 "자산 표시"버튼을 탭하여 자산을 볼 수 있습니다.

이 완전한 프로젝트는 GitHub에서 다운로드 할 수 있습니다.

마무리

이 기사에서는 런타임시 Poly API를 사용하여 3D 자산을 검색하는 방법과 Processing for Android 라이브러리를 사용하여 해당 자산을 표시하는 방법을 살펴 보았습니다. Poly API가 더 많은 사람들이 VR 및 AR 개발에 액세스 할 수있는 잠재력을 가지고 있다고 생각하십니까? 아래 의견에 알려주십시오!

관련

  • Google은 2018 년에 AR 앱을 수백 만 대의 Android 기기에 제공 할 예정입니다.
  • Google은 AI 및 머신 러닝에 대해 무료로 가르쳐드립니다.
  • Google Cardboard를위한 최고의 VR 게임 15 가지
  • Google Cardboard를위한 10 가지 최고의 VR 앱
  • Google Fuchsia 란 무엇입니까? 이것이 새로운 안드로이드입니까?
  • Google Duplex 란 무엇입니까? — 기능, 출시 날짜 등
  • 단 7 분만에 Android 용 VR 앱을 만드는 방법
  • 모바일 VR 헤드셋 – 최고의 옵션은 무엇입니까?

에이 기억에 남는 브랜드 비즈니스 나 웹 사이트의 성공을 향해 치 솟을 수 있습니다. 그러나 전문 디자이너가 아니라면 제대로하는 것은 쉬운 일이 아닙니다.오늘의 거래는 수년간의 교육 없이도 내부 아티스트를 잠금 해제 할 수 있습니다. CreativeNaut 브랜딩 도구 번들은 최고의 기업가 적 패키지 주목받는 신생 기업을 위해 지금 당장 원하는 가격을 지불...

Creative Outlier Air 리뷰 : 저렴한 기능

Lewis Jackson

할 수있다 2024

각 이어 버드에는 연결 상태를 나타내는 LED 링이 있습니다.UB-C 충전 케이스에서 이어 버드에 이르기까지 Creative Outlier Air에 대한 모든 것은 가볍습니다. 처음에는 이어 버드 크기가 너무 커서 걱정했습니다. 그러나 각진 노즐과 실리콘 슬리브 어레이 덕분에 안전하다는 것이 입증되었습니다. 물론, 이어 버드의 경사 모양으로 인해 안전하게 ...

흥미로운 출판물