【Hololens開發】Holograms 101: Introduction with Device(使用Hololens)
本文我們將通過Unity建立一個包括:凝視(gaze)、手勢(gesture)、語音輸入(voice)、空間聲音(spatial sound)和空間對映(spatial mapping)的完整的 Hololens 專案,並直接在 Hololens 上執行。
譯者注:本文與 Holograms 101E 類似,區別在於部署部分,本文 101 是使用Hololens,101E 是使用模擬器。
點選檢視原文
目錄:
Chapter0 - 預先準備
開發環境
一個 Hololens 裝置。
專案檔案
Chapter1 - “Holo” World
在這個章節中,我們將新建一個Unity專案,並且走一遍build和deploy的流程。
- 準備
- 開啟 Unity。
- 點選 Open。
- 找到你之前解壓的 Origami 資料夾。
- 選中 Origami,然後點選 Select Folder。
- 儲存當前 Scene : File > Save Scene As。
- 把 Scene 命名為 Origami ,然後單擊 Save。
- 設定 Main Camera
- 選中 Main Camera。
- 設定其 Transform 元件中 Position
- 找到 Clear Flags 屬性, 把其值從 Skybox 改為 Solid color。
- 修改 Background 屬性,其顏色 RGBA 為(0, 0, 0, 0)。
- 修改 Clipping Planes 屬性的 Near 值為 0.85。(譯者注:參考Holograms 100)
- 設定場景
- 在 Hierarchy 中, 單擊 Create > Create Empty。
- 右鍵單擊新的 GameObject 選擇 Rename。 將 GameObject 重新命名為 OrigamiCollection。
- 從 Project 面板中的 Holograms 資料夾裡:
- 拖拽 Stage
- 拖拽 Sphere1 進入 Hierarchy, 並作為 OrigamiCollection 的子物體。
- 拖拽 Sphere2 進入 Hierarchy, 並作為 OrigamiCollection 的子物體。
- 拖拽 Stage
- 右鍵單擊 Directional Light 物體,選擇 Delete 刪除。
- 從 Holograms 資料夾中, 拖拽 Lights 到 Hierarchy 面板的根部。
- 選中 OrigamiCollection。
- 修改其 Transform 元件中的 Position 屬性為 (0, -0.5, 2.0)。
- 單擊 Play 按鈕,看看現在效果如何。
- 再次單擊 Play 按鈕,退出預覽模式。
- 匯出專案到Visual Studio 2015
- 選擇 File > Build Settings。
- 把 執行平臺 即 Platform 修改為 Windows Store 並且單擊 Switch Platform。.
- 設定 SDK 為 Universal 10 ,Build Type 為 D3D。
- UWP SDK 可以選 Latest installed。(譯者注:最好與你裝VS2015時安裝的那個版本一致,否則VS會提示專案需要更新)
- 勾上 Unity C# Projects。
- 單擊 Add Open Scenes ,添加當前場景.
- 單擊 Build
- 新建一個資料夾,命名為 APP。
- 單擊選擇 App 資料夾。
- 單擊 Select Folder 按鈕。
- 當Unity 完成 Building 的時候,會自動開啟一個資源管理器。
- 開啟 APP 資料夾。
- 開啟生成的 Visual Studio Solution。
- 在VS工具欄中,把 target 從 Debug 改為 Release,從 ARM 改為 x86。
- 單擊 本地計算機(Local Machine) 旁邊的小箭頭,將部署目標變為 Remote Machine。
- 設定 Address 值為你的 Hololens 的 name 或者 IP address。如果你不知道自己的 Hololens的 IP address,在 Hololens 中開啟 Settings > Network & Internet > Advanced Options檢視,或者直接詢問 Cortana:”Hey Cortana, What’s my IP address?”。
- 如果你是通過USB連線 Hololens 的,你應該將部署目標選擇為 Device,而不是 Remote Machine。
- Authentication Mode 選擇為 Universal。
- 點選 Select。
- 點選 除錯(Debug) > 開始執行不除錯(Debug without debugging)
- 等待一會後,就可以戴上你的 Hololens 開始體驗了。
Hololens Emulator 效果圖
Chapter 2 - Gaze(凝視)
在這個章節中,我們將介紹Hololens中的三種互動方式之一,Gaze(凝視)。
- 從 Holograms 資料夾中拖拽 Cursor 物體進入 Hierarchy。
- 在 Scripts 資料夾中新建一個 Script,命名為 WorldCursor。
- 把 WorldCursor 新增給 Cursor。
- 雙擊 WorldCursor ,在VS中編輯指令碼。
- 複製並貼上以下指令碼,並儲存。
using UnityEngine;
public class WorldCursor : MonoBehaviour
{
private MeshRenderer meshRenderer;
// Use this for initialization
void Start()
{
// Grab the mesh renderer that's on the same object as this script.
meshRenderer = this.gameObject.GetComponentInChildren<MeshRenderer>();
}
// Update is called once per frame
void Update()
{
// Do a raycast into the world based on the user's
// head position and orientation.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
{
// If the raycast hit a hologram...
// Display the cursor mesh.
meshRenderer.enabled = true;
// Move thecursor to the point where the raycast hit.
this.transform.position = hitInfo.point;
// Rotate the cursor to hug the surface of the hologram.
this.transform.rotation = Quaternion.FromToRotation(Vector3.up, hitInfo.normal);
}
else
{
// If the raycast did not hit a hologram, hide the cursor mesh.
meshRenderer.enabled = false;
}
}
}
- 再次build該專案,並在 VS 中部署到 Hololens 中。
Gaze 效果圖 注意圖中箭頭所指
Chapter3 - Gestures(手勢)
在本章節中,我們將新增手勢控制的功能。當用戶選擇到一個場景中的紙球時,我們將使紙球基於Unity中的物理引擎下落。
- 在 Scripts 資料夾下,新建立一個指令碼 GazeGestureManager。
- 把指令碼 GazeGestureManager 新增給 OrigamiCollection 物體。
- 編輯 GazeGestureManager 指令碼,新增以下程式碼:
using UnityEngine;
using UnityEngine.VR.WSA.Input;
public class GazeGestureManager : MonoBehaviour
{
public static GazeGestureManager Instance { get; private set; }
// Represents the hologram that is currently being gazed at.
public GameObject FocusedObject { get; private set; }
GestureRecognizer recognizer;
// Use this for initialization
void Start()
{
Instance = this;
// Set up a GestureRecognizer to detect Select gestures.
recognizer = new GestureRecognizer();
recognizer.TappedEvent += (source, tapCount, ray) =>
{
// Send an OnSelect message to the focused object and its ancestors.
if (FocusedObject != null)
{
FocusedObject.SendMessageUpwards("OnSelect");
}
};
recognizer.StartCapturingGestures();
}
// Update is called once per frame
void Update()
{
// Figure out which hologram is focused this frame.
GameObject oldFocusObject = FocusedObject;
// Do a raycast into the world based on the user's
// head position and orientation.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo))
{
// If the raycast hit a hologram, use that as the focused object.
FocusedObject = hitInfo.collider.gameObject;
}
else
{
// If the raycast did not hit a hologram, clear the focused object.
FocusedObject = null;
}
// If the focused object changed this frame,
// start detecting fresh gestures again.
if (FocusedObject != oldFocusObject)
{
recognizer.CancelGestures();
recognizer.StartCapturingGestures();
}
}
}
- 再新建一個指令碼,命名為 SphereCommands。
- 將指令碼 SphereCommands 新增給 Sphere1 和 Sphere2 物體。
- 編輯 SPhereCommands 指令碼,新增以下程式碼:
using UnityEngine;
public class SphereCommands : MonoBehaviour
{
// Called by GazeGestureManager when the user performs a Select gesture
void OnSelect()
{
// If the sphere has no Rigidbody component, add one to enable physics.
if (!this.GetComponent<Rigidbody>())
{
var rigidbody = this.gameObject.AddComponent<Rigidbody>();
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
}
}
}
- 匯出專案,部署到 Hololens 上進行測試。
- 凝視一個紙球。
- 做出選擇的手勢。觀察效果,小球落下。
Chapter4 - Voice 聲音控制
在本章節中,我們將新增兩種聲音控制命令:
- “Reset World”:讓已經降落的小球回到一開始的位置。
- “Drop Sphere”:讓小球降落
- 首先,新建一個指令碼 SpeechManager。
- 把指令碼 SpeechManager 新增給 OrigamiCollection 物體。
- 開啟指令碼 SpeechManager 並新增如下程式碼:
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Windows.Speech;
public class SpeechManager : MonoBehaviour
{
KeywordRecognizer keywordRecognizer = null;
Dictionary<string, System.Action> keywords = new Dictionary<string, System.Action>();
// Use this for initialization
void Start()
{
keywords.Add("Reset world", () =>
{
// Call the OnReset method on every descendant object.
this.BroadcastMessage("OnReset");
});
keywords.Add("Drop Sphere", () =>
{
var focusObject = GazeGestureManager.Instance.FocusedObject;
if (focusObject != null)
{
// Call the OnDrop method on just the focused object.
focusObject.SendMessage("OnDrop");
}
});
// Tell the KeywordRecognizer about our keywords.
keywordRecognizer = new KeywordRecognizer(keywords.Keys.ToArray());
// Register a callback for the KeywordRecognizer and start recognizing!
keywordRecognizer.OnPhraseRecognized += KeywordRecognizer_OnPhraseRecognized;
keywordRecognizer.Start();
}
private void KeywordRecognizer_OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
System.Action keywordAction;
if (keywords.TryGetValue(args.text, out keywordAction))
{
keywordAction.Invoke();
}
}
}
- 開啟指令碼 SphereCommands ,更新程式碼如下:
using UnityEngine;
public class SphereCommands : MonoBehaviour
{
Vector3 originalPosition;
// Use this for initialization
void Start()
{
// Grab the original local position of the sphere when the app starts.
originalPosition = this.transform.localPosition;
}
// Called by GazeGestureManager when the user performs a Select gesture
void OnSelect()
{
// If the sphere has no Rigidbody component, add one to enable physics.
if (!this.GetComponent<Rigidbody>())
{
var rigidbody = this.gameObject.AddComponent<Rigidbody>();
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
}
}
// Called by SpeechManager when the user says the "Reset world" command
void OnReset()
{
// If the sphere has a Rigidbody component, remove it to disable physics.
var rigidbody = this.GetComponent<Rigidbody>();
if (rigidbody != null)
{
DestroyImmediate(rigidbody);
}
// Put the sphere back into its original local position.
this.transform.localPosition = originalPosition;
}
// Called by SpeechManager when the user says the "Drop sphere" command
void OnDrop()
{
// Just do the same logic as a Select gesture.
OnSelect();
}
}
- 匯出專案,部署到 Hololens 上進行測試。
- 凝視一個小球。然後說:”Drop Sphere!”
- 說 “Reset World” 讓小球回到原來的位置。
Chapter5 - Spatial sound 空間聲音
在本章節中,我們將為應用新增音樂,並給關鍵動作新增音效。我們將使用 Spatial sound 來給聲音一個三維空間中具體的方位。
- 在 Unity Editor 中,選擇 Edit > Project Settings > Audio。
- 找到 Spatializer Plugin 選項並選擇 MS HRTF Spatializer。
- 從 Holograms 資料夾拖拽 Ambience 物體到 Hierarchy 面板中的 OrigamiCollection 物體上。
- 選中 OrigamiCollection 物體,找到 Audio Source 元件,修改以下屬性:
- 勾選 Spatialize
- 勾選 Play on Awake
- 把 Spatial Blend 的滑塊拖到最右,使其值為 3D。
- 勾選 Loop。
- 展開 3D Sound Settings,把 Doppler Level 的值修改為 0.1。
- 把 Volume Rolloff 設定為 Logarithmic Rolloff。
- 把 Max Distance 設定為 20。
- 新建一個指令碼命名為 SphereSounds。
- 把指令碼 SphereSounds 新增給 Sphere1 和Sphere2。
- 開啟指令碼新增如下程式碼:
using UnityEngine;
public class SphereSounds : MonoBehaviour
{
AudioSource audioSource = null;
AudioClip impactClip = null;
AudioClip rollingClip = null;
bool rolling = false;
void Start()
{
// Add an AudioSource component and set up some defaults
audioSource = gameObject.AddComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.spatialize = true;
audioSource.spatialBlend = 1.0f;
audioSource.dopplerLevel = 0.0f;
audioSource.rolloffMode = AudioRolloffMode.Logarithmic;
audioSource.maxDistance = 20f;
// Load the Sphere sounds from the Resources folder
impactClip = Resources.Load<AudioClip>("Impact");
rollingClip = Resources.Load<AudioClip>("Rolling");
}
// Occurs when this object starts colliding with another object
void OnCollisionEnter(Collision collision)
{
// Play an impact sound if the sphere impacts strongly enough.
if (collision.relativeVelocity.magnitude >= 0.1f)
{
audioSource.clip = impactClip;
audioSource.Play();
}
}
// Occurs each frame that this object continues to collide with another object
void OnCollisionStay(Collision collision)
{
Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();
// Play a rolling sound if the sphere is rolling fast enough.
if (!rolling && rigid.velocity.magnitude >= 0.01f)
{
rolling = true;
audioSource.clip = rollingClip;
audioSource.Play();
}
// Stop the rolling sound if rolling slows down.
else if (rolling && rigid.velocity.magnitude < 0.01f)
{
rolling = false;
audioSource.Stop();
}
}
// Occurs when this object stops colliding with another object
void OnCollisionExit(Collision collision)
{
// Stop the rolling sound if the object falls off and stops colliding.
if (rolling)
{
rolling = false;
audioSource.Stop();
}
}
}
- 匯出專案,部署到 Hololens 上進行測試。
- 四處走動,靠近遠離感受音量的變化。
Chapter6 - Spatial mapping 空間對映
在本章節,我們將通過 Spatial mapping 去實現把場景中的物體放置在真實世界的真實物體之上。
- 把 Holograms 資料夾中的 Spatial Mapping 拖入 Hierarchy。
- 選中 Spatial Mapping,修改其以下屬性:
- 勾選 Draw Visual Meshes
- 為 Draw Material 指定 wireframe 材質。
- 匯出專案,部署到 Hololens Emulator上進行測試。
- 當應用執行時,可以觀察到一個提前掃描過的房間的網格以 wireframe 材質的樣式出現了。
- 觀察小球是如何掉出桌子,掉到地上的。
現在我將教給你如何讓 ORIgamiCollections 移動到一個新的位置。
- 新建一個指令碼,命名為 TapToPlaceParent。
- 把指令碼拖給 Stage 物體。
- 開啟指令碼進行編輯,新增如下程式碼:
using UnityEngine;
public class TapToPlaceParent : MonoBehaviour
{
bool placing = false;
// Called by GazeGestureManager when the user performs a Select gesture
void OnSelect()
{
// On each Select gesture, toggle whether the user is in placing mode.
placing = !placing;
// If the user is in placing mode, display the spatial mapping mesh.
if (placing)
{
SpatialMapping.Instance.DrawVisualMeshes = true;
}
// If the user is not in placing mode, hide the spatial mapping mesh.
else
{
SpatialMapping.Instance.DrawVisualMeshes = false;
}
}
// Update is called once per frame
void Update()
{
// If the user is in placing mode,
// update the placement to match the user's gaze.
if (placing)
{
// Do a raycast into the world that will only hit the Spatial Mapping mesh.
var headPosition = Camera.main.transform.position;
var gazeDirection = Camera.main.transform.forward;
RaycastHit hitInfo;
if (Physics.Raycast(headPosition, gazeDirection, out hitInfo,
30.0f, SpatialMapping.PhysicsRaycastMask))
{
// Move this object's parent object to
// where the raycast hit the Spatial Mapping mesh.
this.transform.parent.position = hitInfo.point;
// Rotate this object's parent object to face the user.
Quaternion toQuat = Camera.main.transform.localRotation;
toQuat.x = 0;
toQuat.z = 0;
this.transform.parent.rotation = toQuat;
}
}
}
}
- 匯出專案,部署到 Hololens 上進行測試。
- 現在,你可以移動物體到一個新的位置:首先凝視物體,然後做出選擇手勢,把視點移動到新的位置後,再次做出選擇手勢即可。
Chapter7 - Holographic fun
本章將帶你做出一個全息的地獄入口!(scary face)
- 從 Holograms 資料夾中拖拽 Underworld 到 OrigamiCollection 上作為子物體。
- 新建一個指令碼,命名為 HitTarget。
- 把 HitTarget 指令碼新增給 Target 物體。(就是那個藍色的風扇)
- 開啟 HitTarget 指令碼進行編輯,新增如下程式碼:
using UnityEngine;
public class HitTarget : MonoBehaviour
{
// These public fields become settable properties in the Unity editor.
public GameObject underworld;
public GameObject objectToHide;
// Occurs when this object starts colliding with another object
void OnCollisionEnter(Collision collision)
{
// Hide the stage and show the underworld.
objectToHide.SetActive(false);
underworld.SetActive(true);
// Disable Spatial Mapping to let the spheres enter the underworld.
SpatialMapping.Instance.MappingEnabled = false;
}
}
- 再次在 Unity 中,選擇 Target 物體。
- 現在 Target 的 Inspector 面板中有兩個 公有變數 需要填充物體。
- 把 Underworld 物體拖拽到 Underworld 變數上。
- 把 Stage 物體拖拽到 Stage 變數上。
- 匯出專案,部署到 Hololens 上進行測試。
- 現在,把 OrigamiCollection 放在地板上,然後使一個紙球掉落。
- 當紙球擊中藍色的風扇時,會發生一次爆炸。之後,Collection消失了,取而代之的是一個地獄的大門!
The end
到此結束啦~
你已經學會了:
- 如何在Unity裡建立一個Hololens應用。
- 如何使用gaze, gesture, voice, sounds, 以及 spatial mapping。
- 如何匯出專案、部署應用到 Hololens 上。
相信你現在已經準備好開發自己的 Hololens 應用啦~
相關推薦
【Hololens開發】Holograms 101: Introduction with Device(使用Hololens)
本文我們將通過Unity建立一個包括:凝視(gaze)、手勢(gesture)、語音輸入(voice)、空間聲音(spatial sound)和空間對映(spatial mapping)的完整的 Hololens 專案,並直接在 Hololens 上執行。 譯
【移動開發】關於一對一視訊交友直播(五)延遲優化
6 月底釋出了一個針對視訊直播的實時流網路 LiveNet 和完整的直播雲解決方案,很多開發者對這個網路和解決方案的細節和使用場景非常感興趣。 結合實時流網路 LiveNet 和直播雲解決方案的實踐,我們用一系列文章,更系統化地介紹當下大熱的視訊直播各環節的關鍵技術,幫助視訊直播創業者們更全面、深入地瞭解視
【iOS開發】判斷app啟動的方式(launchOptions)
iOS app啟動的方式有哪些: 自己啟動(使用者手動點選啟動)urlscheme啟動(關於urlScheme的詳解點選開啟連結)本地通知啟動 (自己寫的本地通知啟動,藍芽模組的啟動,地理圍欄的啟動)遠端通知啟動 (後臺伺服器的推送通知)在appdelegate.m
【golang-GUI開發】qt之signal和slot(一)
類型 取消 htm return 所在 turn hang nbsp type 想了很久,我決定還是先從signal和slot(信號槽)開始講起。 signal和slot大家一定不陌生,先看一段示例(選自文檔): 1 class Counter : public QOb
【敏捷開發】經驗構件庫-Java版(exp-libs)
完整原文(含原始碼):http://exp-blog.com/2018/09/22/pid-2382/ (轉載請註明出處,僅供分享學習,嚴禁用於商業用途) 環境 簡介 此構件庫為本人多年程式設計總結提煉而成,把常用的功能模組作為原子API
【活動報名】NEO 區塊鏈公開課(1): NEO 區塊鏈開發入門
NEO 區塊鏈公開課是 NGD ( NEO Global Development )、NEL ( NewEconoLab )、InnoSpace 創新院共同發起的區塊鏈技術學習專場活動。旨在滿足廣大開發者對 NEO 區塊鏈技術的入門、進階以及技能提升需求。 精心設計的課程內容,資深技術講師
【6年人工智慧開發】簡述卷積神經網路(CNN)
在百度做了6年人工智慧方面的程式設計開發,也有很多這方面的經驗吧,從古至今,機器人一直承載著人類巨大的夢想。隨著各類感測器、語音互動、機器識別、SLAM等技術的蓬勃發展,機器人開始從科幻作品中走出
【遊戲開發】directx遊戲專案——第一部分(未完)
目的: 編寫啟動渲染系統的程式碼,用於初始化Direct3D,將螢幕清屏為指定的顏色以及關閉系統。 main.h標頭檔案 //main.h #ifndef _UGP_MAIN_H_ #define _UGP_MAIN_H_ #include "StrandedE
【Android開發】wifi開關與wifi連線(密碼連線)
過放蕩不羈的生活,容易得像順水推舟,但是要結識良朋益友,卻難如登天。—— 巴爾扎克 本文demo來自網路,找了好久找到的,後面自己做了些許修改,這裡對原始碼解析,愧於忘記哪裡出來了,感謝作者! 接下來就記錄一下wifi開發的一些學習心得,這邊先看幾張效果圖吧!
【Office 365】Exchange Online 直接轉換遷移(Cutover)問題排查
office 直接 轉換遷移 在本次直接轉換遷移(Cutover)項目中遇到一部分郵箱遷移錯誤,部分是由於該郵箱關聯的Manager賬號或郵箱傳遞限制權限中有問題。部分郵箱遷移報錯信息如下: 在AD中找到該用戶並查看屬性,在Organization頁面看到Manager的選項,並確認
【分治法】最接近點對問題(轉)
線性 sig 2個 線性時間選擇 i++ srand 排序算法 esp 坐標 轉自:http://blog.csdn.net/liufeng_king/article/details/8484284 問題場景:在應用中,常用諸如點、圓等簡單的幾何對象代表現實世界中的實體。在
【計算機網絡】詳解網絡層(二)ARP和RARP
博文 拆分 detail 動態 再次 tcp 將在 ont 關於 ARP ARP(Address Resolution Protocol,地址解析協議)是將IP地址解析為以太網MAC地址(物理地址)的協議。在局域網中,當主機或其他網絡設備有數據要發送給另一個主機或設備時,它
【算法】哈希表的誕生(Java)
sys 什麽是 ros http 鍵值 private 問題 現象 三種 參考資料 《算法(java)》 — — Robert Sedgewick, Kevin Wayne 《數據結構》
【機器學習】谷歌的速成課程(一)
label spa dev 分類 ram 做出 org ron 表示 問題構建 (Framing) 什麽是(監督式)機器學習?簡單來說,它的定義如下: 機器學習系統通過學習如何組合輸入信息來對從未見過的數據做出有用的預測。 標簽 在簡單線性回歸中,標簽是我們要預測
【Android-3】Android中的任務棧(Task)
集合 情況下 清除 bsp 生命周期方法 任務棧 保存 sin 也會 一、Android任務棧 概述:Android中的任務棧其實就是Activity的集合,在Android中退出程序的時候必須把任務棧中的所有Activity清除出棧,此時才能安全的完全的退出程序, 任務棧
【AI基礎】python:openCV——圖像處理(1)
python python:openCV OpenCV圖像的基本操作 :使用包cv2,numpy import cv2 import numpy as np img = cv2.imread(‘1.jpg‘) cv2.imshow(‘image‘,img) k = cv2.waitKey(0) "
【AI基礎】python:openCV——圖像處理(2)
getTrackbarPos圖像處理練習 制作一個滑動條調色板,使用函數cv2.getTrackbarPos();cv2.creatTrackbar() import cv2 import numpy as np def nothing(x): pass img = np.zeros((300,5
【AWS系列】鐳速RaySync VS FTP (1)- AWS加州北到阿裏雲深圳
傳輸 測試 AWS 鐳速 測試軟件信息 鐳速RaySync下載地址: Windows Server: https://www.raysync.cn/support_for_windows.html Linux Server: https://www.raysync.cn/support_fo
【AWS系列】鐳速RaySync VS FTP (5)- AWS法蘭克福到阿裏雲深圳
傳輸 測試 AWS raysync 鐳速 測試軟件信息?鐳速RaySync下載地址: Windows Server: https://www.raysync.cn/support_for_windows.html Linux Server: https://www.raysync.cn/s
【AWS系列】鐳速RaySync VS FTP (6)- AWS加拿大到阿裏雲深圳
傳輸 測試 AWS 鐳速 raysync 測試軟件信息 鐳速RaySync下載地址: Windows Server: https://www.raysync.cn/support_for_windows.html Linux Server: https://www.raysync.cn/s