进度:https://doc.babylonjs.com/start/chap6/particlespray
一个球
DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Babylon Templatetitle>
<style>
html, body {
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#renderCanvas {
width: 100%;
height: 100%;
touch-action: none;
}
style>
<script src="https://cdn.babylonjs.com/babylon.js">script>
<script src="https://cdn.babylonjs.com/loaders/babylonjs.loaders.min.js">script>
<script src="https://code.jquery.com/pep/0.4.3/pep.js">script>
head>
<body>
<canvas id="renderCanvas" touch-action="none">canvas>
<script>
const canvas = document.getElementById("renderCanvas");
const engine = new BABYLON.Engine(canvas, true);
const createScene = () => {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera("Camera", -3 * Math.PI / 4, Math.PI / 3, 50, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(0, -1, 1), scene);
light.position = new BABYLON.Vector3(0, 15, -30);
var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 1, scene, false);
ground.receiveShadows = true;
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {}, scene);
return scene;
};
const scene = createScene();
engine.runRenderLoop(function () {
scene.render();
});
window.addEventListener("resize", function () {
engine.resize();
});
script>
body>
html>
将相机连接到画布:
camera.attachControl(canvas, true);//第二个参数可选,默认false,阻止画布上的默认事件,true即允许
使相机不显示低于地面的物体:
camera.upperBetaLimit = Math.PI / 2.2;// β上限
默认的行为是:
1.keyboard键盘-左和右箭头移动相机左右,上下箭头移动它向前和向后;
2.mouse鼠标——用相机旋转相机的原点;
3.touch向左或向右滑动,左右移动相机,上下滑动,向前和向后移动;
4.gamepad手柄-对应于设备。
构建一个通用相机
// 参数:名称、位置、场景
var camera = new BABYLON.UniversalCamera("UniversalCamera", new BABYLON.Vector3(0, 0, -10), scene);
// 把相机对准一个特定的位置。在这个例子中是场景的原点
camera.setTarget(BABYLON.Vector3.Zero());
// 把相机固定在画布上
camera.attachControl(canvas, true);
构建一个弧旋转相机:
//参数:alpha、beta、半径、目标位置、场景
var camera = new BABYLON.ArcRotateCamera("Camera", 0, 0, 10, new BABYLON.Vector3(0, 0, 0), scene);
//定位摄像机覆盖alpha,beta,半径
camera.setPosition(new BABYLON.Vector3(0, 0, 20));
//把相机连接到画布上
camera.attachControl(canvas, true);
通过使用CTRL+MouseLeftClick,也可以使用ArcRotateCamera来进行Panning移镜头操作。你可以指定使用MouseRightClick来替代,通过在attachControl调用中设置useCtrlForPanning:
camera.attachControl(canvas, noPreventDefault, useCtrlForPanning);
如果需要的话,你也可以通过设置来完全禁用panning :
scene.activeCamera.panningSensibility = 0;
如果将弧形旋转相机的parent设为人物,则可实现跟随人物运动:
camera.parent = dude;
两种跟随相机:
给它一个网格作为目标并跟随其移动
当它被创建时,跟随相机的初始位置设置,然后目标位置设置为三个参数:
相机移动到目标位置的速度是通过它的加速度(camera.cameraAcceleration)达到最大速度(camera.maxCameraSpeed)。
构建一个跟随相机:
// 参数:名称、位置、场景
var camera = new BABYLON.FollowCamera("FollowCam", new BABYLON.Vector3(0, 10, -10), scene);
//半径:相机距离目标距离
camera.radius = 30;
// 相机高于目标中心(原点)的目标高度
camera.heightOffset = 10;
// 目标在x y平面上绕目标(中心)的目标旋转角度
camera.rotationOffset = 0;
//从当前位置到目标位置移动相机的加速度
camera.cameraAcceleration = 0.005
//停止的加速度
camera.maxCameraSpeed = 10
//把相机连接到画布上
camera.attachControl(canvas, true);
// 相机锁定目标 = 人物
camera.lockedTarget = dude;
浮雕相机
该AnaglyphUniversalCamera和AnaglyphArcRotateCamera延长使用使用通用和弧形旋转相机用红色和青色的3D眼镜。他们使用后处理过滤技术。
构造立体浮雕通用相机
// //参数:名称,位置,眼睛位置,场景
var camera = new BABYLON.AnaglyphUniversalCamera("af_cam", new BABYLON.Vector3(0, 1, -15), 0.033, scene);
构造立体浮雕旋转相机:
//参数:名称,alpha,beta,半径,目标,eyeSpace,场景
var camera = new BABYLON.AnaglyphArcRotateCamera("aar_cam", -Math.PI / 2, Math.PI / 4, 20, BABYLON.Vector3.Zero(), 0.033, scene);
该eyeSpace
参数设置左眼视图和右眼视图之间的偏移量。戴好3D眼镜后,您可能需要尝试使用此float值。
您可以通过访问Wikipedia页面全面了解浮雕知识,对其进行详细介绍。
设备定位相机
所述DeviceOrientationCamera是专门设计来给设备取向事件作出反应,例如被倾斜一个现代移动设备向前,后,左,或右。
构造设备定位相机:
//参数:名称,位置,场景
var camera = new BABYLON.DeviceOrientationCamera("DevOr_camera", new BABYLON.Vector3(0, 0, 0), scene);
//将相机对准特定位置
camera.setTarget(new BABYLON.Vector3(0, 0, -10));
//设置摄像机对移动和旋转的敏感度
camera.angularSensibility = 10;
camera.moveSensibility = 10;
//将相机连接到画布
camera.attachControl(canvas, true);
虚拟操纵杆相机
该VirtualJoysticksCamera专门向虚拟摇杆事件做出反应。虚拟操纵杆是屏幕上的2D图形,用于控制相机或其他场景项目。
**注意:**这台相机需要第三方文件hand.js。
完整样本:
document.addEventListener("DOMContentLoaded", startGame, false);
function startGame() {
if (BABYLON.Engine.isSupported()) {
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
BABYLON.SceneLoader.Load("Espilit/", "Espilit.babylon", engine, function (newScene) {
var VJC = new BABYLON.VirtualJoysticksCamera("VJC", newScene.activeCamera.position, newScene);
VJC.rotation = newScene.activeCamera.rotation;
VJC.checkCollisions = newScene.activeCamera.checkCollisions;
VJC.applyGravity = newScene.activeCamera.applyGravity;
//等待纹理和着色器准备就绪
newScene.executeWhenReady(function () {
newScene.activeCamera = VJC;
//将相机连接到画布输入
newScene.activeCamera.attachControl(canvas);
//加载场景后,只需注册一个渲染循环即可渲染它
engine.runRenderLoop(function () {
newScene.render();
}),
}),
}, function (progress) {
//要做:向用户提供进度反馈。
}),
}
}
如果您切换回另一台摄像机,请不要忘记先打电话dispose()
。在VirtualJoysticks
创建的3D WebGL的顶部的2D画布画布绘制青色和黄色圆圈的操纵杆。如果您忘记打电话dispose()
,2D画布将保留并继续处理触摸事件。
VR设备定向相机
VRDeviceOrientationFreeCamera,VRDeviceOrientationArcRotateCamera,和VRDeviceOrientationGamepadCamera是从一个VR设备延伸上方的摄像机手柄装置定向相机一个较新的集合。
FlyCamera
FlyCamera模仿3D空间中的自由运动,认为是“空间中的幽灵”。它具有逐步校正侧倾的选项,以及模拟倾斜转弯的选项。
其默认值为:
构造飞行相机:
//参数:名称,位置,场景
var camera = new BABYLON.FlyCamera("FlyCamera", new BABYLON.Vector3(0, 5, -10), scene);
//像飞机一样旋转,具有更快的侧倾校正和倾斜转弯。
//默认值为100。数字越大,校正速度越慢。
camera.rollCorrect = 10;
//默认为false。
camera.bankedTurn = true;
//弧度默认为90度,以弧度表示倾斜角度。
camera.bankedTurnLimit = Math.PI / 2;
//多少偏航(转弯)会影响滚动(倾斜转弯)。
//小于1会减少滚动,大于1会增加滚动。
camera.bankedTurnMultiplier = 1;
//这会将相机附加到画布上
camera.attachControl(canvas, true);
详情请见:https://doc.babylonjs.com/divingDeeper/cameras/camera_introduction
所有的网格都允许光通过它们,除非阴影生成被激活。允许的默认灯光数是4,但这可以增加。
// 参数:名字, 位置, 场景
var light = new BABYLON.PointLight("pointLight", new BABYLON.Vector3(1, 10, 1), scene);
// 参数:名字,位置,场景
var light = new BABYLON.DirectionalLight("DirectionalLight", new BABYLON.Vector3(0, -1, 0), scene);
聚光灯是由一个位置、一个方向、一个角度和一个指数来定义的。这些值定义了一个从位置开始的光锥,向方向发射。
在弧度中,角度定义了聚光灯的锥形光束的大小(照明区域),指数定义了光的衰减速度和距离(范围)。
// 参数:名字,位置,方向,角度,指数,场景
var light = new BABYLON.SpotLight("spotLight", new BABYLON.Vector3(0, 30, -10), new BABYLON.Vector3(0, -1, 0), Math.PI / 3, 2, scene);
也就是周围环境光
// 参数:名字, 方向,场景
var light = new BABYLON.HemisphericLight("HemiLight", new BABYLON.Vector3(0, 1, 0), scene);
颜色。。
限制:
默认一个场景总最多有4个灯光
var material = new BABYLON.StandardMaterial("mat", scene);
material.maxsimultaneousLights = 6;
打开:
light.setEnabled(true);
关闭:
light.setEnabled(false);
强度:
light0.intensity = 0.5;
light1.intensity = 2.4;
光的范围:
适用于点光与聚光灯
light.range = 100;
Choosing Meshes to Light?
当一个光被创造出来时,所有的现有网格都将被它点亮。有两种方法可以将一些网格从被点亮的地方排除出去。
一个网格可以添加到独占的网格数组中,或者添加不被排除在包含的列表中。
被排除的网格的数量可能是决定使用哪种方法的一个因素。
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere", {}, scene); //默认的球体sphere
var mySphere = BABYLON.MeshBuilder.CreateSphere("mySphere", {diameter: 2, diameterX: 3}, scene);
//optinon: segments(段数) diameter(直径)diameterX diameterY diameterZ arc(弧度)slice(切片) updatable(可更新的) sideOrientation(边方向)
var box = BABYLON.MeshBuilder.CreateBox("box", {}, scene); //默认的盒子box
var myBox = BABYLON.MeshBuilder.CreateBox("myBox", {height: 5, width: 2, depth: 0.5}, scene);
//option: size height width depth(深度) faceColors(面颜色,6种颜色的数组,每个框面一个)faceUV(面UV,6个矢量阵列,每个盒子面一个) updatable(可更新的) sideOrientation(边方向)
// 参数:名字,配置{
// height?: number; 高度 默认2
// diameterTop?: number; 上直径 默认1
// diameterBottom?: number; 下直径 默认1
// diameter?: number; 直径 !=0 会被上两个覆盖
// tessellation?: number; 镶嵌(即圆柱边数量,默认24) 3为三棱柱,4为四棱柱
// subdivisions?: number; 细分 沿着圆柱体高度设置环的数量,默认1 ?没用到过
// arc?: number;
// faceColors?: BABYLON.Color4[];
// ... 7 more ...;
// backUVs?: BABYLON.Vector4;}
const roof = BABYLON.MeshBuilder.CreateCylinder("roof", {diameter: 1.3, height: 1.2, tessellation: 3});
roof.scaling.x = 0.75;
roof.rotation.z = Math.PI / 2;
roof.position.y = 1.22;
var plane = BABYLON.MeshBuilder.CreatePlane("plane", {}, scene); //默认的平面plane
var myPlane = BABYLON.MeshBuilder.CreatePlane("myPlane", {width: 5, height: 2}, scene);
//option: size width height updatable(可更新的) sideOrientation(边方向)
// frontUVs(前UV,只有当边方向设置为 sideOrientation:BABYLON.Mesh.DOUBLESIDE)
// backUVs(后UV,只有当边方向设置为 sideOrientation:BABYLON.Mesh.DOUBLESIDE)
// sourcePlane(源平面,源平面(数学)网格将被转换为)
// sourcePlane是一个平面网格的独特选择,它提供了一种方法来定向和定位它。
var ground = BABYLON.MeshBuilder.CreateGround("ground", {}, scene); //默认的地面ground
var myGround = BABYLON.MeshBuilder.CreateGround("myGround", {width: 6, height: 4, subdivsions: 4}, scene);
const largeGround = BABYLON.MeshBuilder.CreateGroundFromHeightMap(
"largeGround",
"https://assets.babylonjs.com/environments/villageheightmap.png",
{
width:150,
height:150,
subdivisions: 20,// 细分:棱角分明程度,数值越大坡度越大,越棱角分明
minHeight:0,
maxHeight: 10
}
);
const largeGroundMat = new BABYLON.StandardMaterial("largeGroundMat");
largeGroundMat.diffuseTexture = new BABYLON.Texture("url to large ground texture");
largeGround.material = largeGroundMat;
largeGround.position.y = -0.01; // 确保两个地面不发生冲突并引起闪烁。
var myPoints = [];
var point1 = new BABYLON.Vector3(0, 0, 0);
myPoints.push(point1);
var point2 = new BABYLON.Vector3(0, 1, 1);
myPoints.push(point2);
var point3 = new BABYLON.Vector3(0, 1, 0);
myPoints.push(point3);
//或者
var myPoints =[
new BABYLON.Vector3(0, 0, 0),
new BABYLON.Vector3(0, 1, 1),
new BABYLON.Vector3(0, 1, 0)
];
//然后,这些点的数组必须传递给点选项
//创建线lines
var lines = BABYLON.MeshBuilder.CreateLines("lines", {points: myPoints}, scene);
//您可以创建虚线CreateDashedLines,并将破折号dashNb的数量作为一个选项。
在x,y正半轴的象限中绘制半个图像轮廓,然后沿着y轴旋转所形成的图形
const fountainProfile = [
new BABYLON.Vector3(0, 0, 0),
new BABYLON.Vector3(10, 0, 0),
new BABYLON.Vector3(10, 4, 0),
new BABYLON.Vector3(8, 4, 0),
new BABYLON.Vector3(8, 1, 0),
new BABYLON.Vector3(1, 2, 0),
new BABYLON.Vector3(1, 15, 0),
new BABYLON.Vector3(3, 17, 0)
];
// sideOrientain属性将网格设置为双面,因为内部可见,而且顶部和中空部分是倾斜的
const fountain = BABYLON.MeshBuilder.CreateLathe("fountain", {shape: fountainProfile, sideOrientation: BABYLON.Mesh.DOUBLESIDE}, scene);
创建一个挤压形状网格。
挤压是一个参数形状。
它没有预定义的形状。
它的最终形状将取决于输入参数。
//shape to extrude
const lampShape = [];
for(let i = 0; i < 20; i++) {
lampShape.push(new BABYLON.Vector3(Math.cos(i * Math.PI / 10), Math.sin(i * Math.PI / 10), 0));
}
lampShape.push(lampShape[0]); //close shape
//extrusion path
const lampPath = [];
lampPath.push(new BABYLON.Vector3(0, 0, 0));
lampPath.push(new BABYLON.Vector3(0, 10, 0));
for(let i = 0; i < 20; i++) {
lampPath.push(new BABYLON.Vector3(1 + Math.cos(Math.PI - i * Math.PI / 40), 10 + Math.sin(Math.PI - i * Math.PI / 40), 0));
}
lampPath.push(new BABYLON.Vector3(3, 11, 0));
const yellowMat = new BABYLON.StandardMaterial("yellowMat");
yellowMat.emissiveColor = BABYLON.Color3.Yellow();
//extrude lamp
const lamp = BABYLON.MeshBuilder.ExtrudeShape("lamp", {cap: BABYLON.Mesh.CAP_END, shape: lampShape, path: lampPath, scale: 0.5});
ExtrudeShape中options的参数:
pilot.position = new BABYLON.Vector3(2, 3, 4);
//或者
pilot.position.x = 2;
pilot.position.y = 3;
pilot.position.z = 4;
pilot.rotation = new BABYLON.Vector3(alpha, beta, gamma);
//或者
pilot.rotation.x = alpha; //绕x轴旋转
pilot.rotation.y = beta; //绕y轴旋转
pilot.rotation.z = gamma; //绕z轴旋转
box.rotation.y = Math.PI / 4;
box.rotation.y = BABYLON.Tools.ToRadians(45);// 以度为单位,与上一行效果相同
//沿着x轴、y轴和z轴进行缩放
mesh.scaling = new BABYLON.Vector3(scale_x, scale_y, scale_z);
//或单独设置
mesh.scaling.y = 5;
//创建一个材质使用: 参数:名字, 场景(可选,默认当前场景)
var myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
var 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;
var myMaterial = new BABYLON.StandardMaterial("myMaterial", scene);
myMaterial.diffuseTexture = new BABYLON.Texture("https://assets.babylonjs.com/environments/roof.jpg", 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;
myMaterial.backFaceCulling = true;//默认为false,一般情况下不渲染材质的背面,设为true即渲染材质背面,如天空盒子的应用
materialSphere1.wireframe = true;//你可以在线框模式下看到一个网格,如碰撞检测时可见网格的应用
向场景添加图形用户界面。
将其加载到页面:
<script>https://cdn.babylonjs.com/gui/babylon.gui.min.js</script>
可通过设置光照强度来改变白天和黑夜。
先进的动态纹理
// 基于全屏创建GUI
const adt = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI");
// 创建控制面板,设置其位置
const panel = new BABYLON.GUI.StackPanel();
panel.width = "220px";
panel.top = "-25px";
panel.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT;
panel.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM;
adt.addControl(panel); // 并将adt GUI对象的控制权交给panel
// 文本面板
const header = new BABYLON.GUI.TextBlock();
header.text = "Night to Day";
header.height = "30px";
header.color = "white";
panel.addControl(header);
// 滑块
const slider = new BABYLON.GUI.Slider();
slider.minimum = 0;
slider.maximum = 1;
slider.borderColor = "black";
slider.color = "gray";
slider.background = "white";
slider.value = 1;
slider.height = "20px";
slider.width = "200px";
// 为滑块添加数据改变事件
slider.onValueChangedObservable.add((value) => {
if (light) {
light.intensity = value;
}
});
panel.addControl(slider);
动画是由一系列图像,帧构成的,它们依次显示。
尽管我们可以在Babylon.js中将整个成品视为动画,但动画也是一个特定的对象,它详细说明了可以应用于任何网格,相机或灯光的变换,定时和循环。
滑动滑块:
const box = BABYLON.MeshBuilder.CreateBox("box", {});
box.position.x = 2;
// 帧率
const frameRate = 10;
// 参数:名称,动画属性, 帧/秒,动画数据类型,动画循环模式
const xSlide = new BABYLON.Animation("xSlide", "position.x", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
// 关键帧数组
const keyFrames = [];
keyFrames.push({
frame: 0,
value: 2
});
keyFrames.push({
frame: frameRate,
value: -2
});
keyFrames.push({
frame: 2 * frameRate,
value: 2
});
xSlide.setKeys(keyFrames);
box.animations.push(xSlide);
// 参数: 动画对象,开始动画的帧,动画结束的帧,是否循环动画
scene.beginAnimation(box, 0, 2 * frameRate, true);
// 将多个动画应用于同一个目标的方法:
// 参数:动画对象,动画数组[],开始动画的帧,动画结束的帧,是否循环动画
scene.beginDirectAnimation(target, animations, from, to, loop)
动画数据类型的值:
循环模式的值:
动画对象的方法:
var frameRate = 20;
// 相机旋转动画 0-9s不动,9-14s旋转180°
var rotate = new BABYLON.Animation("rotate", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
var rotate_keys = [];
rotate_keys.push({
frame: 0,
value: 0
});
rotate_keys.push({
frame: 9 * frameRate,
value: 0
});
rotate_keys.push({
frame: 14 * frameRate,
value: Math. PI
});
rotate.setKeys(rotate_keys);
//相机移动动画:0-3s移动,3-5s不动,5-8s移动
var movein = new BABYLON.Animation("movein", "position", frameRate, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
var movein_keys = [];
movein_keys.push({
frame: 0,
value: new BABYLON.Vector3(0, 5, -30)
});
movein_keys.push({
frame: 3 * frameRate,
value: new BABYLON.Vector3(0, 2, -10)
});
movein_keys.push({
frame: 5 * frameRate,
value: new BABYLON.Vector3(0, 2, -10)
});
movein_keys.push({
frame: 8 * frameRate,
value: new BABYLON.Vector3(-2, 2, 3)
});
movein.setKeys(movein_keys);
// 门的打开与关闭:0-3s不动,3-5s旋转60°,5-13s不动,13-15s旋转-60°
var sweep = new BABYLON.Animation("sweep", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
var sweep_keys = [];
sweep_keys.push({
frame: 0,
value: 0
});
sweep_keys.push({
frame: 3 * frameRate,
value: 0
});
sweep_keys.push({
frame: 5 * frameRate,
value: Math.PI/3
});
sweep_keys.push({
frame: 13 * frameRate,
value: Math.PI/3
});
sweep_keys.push({
frame: 15 * frameRate,
value: 0
});
sweep.setKeys(sweep_keys);
// 光的明暗:0-7s暗,7-10s变亮,10-14亮,14-15变暗
var lightDimmer = new BABYLON.Animation("dimmer", "intensity", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);
var light_keys = [];
light_keys.push({
frame: 0,
value: 0
});
light_keys.push({
frame: 7 * frameRate,
value: 0
});
light_keys.push({
frame: 10 * frameRate,
value: 1
});
light_keys.push({
frame: 14 * frameRate,
value: 1
});
light_keys.push({
frame: 15 * frameRate,
value: 0
});
lightDimmer.setKeys(light_keys);
// 运行动画
scene.beginDirectAnimation(camera, [movein, rotate], 0, 25 * frameRate, false);
scene.beginDirectAnimation(hinge, [sweep], 0, 25 * frameRate, false);
scene.beginDirectAnimation(spotLights[0], [lightDimmer], 0, 25 * frameRate, false);
scene.beginDirectAnimation(spotLights[1], [lightDimmer.clone()], 0, 25 * frameRate, false);
AnAnimationGroup允许您将动画和网格链接在一起并进行播放,暂停和停止。
// 创建动画组
var animationGroup1 = new BABYLON.AnimationGroup("Group1");
// 向动画组中添加动画,并与网格连接
animationGroup1.addTargetedAnimation(animation1, mesh1);
animationGroup1.addTargetedAnimation(animation3, mesh1);
animationGroup1.addTargetedAnimation(animation2, mesh2);
// 由于动画可能是用不同的时间线创建的,因此必须使用规范化来对齐它们
// 从第0帧到第100帧,第一个参数不能小于动画组中所有动画的最小帧,第二个参数不能大于动画组中所有动画的最大帧
animationGroup1.normalize(0, 100);
// 可以为动画组中所以动画设置组速比speedRadio,用来加快或减慢动画
animationGroup1.speedRatio = 0.25;
// 从现有的动画制作组中创建一个组
var animationGroup = new BABYLON.AnimationGroup("my-animation-group");
for (anim of idleAnim.getAnimations()) {
animationGroup.addTargetedAnimation(anim.animation, anim.target);
}
// 动画结束后触发的回调函数:
animationGroup1.onAnimationEndObservable.add(function() {
mesh2.material = redMaterial;
});
// 有一个onAnimationLoopObservable对象,它可以在每个动画循环时触发一个函数。(组循环完成后,每个动画对象都会触发一次该函数)
animationGroup1.onAnimationLoopObservable.add(function(targetAnimation) {
console.log(targetAnimation.animation.name);
});
// 还有一个onAnimationGroupLoopObservable对象,它可以在组的所有动画都已循环完成时触发一个函数:
animationGroup1.onAnimationGroupLoopObservable.add(function(group) {
console.log("Group looped!");
});
为了使一个动画跟随另一个动画,则需要将另一个参数添加到beginDirectAnimation函数中。此参数本身是由beginDirectAnimation开始的动画结束时要调用的函数。
scene.beginAnimation(目标,开始帧,结束帧,循环,速度,动画结束时);
var frameRate = 10;
var xSlide = new BABYLON.Animation("xSlide", "position.x", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
var keyFramesP = [];
keyFramesP.push({
frame: 0,
value: 2
});
keyFramesP.push({
frame: frameRate,
value: -2
});
keyFramesP.push({
frame: 2 * frameRate,
value: 2
});
xSlide.setKeys(keyFramesP);
var yRot = new BABYLON.Animation("yRot", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);
var keyFramesR = [];
keyFramesR.push({
frame: 0,
value: 0
});
keyFramesR.push({
frame: 2.5 * frameRate,
value: 2 * Math.PI
});
keyFramesR.push({
frame: 5 * frameRate,
value: 4 * Math.PI
});
yRot.setKeys(keyFramesR);
var nextAnimation = function() {
scene.beginDirectAnimation(box, [yRot, xSlide], 0, 2 * frameRate, true);
}
scene.beginDirectAnimation(box, [yRot], 0, 2 * frameRate, false, 1, nextAnimation);
在Blender中创建角色,使用Mixamo向其中添加动画并将其组合为单个模型,再到通过键盘控制动画角色的移动。
有不同的工具和可能的工作流程来创建可以在Babylon.js项目中使用的动画模型。
以下是此操作方法的详细工作流程:
官方文档:https://doc.babylonjs.com/divingDeeper/animation/animatedCharacter
没看懂:
Babylon.js声音引擎基于Web音频规范。
我们决定不对音频标签或其他机制提供后备支持。因此,要使用我们的声音引擎,您需要使用与Web Audio兼容的浏览器。
不过,如果您在不兼容的浏览器上使用它,它将不会破坏我们引擎的其余部分,它只是不播放声音。
声音引擎提供环境声音,空间声音和定向声音。可以通过代码或加载.babylon文件来创建它。它遵循了您将要看到的其他引擎的简单而强大的原理。
支持的声音格式是浏览器中的一种。通常至少是**.mp3和.wav**。
环境声音:
// 加载声音并在准备好后自动播放
// 参数:名称, url, 场景, 加载完成后的回调函数, 配置{循环与自动播放等}
var music = new BABYLON.Sound("Music", "music.wav", scene, null, {
loop: true,
autoplay: true,
// volume: volume, // 音量
// spatialSound: true // 是否为空间声音
});
// 回调函数的使用
var music = new BABYLON.Sound("Music", "music.wav", scene, function() {
//声音已下载并解码,参数:number(几秒后播放)
music.play();
});
单击鼠标或按下按键时播放声音:
var gunshot = new BABYLON.Sound("gunshot", "sounds/gunshot.wav", scene);
window.addEventListener("mousedown", function(evt) {
// left click to fire
if (evt.button === 0) {
gunshot.play();
gunshot.setVolume(volume);// 调整音量
}
});
window.addEventListener("keydown", function(evt) {
// Press space key to fire
if (evt.keyCode === 32) {
gunshot.play();
}
});
设置全局音量:
BABYLON.Engine.audioEngine.setGlobalVolume(0.5);
同时播放多个声音并同步:
var music1 = new BABYLON.Sound(
"Violons11",
"sounds/violons11.wav",
scene,
soundReady,
{ loop: true }
);
var music2 = new BABYLON.Sound(
"Violons18",
"sounds/violons18.wav",
scene,
soundReady,
{ loop: true }
);
var music3 = new BABYLON.Sound(
"Cellolong",
"sounds/cellolong.wav",
scene,
soundReady,
{ loop: true }
);
var soundsReady = 0;
function soundReady() {
soundsReady++;
if (soundsReady === 3) {
music1.play();
music2.play();
music3.play();
}
}
空间声音
// 创建空间声音
var music = new BABYLON.Sound("music", "music.wav", scene, null, {
loop: true,
autoplay: true,
spatialSound: true
});
空间声音的默认属性是:
maxDistance仅在使用“线性”衰减时使用。否则,您可以使用rolloffFactor和refDistance选项调整其他模型的衰减。默认情况下两者都设置为1,但是您当然可以更改它。
例如:
var music = new BABYLON.Sound("music", "music.wav", scene, null, {
loop: true,
autoplay: true,
spatialSound: true,
distanceModel: "exponential",
rolloffFactor: 2 // 下降因子
});
声音在3D世界中的默认位置是(0,0,0)
。要更改此设置,请使用以下setPosition()
功能:
music.setPosition(new BABYLON.Vector3(100, 0, 0));
将声音附加到网格
var music = new BABYLON.Sound("Violons", "sounds/violons11.wav", scene, null, {
loop: true,
autoplay: true
});
// Sound will now follow the box mesh position
music.attachToMesh(box);
将位置设为音频监听器
默认情况下,场景的“耳朵”(即听众)始终是当前活动的摄像机。有时,例如在制作第三人称游戏时,您可能需要将另一个网格设置为侦听器-例如角色头像。这可以通过audioListenerPositionProvider
在场景上设置属性来实现。
您创建的方法必须返回一个有效的Vector3
对象。
//返回静态位置
scene.audioListenerPositionProvider = () => {
return new BABYLON.Vector3(0, 0, 10);
};
//返回网格的当前位置
//!建议使用'absolutePosition'属性
//反映网格在世界上的位置
scene.audioListenerPositionProvider = () => {
//返回静态位置
return myMesh.absolutePosition;
};
要切换回使用相机作为侦听器,只需将属性设置为null
。
创建定向3D声音
默认情况下,空间声音是全向的。但是,如果您愿意,可以有定向声音。
**注意:**定向声音仅适用于连接到网格的空间声音。
var music = new BABYLON.Sound("Violons", "violons11.wav", scene, null, {
loop: true,
autoplay: true
});
// 设置定向锥,参数:内圆锥的大小(以度为单位),外部圆锥的大小(以度为单位),当您位于外部圆锥体之外时的声音音量(介于0.0和1.0之间)
music.setDirectionalCone(90, 180, 0);
music.setLocalDirectionToMesh(new BABYLON.Vector3(1, 0, 0));
music.attachToMesh(box);
锥体的外角必须大于或等于内角,否则将记录错误,并且定向声音将不起作用。
setLocalDirectionToMesh()
只是与您连接到的网格有关的圆锥的方向。默认情况下为(1,0,0)
。
创建自己的自定义衰减
如果要使用特定算法管理衰减(或Web Audio中的距离模型),则可以使用Babylon.js自定义衰减功能绕过本机Web Audio衰减。
注意: Web Audio是硬件加速的。这意味着它主要由设备上的专用音频芯片通过本机代码(浏览器)处理。这样一来,在3D实时渲染的性能方面几乎不会花费任何成本。切换到自定义衰减将使用基于JavaScript的Babylon.js距离计算,并且速度较慢。
而且,自定义衰减仅适用于空间声音(显然),而且还适用于连接到Babylon.js网格的声音。就是说,现在让我们查看执行此操作的代码。首先,必须在选项中指定它:
//创建并加载异步声音
var music = new BABYLON.Sound("Music", "music.wav", scene, null, {
loop: true,
autoplay: true,
useCustomAttenuation: true
});
您将切换到内部Babylon.js数学计算。默认的自定义衰减功能是线性的。
要创建自己的逻辑,您需要以下代码:
//创建自定义衰减功能。在对象附近,音量几乎为0。
//最远,更大声
music.setAttenuationFunction(function(
currentVolume,
currentDistance,
maxDistance,
refDistance,
rolloffFactor
) {
return (currentVolume * currentDistance) / maxDistance;
});
您可以使用这5个参数进行操作,并随心所欲地进行操作。只需返回一个数字,该数字将成为应用于声音的音量。
在此示例中,逻辑有点奇怪,因为您离网格越远,音量越大。
而且,Firefox当前在其Web Audio实现中存在一个错误,无法正确处理线性衰减。这可以通过使用Babylon.js默认的线性自定义衰减来解决。
处理从.babylon文件加载的声音
当前,只有我们的3DS Max导出器可以将声音直接导出到*.babylon*。
要访问由Babylon.js .babylon文件加载器加载的声音,您需要getSoundByName()
在场景对象上使用该函数。
这是一个简单的示例,加载了嵌入一些声音的*.babylon*场景文件:
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
BABYLON.SceneLoader.Load(
"TestScene/",
"testsound.babylon",
engine,
function(newScene) {
newScene.executeWhenReady(function() {
newScene.activeCamera.attachControl(canvas);
var gunshotSound = newScene.getSoundByName("gunshot-1.wav");
window.addEventListener("keydown", function(evt) {
if (evt.keyCode === 32 && gunshotSound) {
gunshotSound.play();
}
});
engine.runRenderLoop(function() {
newScene.render();
});
});
},
function(progress) {
// To do: give progress feedback to user
}
);
按下空格键将播放枪声。
使用音轨
将您的音乐和声音隔离在多个轨道上,以更好地管理声音分组实例中的音量可能很有用。将来的发行版中还将使用它在特定轨道上应用效果。
默认情况下,Babylon.js创建一个BABYLON.SoundTrack
对象作为其主轨道。每次创建新内容时BABYLON.Sound
,都会将其添加到该主轨道中。
var soundTrack = new BABYLON.SoundTrack(scene);
soundTrack.AddSound(cellolong);
soundTrack.AddSound(violons11);
使用此代码,大提琴和violons11声音将从Babylon.js主音轨移至该特定音轨。现在,这意味着您可以独立于主音轨更改此音轨的音量,并因此更改这两种声音的音量。
该AddSound()
功能会将声音从其原始容器(主轨道或特定轨道)移至指定的新声音轨道。例如,使用以下代码:
var soundTrack1 = new BABYLON.SoundTrack(scene);
soundTrack1.AddSound(cellolong);
soundTrack1.AddSound(violons11);
var soundTrack2 = new BABYLON.SoundTrack(scene);
soundTrack2.AddSound(violons11);
该violons11声音将最终只能住在soundTrack2。
使用分析仪
您可以轻松地实时分析音频频率。
理解它如何工作的最简单的代码是:
var myAnalyser = new BABYLON.Analyser(scene);
BABYLON.Engine.audioEngine.connectToAnalyser(myAnalyser);
myAnalyser.drawDebugCanvas();
这将连接到音频引擎的整体音量,并将一起播放的所有声音的频率绘制到屏幕顶部的2D画布显示中。
您可以更改调试画布的位置和大小,并在声轨上使用分析器来代替全局音频引擎:
var myAnalyser = new BABYLON.Analyser(scene);
soundTrack1.connectToAnalyser(myAnalyser);
myAnalyser.DEBUGCANVASSIZE.width = 160;
myAnalyser.DEBUGCANVASSIZE.height = 100;
myAnalyser.DEBUGCANVASPOS.x = 40;
myAnalyser.DEBUGCANVASPOS.y = 30;
myAnalyser.drawDebugCanvas();
您也可以称自己为分析器函数来创建自己的用法。
一个基于组件的工具。
行为是一个简单的类,可以附加到目标上,在目标上它将提供一组特定的功能。功能将由定义的事件触发。
行为由以下接口定义:
name
:返回行为的名称init()
:需要初始化行为时,将调用此函数。这是在附加到目标之前。attach(target)
:将行为附加到目标时,将调用此函数。这是行为将挂接到有用事件的地方。当调用此函数时,Babylon.js将确保当前未加载场景。detach()
:当行为与目标分离时,将调用此函数。该行为必须清除所有关联的资源并解开所有事件如果行为依赖动画,则以下静态属性将可用:
EasingFunction
:定义使用动画的缓动功能EasingMode
:定义动画使用的缓动模式您可以将行为添加到实现IBehaviorAware接口的任何对象(例如,灯光,摄像头或网格)。每个IBehaviorAware都提供以下入口点:
addBehavior(behavior)
:使用此功能可将行为附加到给定的目标。如果场景当前正在加载,则此代码将延迟到场景完成为止。removeBehavior(behavior)
:使用此功能可以将行为与目标分离getBehaviorByName(name)
:返回具有给定名称的行为;如果未找到,则返回nullbehaviors
:此只读属性返回与目标关联的行为列表大多数时候,行为是针对特定目标的行为而设计的。
既有相机行为也有网格行为。
弹跳行为
弹跳行为(BABYLON.BouncingBehavior
)旨在在ArcRotateCamera
到达lowerRadiusLimit
或时产生较小的弹跳效果upperRadiusLimit
。
可以使用以下属性配置此行为:
transitionDuration
:定义动画的持续时间(以毫秒为单位)。默认值为450ms。lowerRadiusTransitionRange
:定义到达下半径时过渡所设置的动画距离的长度。预设值为2。upperRadiusTransitionRange
:定义到达上半径时过渡所设置的动画距离的长度。默认值为-2。autoTransitionRange
:定义一个值,该值指示是否自动定义lowerRadiusTransitionRange
和upperRadiusTransitionRange
。过渡范围将设置为世界空间中边界框对角线的5%。要在上启用此行为ArcRotateCamera
:
camera.useBouncingBehavior = true;
自动旋转行为
autoRotation行为(BABYLON.AutoRotationBehavior
)用于在ArcRotateCamera
没有用户交互时创建的平滑旋转。
可以使用以下属性配置此行为:
idleRotationSpeed
:相机围绕网格旋转的速度idleRotationWaitTime
:用户互动之后,相机开始旋转之前要等待的时间(以毫秒为单位)idleRotationSpinupTime
:旋转达到完全怠速所需的时间(毫秒)zoomStopsAnimation
:用于指示用户缩放是否应停止动画的标志要在上启用此行为ArcRotateCamera
:
camera.useAutoRotationBehavior = true;
取景行为
框架行为(BABYLON.FramingBehavior
)旨在ArcRotateCamera
在将目标设置为网格时自动定位。如果要防止相机在虚拟水平面下移动,此功能也很有用。
可以使用以下属性配置此行为:
mode
:行为可以配置为:BABYLON.FramingBehavior.IgnoreBoundsSizeMode
:相机可以一直移向网格BABYLON.FramingBehavior.FitFrustumSidesMode
:不允许相机缩放到比调整的包围球接触视锥面的点更靠近网格的位置radiusScale
:定义应用于半径的比例(默认为1)positionY
:定义主网格物体相对于相机焦点的Y偏移(默认为0)defaultElevation
:定义当触发返回默认高程空转行为时要返回的水平面上方/下方的角度(以弧度为单位)(默认值为0.3)elevationReturnTime
:定义返回默认Beta位置(默认为1500)所用的时间(以毫秒为单位)。负值表示相机不应恢复为默认值。elevationReturnWaitTime
:定义相机返回默认Beta位置(默认为1000)之前的延迟(以毫秒为单位)zoomStopsAnimation
:定义用户缩放是否应停止动画framingTime
:定义框架时的过渡时间,以毫秒为单位(默认为1500)要在上启用此行为ArcRotateCamera
:
camera.useFramingBehavior = true;
目行为是行为可被附连到一个网格。
指针拖动行为
这用于使用鼠标或vr控制器围绕平面或轴拖动网格。
var pointerDragBehavior = new BABYLON.PointerDragBehavior({dragAxis: new BABYLON.Vector3(0,1,0)});
可以在3种不同的模式下进行初始化
默认情况下,拖动平面/轴时将通过修改对象的方向属性来进行。为了使指定的轴/平面固定在世界上,请将其设置为false。
pointerDragBehavior.useObjectOrientationForDragging = false;
默认情况下,拖动平面将在每帧更新,以禁用此设置updateDragPlane为false
pointerDragBehavior.updateDragPlane = false;
监听拖动事件:
// 拖动开始
pointerDragBehavior.onDragStartObservable.add((event)=>{
console.log("dragStart");
console.log(event);
})
// 拖动时
pointerDragBehavior.onDragObservable.add((event)=>{
console.log("drag");
console.log(event);
})
// 拖动完成
pointerDragBehavior.onDragEndObservable.add((event)=>{
console.log("dragEnd");
console.log(event);
})
要在不移动附加网格的情况下使用拖动行为,请将moveAttached设置为false。然后可以侦听上面的拖动事件,以允许自定义拖动交互。
pointerDragBehavior.moveAttached = false;
要禁用所有拖动行为,请将enabled设置为false
pointerDragBehavior.enabled = false;
要检查拖动网格的当前状态,可以检查currentDraggingPointerID,dragging和lastDragPosition
// 当前正在与行为进行交互的指针的ID(当没有指针处于活动状态时为-1)
pointerDragBehavior.currentDraggingPointerID;
// 指针在世界空间中撞击拖曳平面的最后位置
pointerDragBehavior.lastDragPosition;
// 如果行为当前处于拖动状态
pointerDragBehavior.dragging;
SixDofDrag行为
用于基于指针原点(例如,摄像机或VR控制器位置)在3D空间中拖动网格
var sixDofDragBehavior = new BABYLON.SixDofDragBehavior();
默认情况下,通过缓慢将网格移动到指针所指向的位置来平滑指针抖动。要删除或修改此行为,可以修改以下字段。
//朝目标拖动位置移动每一帧的距离。这对于避免抖动很有用。将此设置为1不会延迟。(默认值:0.2)
sixDofDragBehavior.dragDeltaRatio = 0.2;
默认情况下,将对象拖动到远处/向您的方向放大将使移动对象的较大距离更容易。为了避免/修改这一点,可以使用以下内容。
//朝目标拖动位置移动每一帧的距离。这对于避免抖动很有用。将此设置为1不会延迟。(默认值:0.2)
sixDofDragBehavior.zDragFactor = 0.2;
注–为避免在具有复杂几何形状的模型上使用时对性能产生重大影响,应将对象包装在边界框网格中。请参见BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox
举例:
var createScene = function () {
// Create basic world
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -5), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
ground.position.y= -1
scene.createDefaultVRExperience({floorMeshes:[]})
BABYLON.SceneLoader.LoadAssetContainer("https://models.babylonjs.com/", "seagulf.glb", scene, function (container) {
// Add loaded file to the scene
container.addAllToScene();
// Scale and position the loaded model (First mesh loaded from gltf is the root node)
container.meshes[0].scaling.scaleInPlace(0.002)
// wrap in bounding box mesh to avoid picking perf hit
var gltfMesh = container.meshes[0]
var boundingBox = BABYLON.BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(gltfMesh)
// Create bounding box gizmo
var utilLayer = new BABYLON.UtilityLayerRenderer(scene)
utilLayer.utilityLayerScene.autoClearDepthAndStencil = false;
var gizmo = new BABYLON.BoundingBoxGizmo(BABYLON.Color3.FromHexString("#0984e3"), utilLayer)
gizmo.attachedMesh = boundingBox;
// // Create behaviors to drag and scale with pointers in VR
var sixDofDragBehavior = new BABYLON.SixDofDragBehavior()
boundingBox.addBehavior(sixDofDragBehavior)
var multiPointerScaleBehavior = new BABYLON.MultiPointerScaleBehavior()
boundingBox.addBehavior(multiPointerScaleBehavior)
});
return scene;
};
MultiPointerScale行为
这用于基于2个指针(例如,手指或VR控制器)缩放网格
var multiPointerScaleBehavior = new BABYLON.MultiPointerScaleBehavior();
注–为避免在具有复杂几何形状的模型上使用时对性能产生重大影响,应将对象包装在边界框网格中。请参见BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox
举例:
var createScene = function () {
// Create basic world
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -5), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
ground.position.y= -1
scene.createDefaultVRExperience({floorMeshes:[]})
BABYLON.SceneLoader.LoadAssetContainer("https://models.babylonjs.com/", "seagulf.glb", scene, function (container) {
// Add loaded file to the scene
container.addAllToScene();
// Scale and position the loaded model (First mesh loaded from gltf is the root node)
container.meshes[0].scaling.scaleInPlace(0.002)
// wrap in bounding box mesh to avoid picking perf hit
var gltfMesh = container.meshes[0]
var boundingBox = BABYLON.BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(gltfMesh)
// Create bounding box gizmo
var utilLayer = new BABYLON.UtilityLayerRenderer(scene)
utilLayer.utilityLayerScene.autoClearDepthAndStencil = false;
var gizmo = new BABYLON.BoundingBoxGizmo(BABYLON.Color3.FromHexString("#0984e3"), utilLayer)
gizmo.attachedMesh = boundingBox;
// // Create behaviors to drag and scale with pointers in VR
var sixDofDragBehavior = new BABYLON.SixDofDragBehavior()
boundingBox.addBehavior(sixDofDragBehavior)
var multiPointerScaleBehavior = new BABYLON.MultiPointerScaleBehavior()
boundingBox.addBehavior(multiPointerScaleBehavior)
});
return scene;
};
AttachToBoxBehavior(AppBar)
这用于将网格或UI附加到网格边界框的顶部
var behavior = new BABYLON.AttachToBoxBehavior(appBar);
boundingBox.addBehavior(behavior);
使用调整附着的网格的位置
behavior.distanceAwayFromFace = 0.15;
behavior.distanceAwayFromBottomOfFace = 0.15;
这可用于将应用程序栏附加到网格
举例:
var createScene = function () {
// Create basic world
var scene = new BABYLON.Scene(engine);
var camera = new BABYLON.FreeCamera("camera1", new BABYLON.Vector3(0, 5, -5), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene);
var ground = BABYLON.Mesh.CreateGround("ground1", 6, 6, 2, scene);
ground.position.y= -1
scene.createDefaultVRExperience({floorMeshes:[]})
BABYLON.SceneLoader.LoadAssetContainer("https://models.babylonjs.com/", "seagulf.glb", scene, function (container) {
// Create the 3D UI manager
var manager = new BABYLON.GUI.GUI3DManager(scene);
// Add loaded file to the scene
container.addAllToScene();
// Scale and position the loaded model (First mesh loaded from gltf is the root node)
container.meshes[0].scaling.scaleInPlace(0.002)
// wrap in bounding box mesh to avoid picking perf hit
var gltfMesh = container.meshes[0]
var boundingBox = BABYLON.BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(gltfMesh)
// Create bounding box gizmo
var utilLayer = new BABYLON.UtilityLayerRenderer(scene)
utilLayer.utilityLayerScene.autoClearDepthAndStencil = false;
var gizmo = new BABYLON.BoundingBoxGizmo(BABYLON.Color3.FromHexString("#0984e3"), utilLayer)
gizmo.attachedMesh = boundingBox;
// Create behaviors to drag and scale with pointers in VR
var sixDofDragBehavior = new BABYLON.SixDofDragBehavior()
boundingBox.addBehavior(sixDofDragBehavior)
var multiPointerScaleBehavior = new BABYLON.MultiPointerScaleBehavior()
boundingBox.addBehavior(multiPointerScaleBehavior)
// Create app bar
var appBar = new BABYLON.TransformNode("");
appBar.scaling.scaleInPlace(0.2)
var panel = new BABYLON.GUI.PlanePanel();
panel.margin = 0;
panel.rows = 1;
manager.addControl(panel);
panel.linkToTransformNode(appBar);
for (var index = 0; index < 2; index++) {
var button = new BABYLON.GUI.HolographicButton("orientation");
panel.addControl(button);
button.text = "Button #" + panel.children.length;
if(index == 0){
button.onPointerClickObservable.add(()=>{
if(gizmo.attachedMesh){
gizmo.attachedMesh = null;
boundingBox.removeBehavior(sixDofDragBehavior)
boundingBox.removeBehavior(multiPointerScaleBehavior)
}else{
gizmo.attachedMesh = boundingBox;
boundingBox.addBehavior(sixDofDragBehavior)
boundingBox.addBehavior(multiPointerScaleBehavior)
}
})
}
}
//attach app bar to bounding box
var behavior = new BABYLON.AttachToBoxBehavior(appBar);
boundingBox.addBehavior(behavior);
});
return scene;
};
// 参数:型号名称,文件夹路径,文件名,场景
BABYLON.SceneLoader.ImportMeshAsync(model name, folder path, file name, scene);
BABYLON.SceneLoader.ImportMeshAsync("", "/relative path/", "myFile"); //清空所有网格物体的字符串
BABYLON.SceneLoader.ImportMeshAsync("model1", "/relative path/", "myFile"); //一个模型的模型名称
BABYLON.SceneLoader.ImportMeshAsync(["model1", "model2"], "/relative path/", "myFile"); //模型名称数组
// 导入之后做事情:
BABYLON.SceneLoader.ImportMeshAsync("", "/relative path/", "myFile").then((result) => {
result.meshes[1].position.x = 20;
const myMesh_1 = scene.getMeshByName("myMesh_1");
myMesh1.rotation.y = Math.PI / 2;
});
// 参数:名字,声音文件路径, 场景, 回调函数(声音加载好触发),配置{循环,自动播放}
const music = new BABYLON.Sound("cello", "sounds/cellolong.wav", scene, null, { loop: true, autoplay: true });
// 播放声音
sound.play();
faceUV = [];
faceUV[0] = new BABYLON.Vector4(0.5, 0.0, 0.75, 1.0); //rear face (左下x,左下y,右上x,右上y)
faceUV[1] = new BABYLON.Vector4(0.0, 0.0, 0.25, 1.0); //front face
faceUV[2] = new BABYLON.Vector4(0.25, 0, 0.5, 1.0); //right side
faceUV[3] = new BABYLON.Vector4(0.75, 0, 1.0, 1.0); //left side
const box = BABYLON.MeshBuilder.CreateBox("box", {faceUV: faceUV, wrap: true}); // wrap: true防止图片旋转,因为我们需要图片位置对应的贴合到mesh上。
const house = BABYLON.Mesh.MergeMeshes([box, roof]);
// 参数:
//[多个网格],
//是否设置原始网格,
//当顶点之和 > 64k时,必须设置为true,
//顶点插入到这个网格。网格可以被合并到一个网格子类中,
//用meshes源将网格细分到他的subMesh数组,
//网格是否接受多个材质
const house = BABYLON.Mesh.MergeMeshes([box, roof], true, false, null, false, true);
1.克隆网格(类似于浅拷贝)
clonedHouse = house.clone("clonedHouse"); // 提供网格的独立副本,但实例仍与其材质链接到原始实例,不能更改网格实例的材质。
2.创建网格实例(类似于深拷贝)
instanceHouse = house.createInstance("instanceHouse");// 从网格模型中创建一个新的InstancedMesh对象。
车身:
x,z平面上设点,逆时针排列连接,y轴方向拉伸
//base
const outline = [
new BABYLON.Vector3(-0.3, 0, -0.1),
new BABYLON.Vector3(0.2, 0, -0.1),
]
//curved front 如何计算出的 1/4圆?
for (let i = 0; i < 20; i++) {
outline.push(new BABYLON.Vector3(0.2 * Math.cos(i * Math.PI / 40), 0, 0.2 * Math.sin(i * Math.PI / 40) - 0.1));
}
//top
outline.push(new BABYLON.Vector3(0, 0, 0.1));
outline.push(new BABYLON.Vector3(-0.3, 0, 0.1));
const car = BABYLON.MeshBuilder.ExtrudePolygon("car", {shape: outline, depth: 0.2});
车轮:
const wheelRB = BABYLON.MeshBuilder.CreateCylinder("wheelRB", {diameter: 0.125, height: 0.05})
wheelRB.parent = car;
wheelRB.position.z = -0.1;
wheelRB.position.x = -0.2;
wheelRB.position.y = 0.035;
const wheelRF = wheelRB.clone("wheelRF");
wheelRF.position.x = 0.1;
const wheelLB = wheelRB.clone("wheelLB");
wheelLB.position.y = -0.2 - 0.035;
const wheelLF = wheelRF.clone("wheelLF");
wheelLF.position.y = -0.2 - 0.035;
上材料:
对于挤出的多边形和圆柱面,面0是底部,面2是顶部,面1是连接底部和顶部的边
const skybox = BABYLON.MeshBuilder.CreateBox("skyBox", {size:150}, scene);
const skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
skyboxMaterial.backFaceCulling = false;// 正反都有图案
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("url path/skybox", scene);
skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
skybox.material = skyboxMaterial;
// 参数:名字,属性,每秒动画帧,动画类型属性,循环模式
const animWheel = new BABYLON.Animation("wheelAnimation", "rotation.y", 30, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE);//动画类型属性有什么作用?还有哪些值?
const wheelKeys = [];
//在动画关键点0处,rotation.y的值为0
wheelKeys.push({
frame: 0,
value: 0
});
//在动画键30处(自动画fps = 30开始1秒后),rotation.y的值为2PI,表示完整旋转
wheelKeys.push({
frame: 30,
value: 2 * Math.PI
});
//设置按键
animWheel.setKeys(wheelKeys);
//将此动画链接到右后轮
wheelRB.animations = [];
wheelRB.animations.push(animWheel);
//开始动画-要进行动画处理的对象,第一帧,最后一帧,如果为true,则循环播放
scene.beginAnimation(wheelRB, 0, 30, true);
人物加载进来然后开始动画:
BABYLON.SceneLoader.ImportMeshAsync("him", "/scenes/Dude/", "Dude.babylon", scene).then((result) => {
var dude = result.meshes[0];
dude.scaling = new BABYLON.Vector3(0.25, 0.25, 0.25);
scene.beginAnimation(result.skeletons[0], 0, 100, true, 1.0);//最后一个参数为运行速度,默认为1
});
球体三角运动:
const slide = function (turn, dist) { //覆盖dist后应用转弯,(精致小函数)
this.turn = turn;
this.dist = dist;
}
const track = [];
track.push(new slide(Math.PI / 2, 4));
track.push(new slide(3 * Math.PI / 4, 8));
track.push(new slide(3 * Math.PI / 4, 8 + 4 * Math.sqrt(2)));
let distance = 0;
let step = 0.05;
let p = 0;
// 在渲染下一帧之前执行
scene.onBeforeRenderObservable.add(() => {
sphere.movePOV(0, 0, step);
distance += step;
if (distance > track[p].dist) {
sphere.rotate(BABYLON.Axis.Y, track[p].turn, BABYLON.Space.LOCAL);
p +=1;
p %= track.length;
if (p === 0) {
distance = 0;
sphere.position = new BABYLON.Vector3(2, 0, 2); //重置为初始条件
sphere.rotation = BABYLON.Vector3.Zero();//防止错误累积
}
}
});
划分危险区
车经过时,如果人没经过,则人停止,车过去之后人继续走;
如果车经过时人也在危险区里,则人不停止
查看两个网格是否发生碰撞:
mesh1.intersectMesh(mesh2);
网格材料为线框:
wireMat.wireframe = true;
hitBox.material = wireMat;
网格材料为透明:
wireMat.alpha = 0;// 材料
hitBox.material = wireMat;// 网格
比如森林
// 参数:名字,图像的url,子画面的最大数量,{宽高},场景
const spriteManagerTrees = new BABYLON.SpriteManager("treesManager", "url to tree image", 2000, {width: 512, height: 1024}, scene);
for (let i = 0; i < 500; i++) {
const tree = new BABYLON.Sprite("tree", spriteManagerTrees);
tree.position.x = Math.random() * (-30);
tree.position.z = Math.random() * 20 + 8;
tree.position.y = 0.5;
}
for (let i = 0; i < 500; i++) {
const tree = new BABYLON.Sprite("tree", spriteManagerTrees);
tree.position.x = Math.random() * (25) + 7;
tree.position.z = Math.random() * -35 + 8;
tree.position.y = 0.5;
}
粒子是在簇或云中发射的小精灵,用于模拟火,烟,水或尘埃。
粒子从特定区域发出。每个粒子都有一个生命周期,当达到生命周期时,它会被重新使用和重新发射。
喷泉:
const createScene = function () {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera("Camera", 3 * Math.PI / 2, Math.PI / 2, 70, BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight("hemi", new BABYLON.Vector3(0, 1, 0));
// lathe(车床)轮廓,x,y轴正半轴象限中以y轴正半轴为中心绘制轮廓,后续沿着y轴旋转所形成的的mesh
const fountainProfile = [//喷泉的配置文件
new BABYLON.Vector3(0, 0, 0),
new BABYLON.Vector3(10, 0, 0),
new BABYLON.Vector3(10, 4, 0),
new BABYLON.Vector3(8, 4, 0),
new BABYLON.Vector3(8, 1, 0),
new BABYLON.Vector3(1, 2, 0),
new BABYLON.Vector3(1, 15, 0),
new BABYLON.Vector3(3, 17, 0)
];
//Create lathe
const fountain = BABYLON.MeshBuilder.CreateLathe("fountain", {shape: fountainProfile, sideOrientation: BABYLON.Mesh.DOUBLESIDE}, scene);
fountain.position.y = -5;
// 创建一个粒子系统,参数:名称, 最大粒子数量, 场景
var particleSystem = new BABYLON.ParticleSystem("particles", 5000, scene);
//Texture of each particle
particleSystem.particleTexture = new BABYLON.Texture("textures/flare.png", scene);
// Where the particles come from
particleSystem.emitter = new BABYLON.Vector3(0, 10, 0); // 发射器的位置,下面两个都是以它为零点的局部坐标
particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0); // Starting all from
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0); // To...
// Colors of all particles
particleSystem.color1 = new BABYLON.Color4(0.7, 0.8, 1.0, 1.0);
particleSystem.color2 = new BABYLON.Color4(0.2, 0.5, 1.0, 1.0);
particleSystem.colorDead = new BABYLON.Color4(0, 0, 0.2, 0.0);// 粒子谢幕时候的颜色
// Size of each particle (random between...
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;
// Life time of each particle (random between...
particleSystem.minLifeTime = 2;
particleSystem.maxLifeTime = 3.5;
// 每帧发射的最大粒子数量
particleSystem.emitRate = 1500;
// 混合模式 : BLENDMODE_ONEONE, or BLENDMODE_STANDARD
particleSystem.blendMode = BABYLON.ParticleSystem.BLENDMODE_ONEONE;
// 为每个粒子设置重力
particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
// Direction of each particle after it has been emitted
particleSystem.direction1 = new BABYLON.Vector3(-2, 8, 2);
particleSystem.direction2 = new BABYLON.Vector3(2, 8, -2);
// 发射粒子的最小/大角速度(每个粒子围绕z轴旋转)。此处就相当于180°发射
particleSystem.minAngularSpeed = 0;
particleSystem.maxAngularSpeed = Math.PI;
// 发射粒子的最小/大功率
particleSystem.minEmitPower = 1;// 在这里是粒子发射的最小高度
particleSystem.maxEmitPower = 3;
particleSystem.updateSpeed = 0.025;// 粒子发射后的运行速度
// Start the particle system
particleSystem.start();
return scene;
}
鼠标点击事件
// 开关喷泉开关
let switched = false;
const pointerDown = (mesh) => {
if (mesh === fountain) {// 如果mesh为喷泉对象
switched = !switched;
switched ? particleSystem.start() : particleSystem.stop();
}
}
// 添加鼠标监听事件,pointerInfo为事件对象
scene.onPointerObservable.add((pointerInfo) => {
switch (pointerInfo.type) {
case BABYLON.PointerEventTypes.POINTERDOWN:
if(pointerInfo.pickInfo.hit) {// 如果鼠标指针与物体相撞(鼠标点击到了物体)
pointerDown(pointerInfo.pickInfo.pickedMesh)
}
break;
}
});
var createScene = function (engine) {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera("Camera", -3 * Math.PI / 4, Math.PI / 3, 50, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.DirectionalLight("dir01", new BABYLON.Vector3(0, -1, 1), scene);
light.position = new BABYLON.Vector3(0, 15, -30);
var ground = BABYLON.Mesh.CreateGround("ground", 100, 100, 1, scene, false);
ground.receiveShadows = true;
// Shadow generator
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
BABYLON.SceneLoader.ImportMesh("him", "/scenes/Dude/", "Dude.babylon", scene, function (newMeshes2, particleSystems2, skeletons2) {
var dude = newMeshes2[0];
dude.scaling = new BABYLON.Vector3(0.2, 0.2, 0.2);
// 参数:添加阴影的网格,是否为其后代添加阴影(默认true)
shadowGenerator.addShadowCaster(dude, true);
scene.beginAnimation(skeletons2[0], 0, 100, true);
});
return scene;
};
我们删除当前的摄像头,然后将其替换为
scene.createDefaultVRExperience();
然后点击护目镜
首先要做的是定义我们的重力矢量,定义G力。
Babylon.js场景具有重力属性,可以将其应用于您先前在代码中定义的任何摄像机。这将使照相机沿指定的方向和速度(Vector3对象)移动,除非照相机的椭球体(请参阅下面的#2)在该方向上与另一个网格(例如,地面网格)碰撞,且checkCollisions设置为true
。
定义并应用重力:
scene.gravity = new BABYLON.Vector3(0, -0.15, 0);
// 将场景的重力应用于相机
camera.applyGravity = true;
虽然Babylon.js单位没有直接的物理等效项,但是在默认的相机视场中,大约1单位= 1米是一个相当标准的假设。因此,如果您想近似地球的名义重力,则需要对每秒渲染的帧数做出一些假设,并计算一个合适的向量:
const assumedFramesPerSecond = 60;
const earthGravity = -9.81;
scene.gravity = new BABYLON.Vector3(0, earthGravity / assumedFramesPerSecond, 0);
由于这是每帧计算一次,因此相机实际上并不是在“移动”,而是沿着重力矢量的方向进行微小的“跳跃”。如果您要依靠碰撞检测来确定相机(或为此目的而连接到其上的网格)是否已经“进入”或“退出”了其他网格(例如,“地面”层来感应掉落的角色并重置游戏)。根据您选择的重力,起始高度,而“触发”的位置和高度网状,相机可以跳的权利,通过触发网而没有“交叉”吧。scene.gravity