當微信小程序遇到AR,會擦出怎么樣的火花?期待與激動......
通過該教程,可以從基礎開始打造一個微信小程序的AR框架,所有代碼開源,提供大家學習。
本課程需要一定的基礎:微信開發者工具,JavaScript,Html,Css
第三章:基石-接入Three.js
【前情提要】
上一章,我們已經可以在微信小程序中訪問攝像頭,並且獲得每一幀的數據了。接下來的另一個基礎人物就是對接Three.js庫:
1. 實現在微信小程序中訪問攝像頭,並且可以實時的拿到每一幀畫面的數據。 |
2. 實現在微信小程序中訪問WebGL接口,實現繪制三維物體。該教程采用Three.js引擎 |
3. 實現在背景為攝像頭實時畫面的背景上顯示WebGL的3D物體。 |
4. 整體框架搭建 |
5. 圖像算法接入 |
【目的】
將Three.js引擎接入微信小程序,實現一個cube旋轉的基本demo。
【准備】
下面需要搭建環境,做一些准備工作。
首先,需要注冊微信小程序開發者。注冊地址=>
注冊成功之后,需要下載微信小程序開發工具。下載地址=>
目前筆者的開發環境是:Windows 10
下載的微信小程序版本為:RC v1.0.2.1909111
【創建工程】
按照與上一章同樣的步驟,我們創建一個簡單的基本工程。這里就不再贅述了。創建好之后的基本工程目錄結構如下:
[開發]
下面我們將完成一下幾個步驟:
1. 導入Three.js庫,並做相應的適配。
2. 創建微信小程序的WebGL畫布Canvas並初始化。
3. 利用Three.js創建一個包含Cube的場景,並顯示。
4. 加入Cube的運動動畫。
1. 導入Three.js庫,並做相應的適配
首先我們需要去Three.js的官網下載最新版本的Three.js庫文件,可以前往官方的:Github=>
打開Github頁面之后,在“Bruch”,下選擇“Master”分支,如下圖:
這時候,我們可以進入到“Build”文件夾下,下載最新編譯好的庫,可以看到當前的Build最新的版本為r109。當然同學們在學習的時候,可能最新的版本會更高,沒有關系,下載你們當前最新的版本即可。
進入“Build”文件夾之后,會看見幾個編譯好的庫:three.js, three.min.js, three.module.js。其中:
three.js: 編譯好的原始庫文件
three.min.js:編譯好的壓縮過的庫文件(壓縮之后的庫文件大小更小了,但是里面的內容已經不具備可讀性了)
three.module.js:如果不以庫的形式而是以一個模塊的形式使用three.js可以下載這個文件
目前,我們為了能做一些微信的適配,需要明文可讀的庫文件,所以選擇第一個three.js文件
點擊“Download”按鈕下載該文件。(可能你的瀏覽器不會提示下載,而是直接在頁面上打開了這個文件,可以選擇全選然后復制里面的內容到一個新的文本文件,在保存為three.js格式即可)。
接下來,我們需要在小程序工具中,新建一個文件夾用來保存所有的庫文件,所以新建一個名為:“libs”的文件夾,並將下載好的“three.js”文件放入其中,如下如:
至此,我們就導入了three.js文件到我們的項目工程中,保存之后,重新編譯,可以看到並沒有報任何錯誤。
從編譯中可以看出,three.js已經編譯了,並且由於文件過大跳過了壓縮過程以及ES6轉ES5的過程。這里不用做任何處理,說明three.js已經被我們的小程序工具支持了。不需要做過多的適配工作。(在之前版本的three.js或者微信開發者工具基礎庫比較舊的版本上,導入three.js會出現很多錯誤提示,這是由於three.js中很多關於DOM的引用都沒有被小程序開發基礎庫支持,而現在的版本可以看到已經被很好的支持了)。
2. 創建微信小程序的WebGL畫布Canvas並初始化。
下面新建一個Canvas用來繪制WebGL的內容,打開"pages/index/index.wxml"文件,添加相關的標簽如下:
<!--index.wxml--> <view> <!--創建canvas標簽用於WebGL--> <canvas type="webgl" id="webgl" canvas-id="webgl" style="width:{{canvasWidth}}px;height:{{canvasHeight}}px;"> </canvas> </view>
在這段代碼中,我們制定了canvas的類型type為“webgl”,設置了id為"webgl"(以便js代碼訪問到),以及canvas的樣式style。樣式style我們通過了數據綁定的方法來實現,以便后面在js代碼中動態的修改canvas的樣式(主要是canvas的大小)。接下來,我們就打開“pages/index/index.js”文件,添加代碼初始化canvas:
//index.js //獲取應用實例 const app = getApp(); Page({ data: { canvasWidth:0, canvasHeight:0 }, /** * 頁面加載回調函數 */ onLoad: function () { //初始化Canvas對象 this.initWebGLCanvas(); }, /** * 初始化Canvas對象 */ initWebGLCanvas:function() { //獲取頁面上的標簽id為webgl的對象,從而獲取到canvas對象 const query = wx.createSelectorQuery(); query.select('#webgl').node().exec((res) => { var canvas = res[0].node; this._webGLCanvas = canvas; //獲取系統信息,包括屏幕分辨率,顯示區域大小,像素比等 var info = wx.getSystemInfoSync(); this._sysInfo = info; //設置canvas的大小,這里需要用到窗口大小與像素比乘積來定義 this._webGLCanvas.width = this._sysInfo.windowWidth * this._sysInfo.pixelRatio; this._webGLCanvas.height = this._sysInfo.windowHeight * this._sysInfo.pixelRatio; //設置canvas的樣式 this._webGLCanvas.style = {}; this._webGLCanvas.style.width = this._webGLCanvas.width.width; this._webGLCanvas.style.height = this._webGLCanvas.width.height; //設置顯示層canvas綁定的樣式style數據,頁面層則直接用窗口大小來定義 this.setData({ canvasWidth: this._sysInfo.windowWidth, canvasHeight: this._sysInfo.windowHeight }); }); } })
小提示:
筆者的使用習慣,是在每一個語句結尾使用分號";",當然在js中也可以不使用。這個習慣是來源於c++的編程習慣。筆者認為加上分號能更好的區分每一條語句,不容易和下一行發生混淆。當然,大家可以有自己的使用習慣。
解釋一下上面的代碼,首先在onLoad回調函數中,加入了一個initWebGLCanvas的自定義函數,用來初始化WebGL的canvas對象。在這個函數中:
首先應用wx.createSelectorQuery和select語句獲取到當前的canvas對象;
接着通過wx.getSystemInfoSync語句獲取到當前的系統相關信息,主要用到了窗口大小和像素比兩個信息。
最后通過創就大小和像素比設置canvas的長寬屬性。
3. 利用Three.js創建一個包含Cube的場景,並顯示
接下來,我們將利用three.js創建一個新的三維場景,並顯示一個Cube對象。
小知識:
Three.js在構建場景的時候與大多數的三維引擎類似,都有幾個基本的抽象概念:
Camera:描述了某個視圖觀察者的視覺屬性,包括觀察的位置,方向,范圍等。
Scene:一個三維的場景。
Light:三維場景中的燈光。
Mesh:三維場景中的某個網格對象,包括了網格的集合描述以及材質的描述。
Geomtry:三維圖形的集合描述。
Material:三維圖形的表面材質屬性。
Texture:貼圖。
該教程,並不會對Three.js進行展開講解,畢竟該教程主要是講解如何在微信小程序中搭建AR環境,如果感興趣的同學,可以自行學習=>
具體的步驟就是首先創建WebGLRenderer(WebGL渲染器),然后創建基本的三維顯示元素,包括攝像頭,場景,物體等。最后執行渲染操作。我么可以在initWebGLCanvas函數最后加一個自定義函數的調用:initWebGLScene,然后在這個自定義的函數中,來實現場景的初始化。然后在這個函數結尾再調用一個自定義的執行渲染的函數,具體的代碼如下:
首先,一開始需要引用three.js庫文件:
//index.js //導入three.js庫 import * as THREE from '../../libs/three.js'
接着來定義我們的InitWebGLScene函數以及渲染函數renderWebGL:
/** * 初始化WebGL場景 */ initWebGLScene:function() { //創建攝像頭 var camera = new THREE.PerspectiveCamera(60,this._webGLCanvas.width /this._webGLCanvas.height , 1, 1000); this._camera = camera; //創建場景 var scene = new THREE.Scene(); this._scene = scene; //創建Cube幾何體 var cubeGeo = new THREE.CubeGeometry(30, 30,30); //創建材質,設置材質為基本材質(不會反射光線,設置材質顏色為綠色) var mat = new THREE.MeshBasicMaterial({ color: 0x00FF00 }); //創建Cube的Mesh對象 var cube = new THREE.Mesh(cubeGeo, mat); //設置Cube對象的位置 cube.position.set(0,0,-100); //將Cube加入到場景中 this._scene.add(cube); //創建渲染器 var renderer = new THREE.WebGLRenderer({ canvas: this._webGLCanvas }); //設置渲染器大小 this._renderer = renderer; this._renderer.setSize(this._webGLCanvas.width, this._webGLCanvas.height); //開始渲染 this.renderWebGL(); }, /** * 渲染函數 */ renderWebGL:function(){ //渲染執行場景,指定攝像頭看到的畫面 this._renderer.render(this._scene,this._camera); }
這樣,我們就完成了場景的創建和渲染。保存編譯的化,發現出現了錯誤:
這個錯誤指出addEventListener不是一個有效的函數,定位到了three.js中的代碼,我們可以看到具體的問題出在了以下兩行:
這就是之前提到的適配問題,three.js的原生代碼和微信小程序的框架不適應。這兩行代做所做的事情就是在創建WebGL之前先添加兩個事件監聽函數,用來監聽WebGL的Contex對象是否消逝或者再次出現。然而在微信小程序的框架中,會自動管理WebGL的Context對象,也不支持對Canvas添加這兩個事件,所以我們直接注釋掉這兩行代碼即可。
再次保存,編譯,我們就可以看到正確的顯示了:
可以看到,通過之前的設置,無論屏幕的分辨率是多少,我們都可以將WebGL的畫布鋪滿整個屏幕,而且保持了正確像素比,渲染出來的立方體也沒有變形。
不過,目前完全沒有WebGL的Feel,所以我們可以加一些代碼,讓這個Cube立方體動起來。
4. 加入Cube的運動動畫。
運動,就需要每一幀的刷新。所以我們會用到微信提供的WebGL幀刷新事件。另外,我們需要刷新cube對象的角度,讓它旋轉起來,這就需要在renderWebGL函數中訪問到cube對象。當然可以有很多方法,例如我們可以將cube對象傳遞到renderWebGL函數中。從而在渲染函數渲染每一幀畫面前旋轉cube對象。
另外我們需要控制運動的速度,但是由於每一幀渲染的時間並不是固定的,這就受到手機性能,場景復雜程度,代碼邏輯等一系列因素影響,如果我們每一幀旋轉一個固定的角度,那么最后Cube旋轉的速度就不是勻速的,也不可控。所以如何要控制旋轉的速度固定勻速呢?
這就需要我們獲取到每一幀的時間間隔,然后根據時間間隔來設置每一幀Cube的旋轉角度。如果間隔較大,那旋轉角度也要大一點,因為用了比較長的時間,反之則越小。所以每一幀需要設置的旋轉角度應該和間隔時間成正比。
接下來我們就用代碼實現一下,首先我們在initWebGLScene函數的結尾,修改一下代碼,記錄一下第一次渲染的時間,以及傳遞cube引用到renderWebGL函數中:
/** * 初始化WebGL場景 */ initWebGLScene:function() { 。。。省略之前的代碼
//設置渲染器大小 this._renderer = renderer; this._renderer.setSize(this._webGLCanvas.width, this._webGLCanvas.height); //記錄當前時間 var lastTime = Date.now(); this._lastTime = lastTime; //開始渲染 this.renderWebGL(cube); }
接着我們修改一下renderWebGL函數:
/** * 渲染函數 */ renderWebGL:function(cube){ //獲取當前一幀的時間 var now = Date.now() ; //計算時間間隔,由於Date對象返回的時間是毫秒,所以除以1000得到單位為秒的時間間隔 var duration = (now - this._lastTime) / 1000; //打印幀率 console.log(1/duration + 'FPS'); //重新賦值上一幀時間 this._lastTime = now; //旋轉Cube對象,這里希望每秒鍾Cube對象沿着Y軸旋轉180度(Three.js中用弧度表示,所以是Math.PI) cube.rotation.y += duration * Math.PI; //渲染執行場景,指定攝像頭看到的畫面 this._renderer.render(this._scene,this._camera); //設置幀回調函數,並且每一幀調用自定義的渲染函數 this._webGLCanvas.requestAnimationFrame(()=>{ this.renderWebGL(cube); }); }
在渲染函數中,我們修改了Cube對象的旋轉角度,並且打印了幀率。在筆者的電腦上。幀率維持在60FPS左右
當然,這是在模擬器中的版本。在真機上的測試(修改了Canvas的高度,以便顯示輸出面板,所以Cube形狀會有一些壓扁)。筆者的手機是華為Mate10Pro,測試結果如下圖:
發現幀率也是基本維持在60FPS左右。這已經可以滿足當前絕大多數三維應用的開發了。
【總結】
這一章,我們在微信小程序中引入了Three.js庫,並且對庫中不適配的地方做了修改。最后在Three.js庫的幫助下,創建了一個動態的三維場景並且真機上測試。至此,如果只是開發微信小程序WebGL的同學已經可以在現在的基礎上創建出絢麗多彩的WebGL程序了~