从零开始手撸WebGL3D引擎1: 搭建项目框架

如上文所说,我希望这个基于WebGL的框架是一个独立的库,可以像一个普通的js库那样被引用,但是开发的时候又需要根据需求拆分成很多js文件,那么就涉及到一个打包问题。且这一次我想使用ES6的模块系统和ES6的语法,那么rollup.js都可以做到,好吧就他吧。
既然使用js,我们就用npm初始化了一个模块:mini3d.js,需要说明的是,目前暂时没有将mini3d.js注册到npm,也许以后会,所以现在的用法就是从源码build出mini3d.js文件,然后使用。由于我在Web前端开发上的经验很有限,所以可能显得不专业,但是没事,这个可以慢慢完善。
好了,先看一下目录结构。
mini3d.js目录结构
从零开始手撸WebGL3D引擎1: 搭建项目框架
文章图片

作为一个npm模块,mini3d.js有package.json配置文件以及本地的node_modules目录。mini3d.js框架的代码放到src目录中,测试例子的代码放到examples目录中。rollup.js打包出来的框架代码和测试例子代码以及相关的资源放到build目录中。
package.json 设置

{ "name": "mini3d", "version": "0.1.0", "description": "A tiny webGL library", "main": "mini3d.js", "scripts": { "build": "rollup -c", "watch": "rollup -c -w", "dev": "npm-run-all --parallel start watch", "start": "serve build" }, "repository": { "type": "git", "url": "git+https://github.com/happyfire/mini3d.js.git" }, "keywords": [ "webGL" ], "author": "happyfire", "license": "ISC", "bugs": { "url": "https://github.com/happyfire/mini3d.js/issues" }, "homepage": "https://github.com/happyfire/mini3d.js#readme", "devDependencies": { "@babel/core": "^7.6.2", "@babel/preset-env": "^7.6.2", "core-js": "^3.2.1", "npm-run-all": "^4.1.5", "rollup": "^1.21.4", "rollup-plugin-babel": "^4.3.3", "rollup-plugin-commonjs": "^10.1.0", "rollup-plugin-node-resolve": "^5.2.0", "rollup-plugin-terser": "^5.1.2", "serve": "^11.2.0" }, "dependencies": {} }

目前所有的库都是本地依赖,即通过npm install安装到项目的node_modules中。主要依赖的库有rollup以及rollup的一些插件。插件的作用具体参考rollup.js官网。另外使用了一个serve,用于启动一个http服务进行测试。项目的构建使用了npm脚本,即:
"scripts": { "build": "rollup -c", "watch": "rollup -c -w", "dev": "npm-run-all --parallel start watch", "start": "serve build" },

  • npm run build会执行rollup -c,即根据rollup配置文件rollup.config.js中的设置执行rollup。
  • npm run watch会执行rollup -c -w,同样是根据配置文件执行rollup,并且加入了对代码变动的检测,一旦发现代码改变就会执行构建。
  • npm run start会执行serve build,即在build目录上启动一个http服务,方便测试
  • npm run dev使用npm-run-all同时执行start和watch,即启动http服务,且对源代码进行检测,自动构建。
    以上设置参考了rollup.js官方的例子项目 rollup-starter-app
rollup配置文件rollup.config.js
import resolve from 'rollup-plugin-node-resolve'; import commonjs from 'rollup-plugin-commonjs'; import babel from 'rollup-plugin-babel'; //import { terser } from 'rollup-plugin-terser'; // npm run build -> production is true // npm run dev -> production is false const production = !process.env.ROLLUP_WATCH; export default [ { input: 'src/mini3d.js', output: { format: 'iife', name: 'mini3d', file: 'build/mini3d.js', sourcemap: true }, plugins:[ resolve(),// tells Rollup how to find date-fns in node_modules commonjs(), // converts date-fns to ES modules // babel({ //exclude:'node_modules/**' // }),//production && terser() // minify, but only in production ] },{ input: 'examples/src/main.js', output: { file: 'build/examples/bundle.js', format: 'iife', name: 'main', sourcemap: true }, plugins:[ resolve(),// tells Rollup how to find date-fns in node_modules commonjs(), // converts date-fns to ES modules babel({ exclude:'node_modules/**' }),//production && terser() // minify, but only in production ] }]

注意我注释掉了一些,因为开发时有些功能暂时不用,另外有个别问题我还没解决好,下面会说。
export default后面跟着的是一个数组,所以可以放多个配置。我这为了方便,把example的配置和mini3d.js的配置都放这儿了。
第一个配置是构建mini3d.js,input指定的入口文件:src/mini3d.js,output指定了输出文件即配置。file为build/mini3d.js即最终框架会被打包到这个位置的mini3d.js,注意这个和src/mini3d.js是不同的。format为’iife’意思是将模块打包成iife的格式,且模块名为mini3d。
plugins配置是按顺序使用多个插件去进行处理,resolve然后是commonjs,目前我就用了这两个,结果是产生了build/mini3d.js这个单个文件。即ES6的模块被转换成了iife格式。然后babel被我注释掉了,所以产生的文件里面的会保留ES6的语法(如果用了的话,实际确实用了)。当然如果打开babel也是可以的,但是为了查看最终产生的框架代码我就先去掉了。另外就是有个问题没解决,WebGL会用到typed array,例如Float32Array,这个虽然是ES6的东西,但是很多支持WebGL的浏览器在支持ES6之前就支持这个了,换句话说如果不支持这个也不能支持WebGL。然而使用Bable,具体的是使用了corejs后,会把typed array换成pollyfill,这个我还不知道怎么处理,在我理解中,typed array应该保留,所以我暂时把babel去掉了,但是如果打开babel最后也能运行。然后最后是terser,且在production环境才会使用,这个就是把生成的代码变短变紧凑,为了看最后代码的内容,也先去掉了。
export default数组的第二个配置是example的打包,入口是examples/src/main.js,输出为build/examples/bundle.js。配置和mini3d.js一样,只是我打开了babel,因为example的代码里面不会出现typed array(细节被mini3d.js封装了)。
rollup-plugin-babel的设置
【从零开始手撸WebGL3D引擎1: 搭建项目框架】bable配置文件为 .babelrc,可以放到每个源码目录下面独立设置。这个先不说了,我自己也没完全搞清楚,先留着。目前的配置能跑,由于版本的原因,按照网上的很多教程会有错误。本来说使用WebGL就行看中了环境好搭建,但是作为前端外行来说还是很麻烦的,好在重点是研究WebGL,这种环境相关的东西可以慢慢处理,或者如果真有Web前端专业人士关注我的这个小项目,希望能伸出援手,多谢!
测试项目结构
测试项目的源码在examples/src中,目前仅有一个main.js,由于使用了rollup.js,所以可以使用ES6模块去划分代码,但是入口必须是main.js。mini3d.js框架和example的代码都会被打包到build目录中,如果运行npm run build就会同时打包这两个,目前这样是为了方便,毕竟代码不多。实际最好是分开的。其中examples的代码会被打包到build/examples/bundle.js,这个目录中还有一个写好的index.html。而mini3d.js框架代码直接打包到build根目录中。http服务目前是起在build根目录下的,这样测试项目所在的url为:http://localhost:5000/examples/
index.html和webGL程序入口
总算写完环境搭建了,看一看入口的index.html吧。由于WebGL是依托在canvas上的(下一节会讲),所以在body下面创建了一个canvas,且id为"webgl"。然后两个script标签分别指向打包出来的mini3d.js框架和测试项目代码bundle.js。由于mini3d.js是一个IIFE,被载入时即执行,执行产生的结果赋值给了一个叫mini3d的对象。所以我们在onLoad里面可以使用mini3d.init('webgl')初始化整个框架,传入的webgl是canvas的id。之后执行main()方法,这个main()方法测试代码examples/src/main.js中通过ES6的export default导出的函数,然后被rollup.js处理成bundle.js这个IIFE模块载入执行后导出的main函数。(mini3d.js导出的是一个叫mini3d的对象,而bundle.js导出的是一个叫main的函数)。执行main()函数启动实例代码
下一步
下面会从WebGL的一些基本概念开始(当然是非常总结性的,可能会忽略很多细节),介绍mini3d.js的第一个里程碑ms1_rotate_cube的实现细节。说实话细节是魔鬼,就这么一个简单的东西,搞了很久,为了基础扎实,所有的矩阵都是自己推导的,好吧先看一下截图,确实很不吸引人
从零开始手撸WebGL3D引擎1: 搭建项目框架
文章图片

    推荐阅读