Babylon.js

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

兩種最常用的📷

  1. Universal Camera: for First Person Movement
  2. 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

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的方式

  1. 設置一系列的關鍵幀
  2. 在運行時修改動畫代碼,爲了實現更加複雜的動畫

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

finalPosition=positionvec3(0,ellipsoid.y,0)+ellipsoidOffsetfinalPosition = 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.


  1. babylonjs-vs-threejs-choosing-a-webgl-framework-for-sony ↩︎ ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章