const drawTriangle = regl ( {
frag : `
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
} ` ,
vert : `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0, 1);
} ` ,
attributes : {
position : [ [ 0 , - 1 ] , [ - 1 , 0 ] , [ 1 , 1 ] ]
count : 3
canvas 3d库
< canvas
id = " cobe"
style = " width : 500px; height : 500px; "
width = " 1000"
height = " 1000"
> </ canvas>
然后使用cobe
import createGlobe from 'cobe'
let phi = 0
let canvas = document. getElementById ( "cobe" )
const globe = createGlobe ( canvas, {
devicePixelRatio : 2 ,
width : 1000 ,
height : 1000 ,
phi : 0 ,
theta : 0 ,
dark : 0 ,
diffuse : 1.2 ,
scale : 1 ,
mapSamples : 16000 ,
mapBrightness : 6 ,
baseColor : [ 0.3 , 0.3 , 0.3 ] ,
markerColor : [ 1 , 0.5 , 1 ] ,
glowColor : [ 1 , 1 , 1 ] ,
offset : [ 0 , 0 ] ,
markers : [
{ location : [ 37.7595 , - 122.4367 ] , size : 0.03 } ,
{ location : [ 40.7128 , - 74.006 ] , size : 0.1 } ,
onRender : ( state ) => {
state. phi = phi
phi += 0.01
https://github.com/shuding/cobe
OGL 的定位比较特别,它有点介于 WebGL 封装和渲染引擎之间
可以看到它一方面提供了渲染引擎才有的场景树、相机等概念,另一方面又没有材质和光源,所以需要自己写 Shader 来完成基本渲染。
因此我觉得它比较适合 TA(Technical Artist) 使用,基于它开发可以不用了解太多 WebGL 的细节,专注于图形学算法,而且它的 Uniform 变量名和 Three.js 是一样的,网上找基于 Three.js 写的例子都能直接用。
不过如果当成 WebGL 封装,它成熟度不如 regl,因为缺乏单元测试使得预计会有不少 bug,而如果当成渲染库在功能上又没法和 Three.js 比,因此主要优势似乎只有体积小了。
luma.gl
luma.gl 是 Uber 开发的,主要用它开发地理可视化框架,比如 Desk.gl 和 Kepler.gl ,还有无人车数据可视化 AVS 。
使用前面几个库要同时支持 WebGL 2.0 和 1.0 需要自己做兼容,而 luma.gl 可以自动解决这个问题,方便在支持 WebGL 2.0 的设备上优先使用 WebGL 2.0,比如直接调用 createVertexArray,不过这种 api 没几个,所以这个亮点倒是不显著。
它的独特功能其实是 Shader 模块化拆分 ,这对于写复杂的 Shader 很有帮助。
由于 WebGL 本身只是光栅引擎,基于它开发需要了解矩阵变换并编写着色器,所以 WebGL 学习门槛很高,光入门就要看特别长的文档,比如 WebGL Fundamentals 这个教程系列就有 60 多篇文章。
相比之下使用渲染引擎就容易得多,它将其中的矩阵变化封装成了相机、场景树,并提供了材质和光源,运行时自动生成对应的 GLSL,使得即使完全不懂 WebGL 也能用,大大降低了门槛。
对于大部分应用而言,比起前面的 WebGL 封装,最好还是选择渲染引擎,因为大部分渲染引擎也提供了自定义 Shader 功能,也提供了 GPU 实例等功能,只是一般不能改渲染管线。
Filament
Filament 是 Google 基于 C++ 开发的跨平台物理渲染引擎,支持 Android、iOS、Windows、Mac 等系统,还提供了基于 WebAssembly 的 Web 版本,它用在了 Google 地图和搜索这两个核心 APP 中,因此大概率会长期维护,不用担心弃坑。
尽管只是顺带支持 Web,但完善的实现使得它的渲染效果很突出,在渲染管线上使用了较为新颖的 Clustered forward renderer,因此能支持大量光源。
它生成的 wasm 文件有 2M,不算太大,我原本打算将它引入到 Sugar 中,但这个渲染库在 Web 上使用有两个缺点:
核心是 C++ 写的,如果功能不满足只能去改 C++,而这几乎是必须的,因为暴露给 JavaScript 的 API 很少,最简单的点击交互都不支持,各个组件的设置也很少,比如相机要实现无交互时自动旋转就得改 C++ 代码,不熟悉 C++ 将寸步难行。
只支持 WebGL 2.0,这点比较致命,导致无法在 iOS 的浏览器上使用,看了一下它有调用 glDrawBuffers,但不清楚用于做什么。
CLayGL
Claygl 是 ECharts 核心开发者 pissang 大神的开发的 WebGL 游戏引擎,它还用在了 ECharts-gl 项目中,使得 ECharts 在三维图表方面远超所有竞品,
在我看来它最大的亮点是支持延迟着色 ,目前除了前面提到的 Filament 和后面的 LayaAir,其它 WebGL 引擎都是传统的前向着色,这种管线在渲染时,会对每个物体计算所有光照的贡献,类似如下的写法:
要解决这个问题只能使用延迟着色或者分簇(Clustered)前向着色,目前主流的桌面游戏引擎都是使用延迟着色,并配合前向着色来支持透明物体,相关细节推荐看看 lygyue:延迟渲染 ,ClayGL 也是目前唯一实现这种管线的开源渲染引擎。
不过在 WebGL 中实现延迟着色最大的问题是兼容性,因为它依赖 MTR(Multiple Render Targets ) 技术,只有在 WebGL 2.0 下原生支持,在 WebGL 1.0 中必须依赖「WEBGLdraw buffers 」扩展,但它的兼容性较差,目前桌面浏览器的支持率也只有 71% ,在手机上更是只有 2%,想在手机上使用只能等 WebGL 2.0 普及,但 iOS 一直默认不开启,不知道要再等几年了。
Litescene.js
Litescene.js 主要用于开发 WebGL 场景编辑器 WebGLStudio ,它其中有些 API 就是专门给编辑器用的,WebGLStudio 是少有的开源 WebGL 编辑器,功能很丰富,但使用体验不好,给我的感受是必须用过 Unity 等编辑器才会用,上手门槛有点高。
游戏引擎在渲染器的基础上增加了面向游戏开发的各种功能,包括 AI、物理、编辑器等,工作量巨大,比起图形学算法,更重要还有工程能力,
Unreal Engine
目前最火的游戏引擎是 Unreal Engine 和 Unity,它们都可以使用 Emscripten 编译出 WebAssembly 版本的项目,直接运行在浏览器中。
Unreal Engine 从 4.24 版本开始不默认提供这个功能,只作为扩展存在,交给社区,要用得自己编译一个,所以 Unreal Engine 目前已经基本放弃了 HTML5 版本。
Unity
相比之下 Unity 编译出来的体积小得多,自带的简单 3D 项目编译出 wasm 只有 4M,所以虽然也很少人用,但至少在线上有真实见到过几个。
值得一提的是 Unity 还在开发专门针对小游戏的 Project Tiny 版本,相当于一个精简版的 Unity,它输出的体积更小,比如这个官方的 Tiny Racing 项目 wasm 只有 607k,即便是所有模型和图片加起来的体积也只有 4.4M,虽然这个项目还在预览阶段,很多重要功能缺失,但 Unity 目前普及度高,所以它未来有不小潜力,但它在国内的发展取决于官方的是否重视,比如会不会支持微信等。
Godot
Godot 是目前最火的开源游戏引擎,它有 1182 个贡献者,提交很频繁,最近在开发的 4.0 版本,将支持 Vulkan API,并在渲染方面做了加强,比如支持 SDFGI 。
它也能导出 WebGL 版本,但只是「能导出」,并没有专门优化过,拿几个材质测试了一下生成的 wasm 有 20M,但性能太差,在我的 i9 + RX 5700 XT 上都卡成 PPT,而且卡顿这个问题官方也不打算修了 ,预计 Godot 短期不会在 Web 领域有所发展,只能等它未来或许会支持 WebGPU 了。
three.js
Three.js 是最知名的 WebGL 项目,Contributions 人数高达 1313,和 React 是一个量级的,尽管它自身的定位只是渲染引擎,但社区硬是把不少游戏引擎的功能都加上了,比如物理引擎、贴花、动画等,在源码中有大量例子,很适合学习,但不少重要功能,比如 gltf 加载器,都是放在 examples 目录里,让人感觉很不正式。
Three.js 的历史几乎和 WebGL 一样长,它早在 2010 年 7 月 7 日 就支持 WebGL 渲染了,那个时候 WebGL 规范还在草案中,要等到 2011 年 3 月才正式发布,恐怕这就是为什么提到 WebGL 大家都会想到 Three.js,它大概是第一个支持 WebGL 的引擎。
由于知名度最高,Three.js 最大的优势就是社区强大,搜索问题能找到很多答案,也有非常多开源和商业项目使用,比如 Google 的 WebGL Globe 、model-viewer 、NASA 的 WorldWind 、Autodesk 的 Forge Viewer 等。
但 Three.js 在版本管理方面很不专业,到现在都还没采用 semver 版本命名规范,每次发布都是一个叫 rXXX 的版本,我见过不少基于 Three.js 的项目都是固定在某个版本不敢升级了,比如 Autodesk 就提到过。
虽然 Three.js 有很多人使用,但因为整体代码质量一般,我只推荐用来学习,而不是用在正式项目中。
Egret
Egret 和后面介绍的 LayaAir 和 cocos 都是国内创业公司开发的游戏引擎,Egret 最早是通过一款《围住神经猫》的 HTML5 游戏莫名其妙火的,它最早只支持 2D,但也在 2018 年 5 月推出了开源的 Egret 3D 。
Egret 3D 引擎使用了 ECS 架构,所以它的编辑器提供了类似 Unity 那样添加组件的能力。
但 Egret 3D 开源后没多久就陷入停滞状态了,最新发布的版本是 2018 年 9 月,据说是在重构新版,然而已经过去一年了,可能 3D 并不是公司的重点,开源的版本甚至连 license 都没说明,加上文档比较简陋,所以不推荐使用。
LayaAir
LayaBox 公司最早推出的是 LayaFlash 工具来将 Flash 页游装成 HTML5 版本,随着 Flash 的没落,他们又开发了基于 Web 技术的 LayaAir 引擎。
LayaAir 的三维编辑器主要依赖 Unity,它甚至不能直接使用 WebGL 中最常用的 glTF 格式,要使用三维模型必须先导入到 Unity 中,然后再通过插件转成 LayaAir 所使用的格式。
比起 Three.js/Babylon,LayaAir 有两个比较大的优势,一个是对小程序支持友好,这个算是国内特色,Three.js/Babylon j的核心开发者也没条件测试,所以实际用起来容易遇到 bug,玩玩需要改引擎本身代码才能解决;另一个是近期实现了 Clustered Forward 渲染,可以支持大量光源。
LayaAir 还提供了生成原生 Android/iOS 程序的 LayaNative,这里并非使用 WebView,所以更容易过审。
但需要注意 LayaAir 只是源码开放,并不是真正的开源项目,使用前需要仔细阅读它的协议 ,比如未经授权是不允许对引擎代码进行修改的,免费使用需要加 LayaBox 的 logo。
从提交历史看,LayaAir 提交量最多的开发者在今年 4 月份忽然停止了,似乎是被阿里挖走了,不知道对引擎本身的发展会有多大影响。
Cocos 3D
cocos 曾经是最流行的 2D 手游引擎,但随着游戏逐渐转向 3D,它在 3D 方面和 Unity 差距太大,就渐渐淡出大家的视野了。
cocos 所属的触控科技本来打算 2014 年在美国上市,但由于对估值不满意,尤其是 cocos2d-x 的 MIT 协议被认为价值几乎为零,所以最后放弃了上市,具体细节可以看看创始人的回答cocos2dx 还有未来么? - 陈昊芝的回答 ,其中还提到了和 Unity 的故事,比如本来还想收购 Unity 但被拒了,在放弃上市后,触控经历了很多危机,人数也收缩为之前的 1/5,从那时起 cocos2d-x 其实就在走下坡路了,逐渐被 Unity 超越。
尽管很艰难,触控一直没放弃引擎的开发,在 2019 年 10 月发布了 Cocos Creator 3D,和 cocos2d-x 基于 C++ 不同,Cocos Creator 3D 是基于 TypeScript 开发的 WebGL 引擎。
在协议方面,Cocos Creator 3D 吸取了 cocos2d-x 的教训,和 LayaAir 一样只是源码开放,它也有一份定制的协议,有很多限制,需要仔细阅读 。
尽管 Cocos Creator 3D 很想成为 Unity,编辑器在很多细节点上都参考了 Unity,比如资源管理的 .meta 文件,基于 ECS 的组件机制等,但 WebGL 的限制使得它只能用做小游戏的引擎,因为 OpenGL ES 2.0 功能的缺失,虽然可以发布到微信、百度、支付宝等平台上,但在重度游戏领域没法和 Unity 竞争。
Babylon
最后压轴的是 Babylon,它也是 Sugar 最终采用的 WebGL 引擎,不仅功能强大,代码质量也很高,TypeScript 类型完善,几乎每个函数都有注释。
我个人的使用体会是 Babylon 虽然入门要复杂点,但功能成熟度要比 Three.js 高不少,Three.js 至今在 gLTF 的支持上还有 bug ,而 Babylon 是唯一通过所有测试的框架,如果要深入使用 gLTF,Babylon 是最好选择,因为它还支持大量扩展,比如 KHRmesh quantization、KHRdraco meshcompression、KHR texturebasisu、MSFT lod 等,这些扩展能显著减小体积和提升性能。
Babylon 在材质方面功能丰富,除了基础的 PBR,还提供了用于皮肤的次表面渲染 SubSurface、用于车漆的 ClearCoat、用于布料的 Sheen,以及用于光盘之类的各向异性材质 Anisotropy 等等。
在后期特效方面有 Lut 颜色校正、Tonemap 映射、SSAO、镜面反射、Bloom 等常见特效,还有基于屏幕的反射 SSR(Screen Space Reflections)。
目前 Babylon 在渲染方面也是使用最传统的前向着色,如果要改造成类似 ClayGL 那样的延迟着色成本太高,兼容性也不好,所以最好的选择是用分簇来减少光源,这个想法在 2017 年就有提出,但然后就没有然后了。
除了在渲染方面的功能很多,Babylon 的周边工具也很丰富,最近还推出了类似 UE4 蓝图的材质编辑器 。
对于一般 WebGL 开发,推荐使用 Babylon.js。
如果要支持微信小程序,最好用国内的 LayaAir 和 Cocos,但需要注意它们只是源码开放,并不是无条件免费使用,需要仔细阅读使用协议。
如果只想写原生 WebGL 特效,建议用 regl。
如想支持大量光源和后期特效,又不需要支持 iOS,用 Claygl。
如果熟悉 Unity,直接用它导出 WebGL 也是可行的。
WebGL原理
WebGL是一种3D图形绘图标准,把JavaScript与openGL ES2.0结合在一起,通过增加OpenGL ES 2.0的绑定,WebGL可以为HTML5 canvas提供硬件3d渲染,从而可以借助系统显卡在浏览器中更流畅地展示3D场景和模型,还能创建复杂的导航和数据视觉化。
OpenGL ES(embeded system)是为嵌入式设备或手机提供高级3D图形应用编程接口。Open GL ES目前支持ios、android、linux、windows。
OpenGL渲染流程
Shader
shader的开发语言
HLSL: 主要用于Direct3D。平台:windows。
GLSL: 主要用于OpenGL。 平台:移动平台(iOS,安卓),mac(only use when you target Mac OS X or OpenGL ES 2.0)
CG:与DirectX 9.0以上以及OpenGL 完全兼容。运行时或事先编译成GPU汇编代码。CG比HLSL、GLSL支持更多的平台,Unity Shader采用CG/HLSL作为开发语言。
https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-fundamentals.html
gltf、glb文件相关
gltf-pipeline
转换gltf文件为glb或者draco gltf,或者相反
npm install -g gltf-pipeline
gltf-pipeline -i model.gltf -o model.glb
gltf-pipeline -i model.gltf -b
gltf-pipeline -i model.glb -o model.gltf
gltf-pipeline -i model.glb -j
gltf-pipeline -i model.gltf -o modelDraco.gltf -d
在js中使用
const gltfPipeline = require ( "gltf-pipeline" ) ;
const fsExtra = require ( "fs-extra" ) ;
const gltfToGlb = gltfPipeline. gltfToGlb;
const gltf = fsExtra. readJsonSync ( "model.gltf" ) ;
gltfToGlb ( gltf) . then ( function ( results ) {
fsExtra. writeFileSync ( "model.glb" , results. glb) ;
} ) ;
webgl教程
https://www.ctolib.com/docs-webgl-c-using-a-texture.html
threeJs
threejs是基于webGL的纯js的3D渲染库,可以快速搭建web3D界面
全局导入或者部分导入
import * as THREE from 'three' ;
import { Scene} from 'three'
在THREEjs中,渲染一个3d世界的必要因素是场景(scene)、相机(camera)、渲染器(renderer)。渲染出一个3d世界后,可以往里面增加各种各样的物体、光源等,形成一个3d世界:
var scene = new THREE. Scene ( ) ;
相机是3d场景中的角度,在三维空间中代表观察者所在的位置。在Threejs中有多种相机 呈现的不同效果,主要有透视相机(THREE.PerspectiveCamera)和 正相交相机 (THREE.OrthographicCamera)
var camera = new THREE. PerspectiveCamera ( 75 , window. innerWidth/ window. innerHeight, 0.1 , 1000 ) ;
camera. position. z = 5 ;
创建渲染器
var renderer = new THREE. WebGLRenderer ( ) ;
renderer. setSize ( window. innerWidth, window. innerHeight ) ;
document. body. appendChild ( renderer. domElement ) ;
添加物体到场景
var geometry = new THREE. BoxGeometry ( 1 , 1 , 1 ) ;
var material = new THREE. MeshBasicMaterial ( { color : 0x00ff00 } ) ;
var cube = new THREE. Mesh ( geometry, material ) ;
scene. add ( cube ) ;
function animate ( ) {
cube. rotation. x += 0.1 ;
cube. rotation. y += 0.1 ;
requestAnimationFrame ( animate ) ;
renderer. render ( scene, camera ) ;
animate ( ) ;
详细名词解释
相机即视角
正交摄像机(orthographic)是一个矩形可视区域,物体只有在这个区域内才是可见的物体无论距离摄像机是远或事近,物体都会被渲染成一个大小。一般应用场景是2.5d游戏如跳一跳、机械模型
透视摄像机 (perspective)是最常用的摄像机类型,模拟人眼的视觉,近大远小(透视)。Fov表示的是视角,Fov越大,表示眼睛睁得越大,离得越远,看得更多。如果是需要模拟现实,基本都是用这个相机
透视摄像机视角参数
THREE.PerspectiveCamera(fov,aspect,near,far)
fov:视野角度,从镜头可以看到的场景的部分。通常3D游戏的FOV取值在60-90度之间较好的默认值为60
aspect:渲染区域的纵横比。较好的默认值为window.innerWidth/window.innerHeight
near:最近离镜头的距离
far:远离镜头的距离
物体是由几何体组成的。threejs中定义了常见的3D几何体。
BoxGeometry--长方体 CylinderGeometry--圆柱体 CircleGeometry--圆形平面 PlaneGeometry--方形平面
SphereGeometry--球体 TextGeometry--文字
THREE.PlaneGeometry 矩形 THREE.CircleGeometry 圆形或者扇形
THREE.RingGeometry 圆环或者扇环 THREE.ShapeGeometry 自定义形状
一个物体很多的物理性质,取决于其材料,材料也决定了几何体的外表。材料的创建方法也是new。
MeshBasicMaterial -- 自发光材质 MeshLambertMaterial--漫反射材质 MeshPhongMaterial--镜面反射材质
LineBasicMaterial-用于THREE.Line对象,创建彩色线条 LineDashMaterial-用于THREE.Line对象,创建虚线条
SpriteCanvasMaterial、SpriteMaterial、PointCloudMaterial-在针对单独的点进行渲染时用到
透明度:可以设置材料的透明程度,先设置transparent属性为true开启透明,然后设置opacity的值设置透明程度。opacity为9-1,0表示完全透明,1表示完全不透明。默认值是1。
还可以设置纹理使物体更加真实,纹理通过TextureLoader加载纹理图片:
var texture = new THREE. TextureLoader ( ) . load ( 'textures/crate.gif' ) ;
一个3d世界,如果需要更加逼真,那就需要光源了。光也有很多种,常见的有平行光、点光源、环境光(环境光充满所有的几何体表面)、聚光灯等。
const light = new THREE. DirectionalLight ( 0xffffff , 0.9 )
PointLight:3D空间中的一个点光源,向所有方向发出光线 AmbientLight 环境光,其颜色均匀的应用到场景及其所有对象上,这种光源为场景添加全局的环境光。
AmbientLight。环境光,其颜色均匀的应用到场景及其所有对象上,这种光源为场景添加全局的环境光。这种光没有特定的方向,不会产生阴影。通常不会把AmbientLight作为唯一的光源,而是和SpotLight、DirectionalLight等光源结合使用,从而达到柔化阴影、添加全局色调的效果。指定颜色时要相对保守,例如#0c0c0c。设置太亮的颜色会导致整个画面过度饱和,什么都看不清:
HemisphereLight 特殊光源,用于创建户外自然的光线效果,此光源模拟物体表面反光效果、微弱发光的天空,模拟穹顶(半球)的微弱发光效果,SpotLight 产生圆锥形光柱的聚光灯,台灯、天花板射灯通常都属于这类光源,这种光源的使用场景最多
SpotLight:产生圆锥形光柱的聚光灯,台灯、天花板射灯通常都属于这类光源,这种光源的使用场景最多,特别是在你需要阴影效果的时候。
AreaLight 面光源 指定一个发光区域 LensFlare 不是光源,用于给光源添加镜头光晕效果
LensFlare:不是光源,用于给光源添加镜头光晕效果
加载外部模型
加载器用于加载外部模型。通过Three.js加载器(Loader)实现的。加载器把文本/二进制的模型文件转化为Three.js对象结构。 每个加载器理解某种特定的文件格式。
jsonloader 导入 json 文件
STereoLithography的简写,在快速原型领域被广泛使用。3D打印模型通常使用该格式定义Three.js提供了STLExporter.js,使用它可以把Three.js模型导出为STL格式
openCTM定义的格式,以紧凑的格式存储基于三角形的Mesh
Visualization Toolkit定义的格式,用于声明顶点和面。此格式有二进制/ASCII两种变体,Three.js仅支持ASCII变体
3D场景的二进制格式,主要被away3d引擎使用,Three.js不支持AWD压缩格式
Assimp
开放资产导入库(Open asset import library)是导入多种3D模型的标准方式。使用该Loader你可以导入多种多样的3D模型格式
虚拟现实建模语言(Virtual Reality Modeling Language)是一种基于文本的格式,现已经被X3D格式取代尽管Three.js不直接支持X3D,但是后者很容易被转换为其它格式
Collada(dae)
基于XML的格式,被大量3D应用程序、渲染引擎支持
Three.js自定义的、基于JSON的格式。可以声明式的定义一个Geometry或者Scene.利用该格式,你可以方便的重用复杂的Geometry或Scene
OBJ / MTL
OBJ是Wavefront开发的一种简单3D格式,此格式被广泛的支持,用于定义Geometry,MTL用于配合OBJ,它指定OBJ使用的材质
loader
dracoloader
const loader = new DRACOLoader ( ) ;
loader. setDecoderPath ( '/examples/js/libs/draco/' ) ;
loader. preload ( ) ;
loader. load (
'model.drc' ,
function ( geometry ) {
const material = new THREE. MeshStandardMaterial ( { color : 0x606060 } ) ;
const mesh = new THREE. Mesh ( geometry, material ) ;
scene. add ( mesh ) ;
function ( xhr ) {
console. log ( ( xhr. loaded / xhr. total * 100 ) + '% loaded' ) ;
function ( error ) {
console. log ( 'An error happened' ) ;
在WebGlRenderer渲染器中使用THREE.Sprite创建的粒子可以直接添加到scene中。创建出来的精灵总是面向镜头的。即不会有倾斜变形之类透视变化,只有近大远小的变化。
Three.js中并没有直接提供“点击”功能,我们可以基于THREE.Raycaster来判断鼠标当前对应到哪个物体,用来进行碰撞检测.
实时渲染requestAnimationFrame
场景中如果我们添加了各种 mesh 和模型并给他加入了一些 tweend动画会发现他并不会运动,因为你的场景并没有实时渲染,所以要让场景真的动起来,我们需要用到requestAnimationFrame;
轨道控制器
加上此控制器,就可以通过鼠标拖拽、滚动对整个画面进行拖拽放缩
使用方法就是new一个控制器,然后监听变化,触发render
const controls = new THREE. OrbitControls ( camera, renderer. domElement) ;
controls. addEventListener ( "change" , ( ) => {
renderer. render ( scene, camera) ;
} ) ;
controls. minDistance = 1 ;
controls. maxDistance = 2000 ;
controls. enablePan = false ;
可以拷贝下来,挂在window上。官方大部分例子都使用了一个stat的插件,在左上角会出现性能变化的曲线,供我们调试使用。使用方法:
const stat = new Stats ( ) ;
document. body. appendChild ( stat. dom) ;
function render ( ) {
renderer. render ( scene, camera) ;
stat. update ( ) ;
实例1:跳一跳游戏
var Game = function ( ) {
Game . prototype = {
init :
restart :
addSuccessFn :
addFailedFn :
_createJumper :
_createCube :
_setLight :
_setCamera :
_setRenderer :
_render :
_createHelpers :
_checkUserAgent :
_handleWindowResize :
_handleMousedown :
_handleMouseup :
_fallingRotate :
_falling :
_checkInCube :
_updateCameraPos :
_updateCamera :
_setSize :
var game = new Game ( )
game. init ( )
game. addSuccessFn ( success)
game. addFailedFn ( failed)
function restart ( ) {
function failed ( ) {
function success ( score ) {
实例2:雪花粒子
let width = window. innerWidth;
let height = window. innerHeight;
let renderer = new THREE. WebGLRenderer ( ) ;
renderer. setSize ( width, height) ;
renderer. setClearColor ( 'rgb(22,33,82)' , 1.0 ) ;
let element = document. getElementById ( 'snow' ) ;
element. appendChild ( renderer. domElement) ;
let scene = new THREE. Scene ( ) ;
let camera = new THREE. PerspectiveCamera ( 75 , width / height, 1 , 500 ) ;
camera. position. set ( 0 , 0 , 50 ) ;
camera. lookAt ( scene. position) ;
scene. add ( camera) ;
let loader = new THREE. TextureLoader ( )
loader. load ( '../assets/snow.png' , ( texture ) => {
let material = new THREE. PointsMaterial ( {
map : texture,
transparent : true ,
size : 5 ,
} ) ;
} ) ;
let range = 400 ;
let geom = new THREE. Geometry ( ) ;
for ( let i = 0 ; i < 800 ; i++ ) {
let v = new THREE. Vector3 (
Math. random ( ) * range - range / 2 ,
Math. random ( ) * range - range / 2 ,
Math. random ( ) * range - range / 2
v. velocity = createVelocity ( ) ;
geom. vertices. push ( v) ;
points = new THREE. Points ( geom, material) ;
scene. add ( points) ;
renderer. render ( scene, camera) ;
function randomRange ( t, i ) {
return Math. random ( ) * ( i - t) + t
function createVelocity ( ) {
let velocity = new THREE. Vector3 ( 0 , - 0.4 , 0 ) ;
velocity. rotateX ( randomRange ( - 45 , 45 ) ) ;
velocity. rotateY ( randomRange ( 0 , 360 ) ) ;
return velocity;
setInterval ( animate, 1000 / 40 ) ;
function animate ( ) {
let vertices = points. geometry. vertices;
vertices. forEach ( function ( v, idx ) {
v. y = v. y + ( v. velocity. y) ;
v. x = v. x + ( v. velocity. x) ;
v. z = v. z + ( v. velocity. z) ;
if ( v. y <= - range / 2 ) v. y = range / 2 ;
if ( v. x <= - range / 2 || v. x >= range / 2 ) v. x = v. x * - 1 ;
if ( v. z <= - range / 2 || v. z >= range / 2 ) v. velocity. z = v. velocity. z * - 1 ;
} ) ;
points. geometry. verticesNeedUpdate = true ;
renderer. render ( scene, camera) ;
实例3:变换粒子效果
let width = window. innerWidth;
let height = window. innerHeight;
let renderer = new THREE. WebGLRenderer ( ) ;
renderer. setSize ( width, height) ;
renderer. setClearColor ( 'rgb(22,33,82)' , 1.0 ) ;
let element = document. getElementById ( 'snow' ) ;
element. appendChild ( renderer. domElement) ;
let scene = new THREE. Scene ( ) ;
let camera = new THREE. OrthographicCamera ( width / - 2 , width / 2 , height / 2 , height / - 2 , 1 , 1000 ) ;
camera. position. set ( 0 , 0 , 10 ) ;
scene. add ( camera) ;
const rng = d3. randomNormal ( 0 , 0.01 ) ;
for ( let i = 0 ; i < pointsCount; i++ ) {
let v = new THREE. Vector3 (
( rng ( ) + Math. cos ( i) ) * ( width / 2.5 ) ,
( rng ( ) + Math. sin ( i) ) * ( height / 2.5 ) ,
let c = new THREE. Color ( 0 , 1 , 0 ) ;
const rng = d3. randomNormal ( 0 , 0.05 ) ;
for ( let i = 0 ; i < pointsCount; i++ ) {
let v = new THREE. Vector3 (
rng ( ) * width,
rng ( ) * height,
let c = new THREE. Color ( 0 , 0.5 , 1 ) ;
let material = new THREE. PointsMaterial ( {
size : 1.0 ,
vertexColors : THREE . VertexColors,
function greenCircleLayout ( geometry ) {
const rng = d3. randomNormal ( 0 , 0.01 ) ;
geometry. vertices. forEach ( ( d, i ) => {
new TWEEN. Tween ( d) . to ( {
x : ( rng ( ) + Math. cos ( i) ) * ( width / 2.5 ) ,
y : ( rng ( ) + Math. sin ( i) ) * ( height / 2.5 ) ,
} , 800 )
. start ( )
} ) ;
geometry. colors. forEach ( ( d, i ) => {
new TWEEN. Tween ( d) . to ( {
g : 1 ,
b : 0 ,
} , 800 )
. start ( )
} ) ;
function blueNormalLayout ( geometry ) {
let layouts = [ greenCircleLayout, blueNormalLayout] ;
let startTime = new Date ( ) . getTime ( ) ;
let currentLayout = 0 ;
requestAnimationFrame ( animate) ;
function animate ( ) {
let now = new Date ( ) . getTime ( ) ;
if ( now - startTime > 1500 ) {
currentLayout = ( currentLayout + 1 ) % layouts. length;
layouts[ currentLayout] ( pointCloud. geometry) ;
startTime = now;
TWEEN . update ( ) ;
pointCloud. geometry. verticesNeedUpdate = true ;
pointCloud. geometry. colorsNeedUpdate = true ;
renderer. render ( scene, camera) ;
requestAnimationFrame ( animate)
https://juejin.im/post/6866335813790072845#heading-7
regl是
官网:http://regl.party/
GitHub:https://github.com/regl-project/regl
例程地址:https://regl-project.github.io/regl/www/gallery.html
const drawTriangle = regl ( {
frag : `
void main() {
gl_FragColor = vec4(1, 0, 0, 1);
} ` ,
vert : `
attribute vec2 position;
void main() {
gl_Position = vec4(position, 0, 1);
} ` ,
attributes : {
position : [ [ 0 , - 1 ] , [ - 1 , 0 ] , [ 1 , 1 ] ]