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的大小,是碰撞體積的大小。
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.