Babylon.js
最近想搞一搞webgl的3d小游戏,为以后的微信小游戏技术铺路。
- Three.js: The aim of the project is to create an easy to use, lightweight, 3D library with a default WebGL renderer.
- PlayCanvas: Fast and lightweight WebGL game engine
- Babylon.js: Babylon.js is a powerful, beautiful, simple, and open game and rendering engine packed into a friendly JavaScript framework.
Three.js非常棒,但偏重render,看官网的列子就可以知道; 用作开发游戏的话,需要额外的插件补充。Babylon.js render和game并重,由法国的一个微软团队发布和维护,看起来稍微比PlayCanvas更有活力点。所以就选择了Babylon.js. ? 1
Babylon 101
About
A step-by-step turtiol;
基本架构
- scene
- light
- camera
- meshes
Shapes
- Box
- Sphere
- Plane
- Ground
let box = BABYLON.MeshBuilder.CreateBox("box", {height: 5, width: 2, depth: 0.5}, scene);
let sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 2, diameterX: 3}, scene);
let myPlane = BABYLON.MeshBuilder.CreatePlane("myPlane", {width: 5, height: 2}, scene);
let myGround = BABYLON.MeshBuilder.CreateGround("myGround", {width: 6, height: 4, subdivisions: 4}, scene);
Material
reactions to light
- diffuse 漫反射
- sepcular 镜面反射
- emissive 发光
- ambient 环境光
color
material可以设置四种属性的color
let myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
myMaterial.diffuseColor = new BABYLON.Color3(1, 0, 1);
myMaterial.specularColor = new BABYLON.Color3(0.5, 0.6, 0.87);
myMaterial.emissiveColor = new BABYLON.Color3(1, 1, 1);
myMaterial.ambientColor = new BABYLON.Color3(0.23, 0.98, 0.53);
mesh.material = myMaterial;
Texture
var myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
myMaterial.diffuseTexture = new BABYLON.Texture("PATH TO IMAGE", scene);
myMaterial.specularTexture = new BABYLON.Texture("PATH TO IMAGE", scene);
myMaterial.emissiveTexture = new BABYLON.Texture("PATH TO IMAGE", scene);
myMaterial.ambientTexture = new BABYLON.Texture("PATH TO IMAGE", scene);
mesh.material = myMaterial;
note
如果texture是带有透明通道的图片
myMaterial.diffuseTexture.hasAlpha = true;
Back Face Culling
背面剔除
WireFrame
设置显示Material的WireFrame
materialSphere1.wireframe = true;
Camera
两种最常用的?
- Universal Camera: for First Person Movement
- Arc Rotate Camera: 轨道相机
绑定到画布上
camera.attachControl(canvas, true);
// true: enable default actions 默认false
触摸事件
PEP or hand.js
Universal Camera
默认相机
,FPS-like Control
let camera = new BABYLON.UniversalCamera("UniversalCamera", vec3(0, 0, -10), scene);
// 将相机对准一个点
camera.setTarget(vec3_zero);
camera.attachControl(canvas, true);
Arc Camera
运行类似卫星,可以围绕目标旋转的相机(始终面向目标)
// Parameters: alpha, beta, radius, target position, scene
let camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 10, new BABYLON.Vector3(0, 0, 0), scene);
FlowCamera
Virtual Joysticks Camera
虚拟操纵杆相机
Lights
Light默认会穿过Meshes,除非Meshes 的shadow generator激活
默认最多4个Lights
Lights 类型
**The Point Light **
let light = new BABYLON.PointLight("pointLight", vec3(1, 10, 1), scene);
The Direction Light
let light = new BABYLON.DirectionLight("directionLight", vec3(0, -1, 0), scene);
The Spot Light
锥形光源
let light = new BABYLON.SpotLight("spotLight", vec3(0, 30, -10), vec3(0, -1, 0), π / 3, 2, scene);
The Hemispheric Light
模拟周围的环境光
let light = new BABYLON.HemisphericLight("HemiLight", vec3(0, 1, 0), scene);
On, Off or Dimmer
light.setEnabled(false); // or true
light.intensity = 0.5; // 默认值为 1
light.range = 100;
Animation
两种设置Animation的方式
- 设置一系列的关键帧
- 在运行时修改动画代码,为了实现更加复杂的动画
Basic Animation
function createScene() {
let box1 = BABYLON.Mesh.CreateBox("Box1", 10.0, scene);
box1.position.x = -20;
let animationBox = new BABYLON.Animation("myAnimation", "scaling,x", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATION_CYCLE);
}
- 参数1: 动画的名字
- 参数2: 动画中变化的属性
- 参数3:帧率
- 参数4: 关键帧中,属性的值的类型
- 参数5: 动画播放完时的行为
设置关键帧
let keys = [];
// T 为参数2, 且此处为FLOAT
const keyFrame<T> = (frame: number, value: T) => { frame: frame, value: value }
keys.push(keyFrame(0, 1));
keys.push(keyFrame(20, 0.2));
keys.push(keyFrame(100, 1));
animationBox.setKeys(keys);
box1.animations = [animationBox];
scene.beginAnimation(box1, 0, 100, true);
beginAnimation的参数
Name | Type | Description | Optional |
---|---|---|---|
target | any | The target | No |
from | number | The fps starting frame | No |
to | number | The fps ending frame | No |
loop | boolean | If true, the animation will loop (dependent upon BABYLON.Animation.ANIMATIONLOOPMODE) | Yes |
speedRatio | number | default : 1. The speed ratio of this animation | Yes |
onAnimationEnd | () => void | The function triggered on the end of the animation, even if the animation is manually stopped (also dependent upon ANIMATIONLOOPMODE) | Yes |
animatable | Animatable | An optional specific animation | Yes |
stopCurrent | boolean | Should we stop the current existing animations if any? Default is yes | Yes |
With Promise
let anim = scene.beginAnimation(box1, 0, 100, false);
console.log('before');
await anim.waitAsync();
console.log('after');
Animation Blending
enabelBlending = true
使用示例:用户控制的正在运动的角色。例如从行走动画变化到跑步动画。
Animation weights
同时运行多个动画,每个动画有其权重。
Easing funtions
Cameras, Mesh Collisions and Gravity
三步走
1. 添加重力
2. 定义一个椭球
这个椭球代表player的大小,是碰撞体积的大小。
f i n a l P o s i t i o n = p o s i t i o n − v e c 3 ( 0 , e l l i p s o i d . y , 0 ) + e l l i p s o i d O f f s e t finalPosition = position - vec3(0, ellipsoid.y, 0) + ellipsoidOffset finalPosition=position−vec3(0,ellipsoid.y,0)+ellipsoidOffset
3. 开启碰撞
// add gravity
scene.gravity = vec3(0, -9.81, 0);
camera.applyGravity = true;
// define a ellipsoid
camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
// enable collision
scene.collisionEnables = true;
camera.checkCollisions = true;
ground.checkCollisions = true;
box.checkCollisions = true;
Intersect Collisions - mesh
碰撞检测
1. intersectsMesh
let result = mesh1.intersectsMesh(mesh2, false);
第二个参数 默认为false
, 当设置为true
是,boundingbox会更贴近mesh, 但性能开销更大。
2. intersectPoint
let result = mesh.intersectsPoint(vec3(10, -5, 0));
Picking Collisions
window.addEventListener("click", () => {
let pickResult = scene.pick(scene.pointerX, scene.pointerY);
if (pickResult.hit) {
impact.position.x = pickResult.pickedPoint.x;
impact.position.y = pickResult.pickedPoint.y;
}
})
type PickResult = {
hit: boolean,
distance: number,
pickedMesh: BABYLON.Mesh,
pickedPoint: BABYLON.Vector3,
faceId: number, // position of the picked face's indices in the indices array
submeshId: number, // id of the picked submesh inside the picked mesh
// ...
}
Raycasts
Sprites
Sprites always face the camera.
Sprite Manager
为了优化GPU资源,使用SpriteManager
let spriteManagerPlayer = new BABYLON.SpriteManager("playManager", "assets/player.png", 2, {width: 64, height: 64}, scene);
let player = new BABYLON.Sprite("player", spriteManagerPlayer);
Sprite Animation
player.playAnimation(0, 43, true, 100);
// start end loop? durationEveryFrame
Particles
Particles System
let particleSystem = new BABYLON.ParticleSystem("particles", 2000, scene);
// 2000 : 系统容量
particleSystem.start();
particleSystem.stop();
//delay
particleSystem.startDelay = 3000;
// or
particleSystem.start(3000);
particleSystem.targetStopDuration = 5;
particleSystem.disposeOnStop = true; // 一次性粒子系统
note
调用stop
后将不会产生新的粒子,但是已经存在仍然在。如要完全重置, 调用reset
Pre-warming
在真实渲染前,执行100次,且循环比渲染时快5倍
system.preWarmCycles = 100;
system.preWarmStepOffset = 5;
system.start();
Particle Texture
particleSystem.particleTexture = new BABYLON.Texture("PATH TO IMAGE", scene);
particleSystem.textureMask = new BABYLON.Color4(0.1, 0.8, 0.8, 1.0);
Particle Emitter
particleSystem.emitter = vec3(-1, 2, 3);
// or
let source = BABYLON.Mesh.CreateBox("source", 1.0, scene);
particleSystem.emitter = source; // position of source is used;
Location And Spread
particleSystem.minEmitBox = vec3(-2, -3, 4);
particleSystem.maxEmitBox = vec3(4, 2, 3);
Lifetime
// Life time of each particle (random between...)
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5
addLifeTimeGradient
Size
(minSize, maxSize)
(minScaleX, maxScaleX)
addSizeGradient
Color
particleSystem.addColorGradient(0, new BABYLON.Color4(1, 1, 1, 0));
Rate
particleSystem.emitRate = 100;
Direction
Gravity
Rotation
Speed
Drag
over time
Shape Emitters
The shapes:
- Point
- Box
- Sphere
- Hemisphere
- Cylinder
- Cone
Noise Texture
GPU Particles
WebGL2.0
Shadows
Basic
let shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
// 1024: the size of shadow
// light: 指定光源
shadowGenerator.getShadowMap().renderList.push(mesh);
// addShadowCaster
// removeShadowCaster
ground.receiveShadows = true;
Soft Shadows
更进一步,你可以激活阴影过滤,移除hard edges, 来创建更美观的阴影。
- usePoissonSampling
- useExponentialShadowMap
- useBlurExponentialShadowMap
- useCloseExponentialShadowMap
- useBlurCloseExponentialShadowMap
- usePercentageCloserFiltering (WebGL 2.0)
Light
每个ShadowGenerator只能被一个light使用。只要Point Light 和 spot light可以投射出阴影。
Toubleshooting
- Bias
- Back face rendering
- Improving the projection matrix precision
- Use the best option for self-shadowing
- Frustum edge falloff
- Freezing shadows in static world
- Cleaning bone matrix weights
- Self Shadow
看到的一个关于Three.js 和 Babylon.js 的简易对比1
So once again… Three.js vs. Babylon.js. This was a very short evaluation. What it came down to was that three.js had far more libraries and extensions - however, this was not the strength of three.js since there is no cohesive development cycles with three.js and although many libraries, tools, and extensions exist, more than often they are not maintained. So it was easy to demonstrate that practically any tool or extension we would require for the SXSW production would require myself or the team updating the extension or tool to be compatible with the other tools we might use on the project. This was due to the failings of the framework since each developer who writes an extension for three.js is writing for a specific compatibility for their own project needs… and not for the overall framework… as this is not within the scope of any developer or group of developers. Thus I find that it requires weeks if not months of of maintenance in three.js prior to building content, just to ensure compatibility between all of the tools and extensions needed to use for most projects. As for babylon.js, the wheel is not generally re-invented as it is with three.js, as most extensions are quickly absorbed into a cohesive framework quickly - provided they have universal appeal - and this integration ensures compatibility as there are fewer and fewer extensions to use, but instead an integrated set of tools which are thoroughly tested and used in production revealing any incompatibilities quickly.
The bottom line is that there are no alpha, beta, and development cycles in three.js, thus no stable releases. Whereas the opposite exists with babylon.js. There is a cohesive development of the tools, and Sony is smart enough to see beyond the politics and to realize that having Microsoft support the development of babylon.js is a huge bonus for an open source framework. And if anyone had to choose a company to support the development of a WebGL or any framework, who better than Microsoft? With practically every other useful WebGL framework in existence spawned by MIT, most all are barely useful at best. And why would anyone pay to use a limited WebGL framework such as PlayCanvas when Babylon.js is far more functional, stable, and free? This baffles me and most anyone who chooses one project using babylon.js. The only argument against babylon.js is that the development of the framework is now supported in house by Microsoft. But for myself and others, this is a positive, not a negative. I’ve been assured by the creators and lead developers of babylon.js that they have secured an agreement with Microsoft ensuring the framework remain open source and free. This ensures that anyone is able to contribute and review all code in the framework, and that it remains in the public domain. Sony gets this and we quickly moved forward adopting babylon.js as the WebGL framework within at least one division of Sony Electronics.