1. 事前準備
本程式碼研究室會逐步說明如何建構 AR 網頁應用程式,並使用 JavaScript 算繪 3D 模型,讓模型看起來像是存在於現實世界。
您可以使用結合擴增實境和虛擬實境 (VR) 功能的 WebXR Device API。您將著重於 WebXR Device API 的 AR 擴充功能,建立可在互動式網路上執行的簡易 AR 應用程式。

什麼是 AR?
AR 通常是指將電腦生成的圖像與現實世界混合。如果是手機 AR,則是指在即時攝影機畫面中,逼真地放置電腦繪圖。為了讓這項效果在手機移動時保持真實感,支援 AR 的裝置必須瞭解移動時所處的世界,並判斷在 3D 空間中的姿勢 (位置和方向)。這可能包括偵測表面和估算環境照明。
自從 Google 發布 ARCore 和 Apple 發布 ARKit 後,AR 技術已廣泛應用於各種應用程式,無論是自拍濾鏡還是 AR 遊戲,都能看到 AR 技術的身影。
建構項目
在本程式碼研究室中,您將建構網頁應用程式,透過擴增實境將模型放置在現實世界中。您的應用程式將會:
- 使用目標裝置的感應器,判斷及追蹤裝置在世界中的位置和方向
- 在攝影機即時畫面頂端算繪合成的 3D 模型
- 執行命中測試,將物件放置在真實世界中探索到的表面上
課程內容
- 如何使用 WebXR 裝置 API
- 如何設定基本 AR 場景
- 如何使用 AR 命中測試尋找表面
- 如何載入及算繪與真實世界攝影機影像同步的 3D 模型
- 如何根據 3D 模型算繪陰影
本程式碼研究室著重於 AR API。我們不會對與主題無關的概念和程式碼多做介紹,但會事先準備好這些程式碼區塊,屆時您只要複製及貼上對應存放區程式碼即可。
軟硬體需求
- 用於編碼和託管靜態網頁內容的工作站
- 支援 ARCore 的 Android 裝置,搭載 Android 8.0 Oreo
- Google Chrome
- 已安裝 Google Play 服務 - AR 適用 (Chrome 會在相容裝置上自動提示您安裝)
- 選擇的網路伺服器
- USB 傳輸線,用於將 AR 裝置連接至工作站
- 範例程式碼
- 文字編輯器
- 具備 HTML、CSS、JavaScript 和 Google Chrome 開發人員工具的基本知識
在 AR 裝置上按一下「Try it」,即可試用本程式碼研究室的第一個步驟。如果頁面顯示「您的瀏覽器沒有 AR 功能」訊息,請確認 Android 裝置已安裝「Google Play 服務 - AR 適用」。
2. 設定開發環境
下載程式碼
- 點選下方連結,即可在工作站下載這個程式碼研究室的所有程式碼:
- 將下載的 ZIP 檔案解壓縮。這會解壓縮根資料夾 (
ar-with-webxr-master),其中包含本程式碼研究室多個步驟的目錄,以及您需要的所有資源。
「step-03」和「step-04」資料夾包含本程式碼研究室第三和第四個步驟的預期最終狀態,以及 final 結果。僅供參考。
您會在 work 目錄中完成所有編碼工作。
安裝網路伺服器
- 您可以自由使用自己的網路伺服器。如果尚未設定,本節將詳細說明如何設定 Web Server for Chrome。
如果工作站尚未安裝該應用程式,可以從 Chrome 線上應用程式商店安裝。
- 安裝 Web Server for Chrome 應用程式後,請前往
chrome://apps並點選「Web Server」圖示:
![]()
接著會看到這個對話方塊,可供您設定本機網路伺服器:

- 按一下「選擇資料夾」,然後選取
ar-with-webxr-master資料夾。這樣一來,您就能透過網頁伺服器對話方塊中醒目顯示的網址 (位於「Web Server URL(s)」部分),放送進行中的工作。 - 在「選項 (需要重新啟動)」下方,勾選「自動顯示 index.html」核取方塊。
- 將「Web server」切換為「Stop」,然後切換回「Started」。

- 確認至少顯示一個網頁伺服器網址:http://127.0.0.1:8887,這是預設的本機主機網址。
設定通訊埠轉送
設定 AR 裝置,讓裝置在存取 localhost:8887 時,能存取工作站上的相同通訊埠。
- 在開發工作站上前往 chrome://inspect,然後按一下「通訊埠轉送...」:

- 使用「通訊埠轉送設定」對話方塊,將通訊埠 8887 轉送至 localhost:8887。
- 勾選「啟用通訊埠轉送」核取方塊:

驗證設定
測試連線:
- 使用 USB 傳輸線將 AR 裝置連接至工作站。
- 在 Chrome 的 AR 裝置上,於網址列中輸入 http://localhost:8887。AR 裝置應將這項要求轉送至開發工作站的網路伺服器。畫面上應會顯示檔案目錄。
- 在 AR 裝置上,按一下
step-03,在瀏覽器中載入step-03/index.html檔案。
畫面上應會顯示「啟動擴增實境」按鈕 | 不過,如果看到「不支援的瀏覽器」錯誤頁面,表示裝置可能不相容。 |
|
|
現在,AR 裝置應該可以連線至網路伺服器。
- 按一下「啟動擴增實境」。系統可能會提示您安裝 ARCore。

首次執行 AR 應用程式時,系統會提示您授予相機權限。
→ 
一切就緒後,您應該會看到攝影機畫面疊加了方塊場景。相機剖析的世界越多,場景理解能力就越好,因此四處移動有助於穩定畫面。

3. 設定 WebXR
在本步驟中,您將瞭解如何設定 WebXR 工作階段和基本 AR 場景。HTML 網頁會提供 CSS 樣式和 JavaScript,以啟用基本 AR 功能。這可加快設定程序,讓程式碼研究室專注於 AR 功能。
HTML 網頁
您可以使用現有的網路技術,在傳統網頁中建構 AR 體驗。在這個體驗中,您會使用全螢幕的算繪畫布,因此 HTML 檔案不必太複雜。
AR 功能需要使用者手勢才能啟動,因此您可以使用一些Material Design 元件,顯示「啟動 AR」按鈕和不支援的瀏覽器訊息。
work 目錄中現有的 index.html 檔案應如下所示。這是實際內容的子集,請勿將此程式碼複製到檔案中!
<!-- Don't copy this code into your file! -->
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Building an augmented reality application with the WebXR Device API</title>
<link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
<!-- three.js -->
<script src="https://unpkg.com/three@0.123.0/build/three.js"></script>
<script src="https://unpkg.com/three@0.123.0/examples/js/loaders/GLTFLoader.js"></script>
<script src="../shared/utils.js"></script>
<script src="app.js"></script>
</head>
<body>
<!-- Information about AR removed for brevity. -->
<!-- Starting an immersive WebXR session requires user interaction. Start the WebXR experience with a simple button. -->
<a onclick="activateXR()" class="mdc-button mdc-button--raised mdc-button--accent">
Start augmented reality
</a>
</body>
</html>
開啟主要 JavaScript 程式碼
應用程式的起點位於 app.js。這個檔案提供一些樣板,用於設定 AR 體驗。
您的工作目錄也已包含應用程式程式碼 (app.js)。
查看 WebXR 和 AR 支援情形
使用者處理 AR 前,請先檢查 navigator.xr 和必要的 XR 功能是否存在。navigator.xr 物件是 WebXR Device API 的進入點,因此如果裝置相容,這個物件就應該存在。此外,請確認系統支援 "immersive-ar" 工作階段模式。
如果一切正常,按一下「進入擴增實境」按鈕,系統就會嘗試建立 XR 工作階段。否則,系統會呼叫 onNoXRDevice() (在 shared/utils.js 中),顯示指出缺少 AR 支援的訊息。
app.js 中已有這段程式碼,因此無須變更。
(async function() {
if (navigator.xr && await navigator.xr.isSessionSupported("immersive-ar")) {
document.getElementById("enter-ar").addEventListener("click", activateXR)
} else {
onNoXRDevice();
}
})();
要求 XRSession
點選「Enter augmented Reality」(進入擴增實境) 時,程式碼會呼叫 activateXR()。即可啟動 AR 體驗。
- 在
app.js中找出activateXR()函式。部分程式碼已省略:
activateXR = async () => {
// Initialize a WebXR session using "immersive-ar".
this.xrSession = /* TODO */;
// Omitted for brevity
}
WebXR 的進入點是 XRSystem.requestSession()。使用 immersive-ar 模式,即可在真實環境中查看算繪內容。
- 使用
"immersive-ar"模式初始化this.xrSession:
activateXR = async () => {
// Initialize a WebXR session using "immersive-ar".
this.xrSession = await navigator.xr.requestSession("immersive-ar");
// ...
}
初始化 XRReferenceSpace
XRReferenceSpace 說明虛擬世界中物件使用的座標系統。'local' 模式最適合用於 AR 體驗,參考空間的原點位於檢視者附近,且追蹤穩定。
在 onSessionStarted() 中使用下列程式碼初始化 this.localReferenceSpace:
this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");
定義動畫迴圈
- 使用
XRSession的requestAnimationFrame啟動算繪迴圈,與window.requestAnimationFrame類似。
系統會在每個影格呼叫 onXRFrame,並提供時間戳記和 XRFrame。
- 完成
onXRFrame的實作。繪製影格時,請新增下列項目,將下一個要求加入佇列:
// Queue up the next draw request.
this.xrSession.requestAnimationFrame(this.onXRFrame);
- 新增程式碼以設定圖形環境。在
onXRFrame底部新增:
// Bind the graphics framebuffer to the baseLayer's framebuffer.
const framebuffer = this.xrSession.renderState.baseLayer.framebuffer;
this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, framebuffer);
this.renderer.setFramebuffer(framebuffer);
- 如要判斷觀看者姿勢,請使用
XRFrame.getViewerPose()。這項XRViewerPose描述裝置在空間中的位置和方向。其中也包含XRView陣列,說明場景應從每個視角算繪,才能在目前裝置上正確顯示。立體虛擬實境有兩個檢視畫面 (每隻眼睛各一個),但 AR 裝置只有一個檢視畫面。
pose.views中的資訊最常用於設定虛擬攝影機的檢視矩陣和投影矩陣。這會影響 3D 場景的布局。設定攝影機後,即可算繪場景。 - 在
onXRFrame底部新增:
// Retrieve the pose of the device.
// XRFrame.getViewerPose can return null while the session attempts to establish tracking.
const pose = frame.getViewerPose(this.localReferenceSpace);
if (pose) {
// In mobile AR, we only have one view.
const view = pose.views[0];
const viewport = this.xrSession.renderState.baseLayer.getViewport(view);
this.renderer.setSize(viewport.width, viewport.height);
// Use the view's transform matrix and projection matrix to configure the THREE.camera.
this.camera.matrix.fromArray(view.transform.matrix);
this.camera.projectionMatrix.fromArray(view.projectionMatrix);
this.camera.updateMatrixWorld(true);
// Render the scene with THREE.WebGLRenderer.
this.renderer.render(this.scene, this.camera);
}
測試
執行應用程式,並在開發裝置上前往 work/index.html。畫面上應該會顯示攝影機畫面,以及在空間中漂浮的立方體,且立方體的透視角度會隨著裝置移動而改變。追蹤效果會隨著你的移動而提升,因此請找出最適合你和裝置的追蹤方式。

4. 新增瞄準線
設定基本 AR 場景後,就可以開始使用命中測試與現實世界互動。在本節中,您將編寫命中測試程式,並使用該程式尋找現實世界中的表面。
瞭解命中測試
一般來說,命中測試是指從空間中的某個點沿某個方向投射直線,並判斷是否與任何感興趣的物件相交。在本例中,您將裝置對準現實世界中的某個位置。想像從裝置鏡頭發出的光線,直接射入前方的實體世界。
WebXR Device API 可讓您瞭解這道光線是否與現實世界中的任何物件相交,這取決於基礎 AR 功能和對世界的理解。

要求提供XRSession,並加購額外功能
如要進行熱門測試,要求 XRSession 時必須使用額外功能。
- 在
app.js中找出navigator.xr.requestSession。 - 新增
"hit-test"和"dom-overlay"功能,如下所示:requiredFeature
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
requiredFeatures: ["hit-test", "dom-overlay"]
});
- 設定 DOM 疊加層。將
document.body元素疊加在 AR 攝影機畫面上,如下所示:
this.xrSession = await navigator.xr.requestSession("immersive-ar", {
requiredFeatures: ["hit-test", "dom-overlay"],
domOverlay: { root: document.body }
});
新增動作提示
ARCore 充分瞭解環境後,就能發揮最佳效果。這項技術稱為「即時定位與地圖建構」(SLAM),會使用視覺上明顯不同的特徵點,計算位置和環境特徵的變化。
使用上一個步驟中的 "dom-overlay",在攝影機串流畫面上方顯示動作提示。
將 <div> 新增至 ID 為 stabilization 的 index.html。這個 <div> 會向使用者顯示動畫,代表穩定狀態,並提示他們移動裝置,以強化 SLAM 程序。使用者進入 AR 模式後,系統會顯示這個圖示,並在十字線找到表面後隱藏,由 <body> 類別控制。
<div id="stabilization"></div>
</body>
</html>
新增十字線
使用十字線指出裝置視野所指的位置。
- 在
app.js中,將setupThreeJs()中的DemoUtils.createCubeScene()呼叫替換為空白的Three.Scene()。
setupThreeJs() {
// ...
// this.scene = DemoUtils.createCubeScene();
this.scene = DemoUtils.createLitScene();
}
- 在新的場景中填入代表碰撞點的物件。提供的
Reticle類別會處理shared/utils.js中的十字線模型載入作業。 - 在
setupThreeJs()中將Reticle新增至場景:
setupThreeJs() {
// ...
// this.scene = DemoUtils.createCubeScene();
this.scene = DemoUtils.createLitScene();
this.reticle = new Reticle();
this.scene.add(this.reticle);
}
如要執行命中測試,請使用新的 XRReferenceSpace。這個參考空間會從觀看者的角度指出新的座標系統,以建立與觀看方向對齊的光線。XRSession.requestHitTestSource() 會使用這個座標系統,計算命中測試。
- 在
app.js的onSessionStarted()中新增下列內容:
async onSessionStarted() {
// ...
// Setup an XRReferenceSpace using the "local" coordinate system.
this.localReferenceSpace = await this.xrSession.requestReferenceSpace("local");
// Add these lines:
// Create another XRReferenceSpace that has the viewer as the origin.
this.viewerSpace = await this.xrSession.requestReferenceSpace("viewer");
// Perform hit testing using the viewer as origin.
this.hitTestSource = await this.xrSession.requestHitTestSource({ space: this.viewerSpace });
// ...
}
- 使用這個
hitTestSource,對每個影格執行命中測試:- 如果點擊測試沒有結果,表示 ARCore 尚未有足夠時間瞭解環境。在這種情況下,請使用穩定性
<div>提示使用者移動裝置。 - 如有結果,請將十字線移至該位置。
- 如果點擊測試沒有結果,表示 ARCore 尚未有足夠時間瞭解環境。在這種情況下,請使用穩定性
- 修改
onXRFrame即可移動十字線:
onXRFrame = (time, frame) => {
// ... some code omitted ...
this.camera.updateMatrixWorld(true);
// Add the following:
const hitTestResults = frame.getHitTestResults(this.hitTestSource);
if (!this.stabilized && hitTestResults.length > 0) {
this.stabilized = true;
document.body.classList.add("stabilized");
}
if (hitTestResults.length > 0) {
const hitPose = hitTestResults[0].getPose(this.localReferenceSpace);
// update the reticle position
this.reticle.visible = true;
this.reticle.position.set(hitPose.transform.position.x, hitPose.transform.position.y, hitPose.transform.position.z)
this.reticle.updateMatrixWorld(true);
}
// More code omitted.
}

新增輕觸螢幕時的行為
XRSession 可以透過 select 事件 (代表主要動作) 根據使用者互動發出事件。在行動裝置上使用 WebXR 時,主要動作是輕觸螢幕。
- 在
onSessionStarted底部新增select事件監聽器:
this.xrSession.addEventListener("select", this.onSelect);
在這個範例中,輕觸螢幕會在十字線位置放置向日葵。
- 在
App類別中建立onSelect的實作:
onSelect = () => {
if (window.sunflower) {
const clone = window.sunflower.clone();
clone.position.copy(this.reticle.position);
this.scene.add(clone);
}
}
測試應用程式
您已建立十字線,可使用裝置透過命中測試瞄準。輕觸螢幕後,您應該就能在十字線指定的位置放置向日葵。
- 執行應用程式時,您應該會看到十字線追蹤地板表面。如果沒有,請慢慢移動手機環顧四周。
- 看到十字線後,輕觸十字線。並在上面放上向日葵。你可能需要稍微移動,讓底層的 AR 平台更準確地偵測現實世界中的表面。低光源環境和沒有特徵的表面會降低場景理解品質,並增加找不到命中點的機率。如果遇到任何問題,請查看
step-04/app.js程式碼,瞭解這個步驟的運作範例。

5. 新增陰影
如要建立逼真的場景,數位物件必須有適當的光線和陰影,才能讓場景更逼真,帶來身歷其境的感受。
three.js 會處理光線和陰影。您可以指定哪些燈光應投射陰影、哪些材質應接收及算繪這些陰影,以及哪些網格可以投射陰影。這個應用程式的場景包含投射陰影的光線,以及僅用於算繪陰影的平面。
- 在
three.jsWebGLRenderer上啟用陰影。建立算繪器後,請在其shadowMap上設定下列值:
setupThreeJs() {
...
this.renderer = new THREE.WebGLRenderer(...);
...
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
...
}
在 DemoUtils.createLitScene() 中建立的範例場景包含名為 shadowMesh 的物件,這是只會算繪陰影的水平平面。這個介面一開始的 Y 位置為 10,000 個單位。放置向日葵後,請移動 shadowMesh,使其與現實世界的表面高度相同,這樣花朵的陰影就會顯示在現實世界的地面上。
- 在
onSelect中,將clone新增至場景後,請加入程式碼來重新放置陰影平面:
onSelect = () => {
if (window.sunflower) {
const clone = window.sunflower.clone();
clone.position.copy(this.reticle.position);
this.scene.add(clone);
const shadowMesh = this.scene.children.find(c => c.name === "shadowMesh");
shadowMesh.position.y = clone.position.y;
}
}
測試
放置向日葵時,您應該會看到向日葵投射陰影。如果遇到任何問題,請查看 final/app.js 程式碼,瞭解這個步驟的運作範例。

6. 其他資源
恭喜!您已完成本程式碼研究室,瞭解如何使用 WebXR 建立 AR 體驗。

