从零开始手撸WebGL3D引擎1: 搭建项目框架
如上文所说,我希望这个基于WebGL的框架是一个独立的库,可以像一个普通的js库那样被引用,但是开发的时候又需要根据需求拆分成很多js文件,那么就涉及到一个打包问题。且这一次我想使用ES6的模块系统和ES6的语法,那么rollup.js都可以做到,好吧就他吧。
既然使用js,我们就用npm初始化了一个模块:mini3d.js,需要说明的是,目前暂时没有将mini3d.js注册到npm,也许以后会,所以现在的用法就是从源码build出mini3d.js文件,然后使用。由于我在Web前端开发上的经验很有限,所以可能显得不专业,但是没事,这个可以慢慢完善。
好了,先看一下目录结构。
mini3d.js目录结构
文章图片
作为一个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
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的实现细节。说实话细节是魔鬼,就这么一个简单的东西,搞了很久,为了基础扎实,所有的矩阵都是自己推导的,好吧先看一下截图,确实很不吸引人
文章图片
推荐阅读
- Docker应用:容器间通信与Mariadb数据库主从复制
- 一个人的碎碎念
- 我从来不做坏事
- 上班后阅读开始变成一件奢侈的事
- 从蓦然回首到花开在眼前,都是为了更好的明天。
- 日志打卡
- 西湖游
- 改变自己,先从自我反思开始
- leetcode|leetcode 92. 反转链表 II
- 从我的第一张健身卡谈传统健身房