• 当微信小程序遇到AR(三)


    微信小程序遇到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="{{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程序了~

    【代码】

    Github=>

  • 相关阅读:
    WinCE下SQLCE数据库开发(VS,VB.net,VC++)
    基于VC++的WinCE网口通信
    WinCE下的串口通信开发(VS2005,VB.Net,VC++)
    多线程CSerialPort类的多串口通信实现
    双T滤波电路用于PWM方式DAC的分析
    AD9516锁相环功能外接环路滤波器的设计与分析
    块结构中断有序化处理方法(一种单片机单线程方式下处理多中断的方法)
    STM32F10X固件库函数——串口清状态位函数分析
    STM32和STR71X移植uCos-II操作系统比较分析
    基于uIP和uC/OS-II嵌入式网络开发
  • 原文地址:https://www.cnblogs.com/starskyli/p/11652006.html
Copyright © 2020-2023  润新知