重要前提
安装AI Skills的关键前提是:必须科学上网,且开启TUN模式,这一点至关重要,直接决定安装能否顺利完成,在此郑重提醒三遍:科学上网,科学上网,科学上网。查看完整安装教程 →
babylonjs-engine by freshtechbro/claudedesignskills
npx skills add https://github.com/freshtechbro/claudedesignskills --skill babylonjs-engine基础设置
// Get canvas element
const canvas = document.getElementById('renderCanvas');
// Create engine
const engine = new BABYLON.Engine(canvas, true, {
preserveDrawingBuffer: true,
stencil: true
});
// Create scene
const scene = new BABYLON.Scene(engine);
// Render loop
engine.runRenderLoop(() => {
scene.render();
});
// Handle resize
window.addEventListener('resize', () => {
engine.resize();
});
ES6/TypeScript 设置
import { Engine } from '@babylonjs/core/Engines/engine';
import { Scene } from '@babylonjs/core/scene';
import { FreeCamera } from '@babylonjs/core/Cameras/freeCamera';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { HemisphericLight } from '@babylonjs/core/Lights/hemisphericLight';
import { CreateSphere } from '@babylonjs/core/Meshes/Builders/sphereBuilder';
const canvas = document.getElementById('renderCanvas') as HTMLCanvasElement;
const engine = new Engine(canvas);
const scene = new Scene(engine);
// Camera setup
const camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene);
camera.setTarget(Vector3.Zero());
camera.attachControl(canvas, true);
// Lighting
const light = new HemisphericLight('light1', new Vector3(0, 1, 0), scene);
light.intensity = 0.7;
// Create mesh
const sphere = CreateSphere('sphere1', { segments: 16, diameter: 2 }, scene);
sphere.position.y = 2;
// Render
engine.runRenderLoop(() => {
scene.render();
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
场景配置选项
const scene = new BABYLON.Scene(engine, {
// Optimize for large mesh counts
useGeometryUniqueIdsMap: true,
useMaterialMeshMap: true,
useClonedMeshMap: true
});
自由相机(FPS 风格)
const camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
// Movement settings
camera.speed = 0.5;
camera.angularSensibility = 2000;
camera.keysUp = [87]; // W
camera.keysDown = [83]; // S
camera.keysLeft = [65]; // A
camera.keysRight = [68]; // D
弧形旋转相机(轨道)
const camera = new BABYLON.ArcRotateCamera(
'camera',
-Math.PI / 2, // alpha (horizontal rotation)
Math.PI / 2.5, // beta (vertical rotation)
15, // radius (distance)
new BABYLON.Vector3(0, 0, 0), // target
scene
);
camera.attachControl(canvas, true);
// Constraints
camera.lowerRadiusLimit = 5;
camera.upperRadiusLimit = 50;
camera.lowerBetaLimit = 0.1;
camera.upperBetaLimit = Math.PI / 2;
通用相机(高级)
const camera = new BABYLON.UniversalCamera('camera', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
// Collision detection
camera.checkCollisions = true;
camera.applyGravity = true;
camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
半球光(环境光)
const light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
light.diffuse = new BABYLON.Color3(1, 1, 1);
light.specular = new BABYLON.Color3(1, 1, 1);
light.groundColor = new BABYLON.Color3(0, 0, 0);
定向光(类似太阳光)
const light = new BABYLON.DirectionalLight('dirLight', new BABYLON.Vector3(-1, -2, -1), scene);
light.position = new BABYLON.Vector3(20, 40, 20);
light.intensity = 0.5;
// Shadow setup
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.useExponentialShadowMap = true;
点光源(全向)
const light = new BABYLON.PointLight('pointLight', new BABYLON.Vector3(0, 10, 0), scene);
light.intensity = 0.7;
light.diffuse = new BABYLON.Color3(1, 0, 0);
light.specular = new BABYLON.Color3(0, 1, 0);
// Range and falloff
light.range = 100;
light.radius = 0.1;
聚光灯(聚焦)
const light = new BABYLON.SpotLight(
'spotLight',
new BABYLON.Vector3(0, 10, 0), // position
new BABYLON.Vector3(0, -1, 0), // direction
Math.PI / 3, // angle
2, // exponent
scene
);
light.intensity = 0.8;
光照优化(仅包含特定网格)
// Only affect specific meshes
light.includedOnlyMeshes = [mesh1, mesh2, mesh3];
// Or exclude specific meshes
light.excludedMeshes = [mesh4, mesh5];
内置形状
// Box
const box = BABYLON.MeshBuilder.CreateBox('box', {
size: 2,
width: 2,
height: 2,
depth: 2
}, scene);
// Sphere
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {
diameter: 2,
segments: 32,
diameterX: 2,
diameterY: 2,
diameterZ: 2,
arc: 1,
slice: 1
}, scene);
// Cylinder
const cylinder = BABYLON.MeshBuilder.CreateCylinder('cylinder', {
height: 3,
diameter: 2,
tessellation: 24
}, scene);
// Plane
const plane = BABYLON.MeshBuilder.CreatePlane('plane', {
size: 5,
width: 5,
height: 5
}, scene);
// Ground
const ground = BABYLON.MeshBuilder.CreateGround('ground', {
width: 10,
height: 10,
subdivisions: 2
}, scene);
// Ground from heightmap
const ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap('ground', 'heightmap.png', {
width: 100,
height: 100,
subdivisions: 100,
minHeight: 0,
maxHeight: 10
}, scene);
// Torus
const torus = BABYLON.MeshBuilder.CreateTorus('torus', {
diameter: 3,
thickness: 1,
tessellation: 16
}, scene);
// TorusKnot
const torusKnot = BABYLON.MeshBuilder.CreateTorusKnot('torusKnot', {
radius: 2,
tube: 0.6,
radialSegments: 64,
tubularSegments: 8,
p: 2,
q: 3
}, scene);
网格变换
// Position
mesh.position = new BABYLON.Vector3(0, 5, 10);
mesh.position.x = 5;
mesh.position.y = 2;
// Rotation (radians)
mesh.rotation = new BABYLON.Vector3(0, Math.PI / 2, 0);
mesh.rotation.y = Math.PI / 4;
// Scaling
mesh.scaling = new BABYLON.Vector3(2, 2, 2);
mesh.scaling.x = 1.5;
// Look at
mesh.lookAt(new BABYLON.Vector3(0, 0, 0));
// Parent-child relationships
childMesh.parent = parentMesh;
网格属性
// Visibility
mesh.isVisible = true;
mesh.visibility = 0.5; // 0 = invisible, 1 = fully visible
// Picking
mesh.isPickable = true;
mesh.checkCollisions = true;
// Culling
mesh.cullingStrategy = BABYLON.AbstractMesh.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY;
// Receive shadows
mesh.receiveShadows = true;
标准材质
const material = new BABYLON.StandardMaterial('material', scene);
// Colors
material.diffuseColor = new BABYLON.Color3(1, 0, 1);
material.specularColor = new BABYLON.Color3(0.5, 0.6, 0.87);
material.emissiveColor = new BABYLON.Color3(0, 0, 0);
material.ambientColor = new BABYLON.Color3(0.23, 0.98, 0.53);
// Textures
material.diffuseTexture = new BABYLON.Texture('diffuse.png', scene);
material.specularTexture = new BABYLON.Texture('specular.png', scene);
material.emissiveTexture = new BABYLON.Texture('emissive.png', scene);
material.ambientTexture = new BABYLON.Texture('ambient.png', scene);
material.bumpTexture = new BABYLON.Texture('normal.png', scene);
material.opacityTexture = new BABYLON.Texture('opacity.png', scene);
// Properties
material.alpha = 0.8;
material.backFaceCulling = true;
material.wireframe = false;
material.specularPower = 64;
// Apply to mesh
mesh.material = material;
PBR 材质(基于物理的渲染)
const pbr = new BABYLON.PBRMaterial('pbr', scene);
// Metallic workflow
pbr.albedoColor = new BABYLON.Color3(1, 1, 1);
pbr.albedoTexture = new BABYLON.Texture('albedo.png', scene);
pbr.metallic = 1.0;
pbr.roughness = 0.5;
pbr.metallicTexture = new BABYLON.Texture('metallic.png', scene);
// Or specular workflow
pbr.albedoTexture = new BABYLON.Texture('albedo.png', scene);
pbr.reflectivityTexture = new BABYLON.Texture('reflectivity.png', scene);
// Environment
pbr.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData('environment.dds', scene);
// Other maps
pbr.bumpTexture = new BABYLON.Texture('normal.png', scene);
pbr.ambientTexture = new BABYLON.Texture('ao.png', scene);
pbr.emissiveTexture = new BABYLON.Texture('emissive.png', scene);
mesh.material = pbr;
多重材质
const multiMat = new BABYLON.MultiMaterial('multiMat', scene);
multiMat.subMaterials.push(material1);
multiMat.subMaterials.push(material2);
multiMat.subMaterials.push(material3);
mesh.material = multiMat;
mesh.subMeshes = [];
mesh.subMeshes.push(new BABYLON.SubMesh(0, 0, verticesCount, 0, indicesCount1, mesh));
mesh.subMeshes.push(new BABYLON.SubMesh(1, 0, verticesCount, indicesCount1, indicesCount2, mesh));
GLTF/GLB 导入
// Append to scene
BABYLON.SceneLoader.Append('path/to/', 'model.gltf', scene, function(scene) {
console.log('Model loaded');
});
// Import mesh
BABYLON.SceneLoader.ImportMesh('', 'path/to/', 'model.gltf', scene, function(meshes) {
const mesh = meshes[0];
mesh.position.y = 5;
});
// Async version
const result = await BABYLON.SceneLoader.ImportMeshAsync(
null, // all meshes
'https://assets.babylonjs.com/meshes/',
'village.glb',
scene
);
console.log('Loaded meshes:', result.meshes);
// Load from binary
const result = await BABYLON.SceneLoader.AppendAsync(
'',
'data:' + arrayBuffer,
scene
);
资源管理器(批量加载)
const assetsManager = new BABYLON.AssetsManager(scene);
// Add mesh task
const meshTask = assetsManager.addMeshTask('model', '', 'path/to/', 'model.gltf');
meshTask.onSuccess = function(task) {
task.loadedMeshes[0].position = new BABYLON.Vector3(0, 0, 0);
};
// Add texture task
const textureTask = assetsManager.addTextureTask('texture', 'texture.png');
textureTask.onSuccess = function(task) {
material.diffuseTexture = task.texture;
};
// Load all
assetsManager.onFinish = function(tasks) {
console.log('All assets loaded');
engine.runRenderLoop(() => scene.render());
};
assetsManager.load();
Havok 物理设置
// Import Havok
import HavokPhysics from '@babylonjs/havok';
// Initialize
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
// Enable physics
scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
// Create physics aggregate for mesh
const sphereAggregate = new BABYLON.PhysicsAggregate(
sphere,
BABYLON.PhysicsShapeType.SPHERE,
{ mass: 1, restitution: 0.75 },
scene
);
// Ground (static)
const groundAggregate = new BABYLON.PhysicsAggregate(
ground,
BABYLON.PhysicsShapeType.BOX,
{ mass: 0 }, // mass 0 = static
scene
);
物理形状
// Available shapes
BABYLON.PhysicsShapeType.SPHERE
BABYLON.PhysicsShapeType.BOX
BABYLON.PhysicsShapeType.CAPSULE
BABYLON.PhysicsShapeType.CYLINDER
BABYLON.PhysicsShapeType.CONVEX_HULL
BABYLON.PhysicsShapeType.MESH
BABYLON.PhysicsShapeType.HEIGHTFIELD
物理体控制
// Get body
const body = aggregate.body;
// Apply force
body.applyForce(
new BABYLON.Vector3(0, 10, 0), // force
new BABYLON.Vector3(0, 0, 0) // point of application
);
// Apply impulse
body.applyImpulse(
new BABYLON.Vector3(0, 5, 0),
new BABYLON.Vector3(0, 0, 0)
);
// Set velocity
body.setLinearVelocity(new BABYLON.Vector3(0, 5, 0));
body.setAngularVelocity(new BABYLON.Vector3(0, 1, 0));
// Properties
body.setMassProperties({ mass: 2 });
body.setCollisionCallbackEnabled(true);
直接动画
// Animate property
BABYLON.Animation.CreateAndStartAnimation(
'anim',
mesh,
'position.y',
30, // FPS
120, // total frames
mesh.position.y, // from
10, // to
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
动画类
const animation = new BABYLON.Animation(
'myAnimation',
'position.x',
30,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
// Keyframes
const keys = [
{ frame: 0, value: 0 },
{ frame: 30, value: 10 },
{ frame: 60, value: 0 }
];
animation.setKeys(keys);
// Attach to mesh
mesh.animations.push(animation);
// Start
scene.beginAnimation(mesh, 0, 60, true);
动画组
const animationGroup = new BABYLON.AnimationGroup('group', scene);
animationGroup.addTargetedAnimation(animation1, mesh1);
animationGroup.addTargetedAnimation(animation2, mesh2);
// Control
animationGroup.play();
animationGroup.pause();
animationGroup.stop();
animationGroup.speedRatio = 2.0;
// Events
animationGroup.onAnimationEndObservable.add(() => {
console.log('Animation complete');
});
骨骼动画(来自导入的模型)
// Get skeleton from imported model
const skeleton = result.skeletons[0];
// Get animation ranges
const ranges = skeleton.getAnimationRanges();
// Play animation range
scene.beginAnimation(skeleton, 0, 100, true);
// Or use animation groups
result.animationGroups[0].play();
result.animationGroups[0].setWeightForAllAnimatables(0.5);
const createScene = function() {
const scene = new BABYLON.Scene(engine);
// Quick setup
scene.createDefaultCameraOrLight(true, true, true);
const env = scene.createDefaultEnvironment({
createGround: true,
createSkybox: true,
skyboxSize: 150,
groundSize: 50
});
// Your meshes
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
sphere.position.y = 1;
return scene;
};
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
// Load model
const result = await BABYLON.SceneLoader.ImportMeshAsync(
null,
'https://assets.babylonjs.com/meshes/',
'village.glb',
scene
);
// Setup physics
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
return scene;
};
createScene().then(scene => {
engine.runRenderLoop(() => scene.render());
});
scene.onPointerDown = function(evt, pickResult) {
if (pickResult.hit) {
console.log('Picked mesh:', pickResult.pickedMesh.name);
console.log('Pick point:', pickResult.pickedPoint);
// Highlight picked mesh
pickResult.pickedMesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
}
};
// Or use action manager
mesh.actionManager = new BABYLON.ActionManager(scene);
mesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(
BABYLON.ActionManager.OnPickTrigger,
function() {
console.log('Mesh clicked');
}
)
);
// Default pipeline
const pipeline = new BABYLON.DefaultRenderingPipeline('pipeline', true, scene, [camera]);
pipeline.samples = 4;
pipeline.fxaaEnabled = true;
pipeline.bloomEnabled = true;
pipeline.bloomThreshold = 0.8;
pipeline.bloomWeight = 0.5;
pipeline.bloomKernel = 64;
// Depth of field
pipeline.depthOfFieldEnabled = true;
pipeline.depthOfFieldBlurLevel = BABYLON.DepthOfFieldEffectBlurLevel.Low;
pipeline.depthOfField.focusDistance = 2000;
pipeline.depthOfField.focalLength = 50;
// Glow layer
const glowLayer = new BABYLON.GlowLayer('glow', scene);
glowLayer.intensity = 0.5;
// Highlight layer
const highlightLayer = new BABYLON.HighlightLayer('highlight', scene);
highlightLayer.addMesh(mesh, BABYLON.Color3.Green());
import { AdvancedDynamicTexture, Button, TextBlock, Rectangle } from '@babylonjs/gui';
// Fullscreen UI
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
// Button
const button = BABYLON.GUI.Button.CreateSimpleButton('button', 'Click Me');
button.width = '150px';
button.height = '40px';
button.color = 'white';
button.background = 'green';
button.onPointerUpObservable.add(() => {
console.log('Button clicked');
});
advancedTexture.addControl(button);
// Text
const text = new BABYLON.GUI.TextBlock();
text.text = 'Hello World';
text.color = 'white';
text.fontSize = 24;
advancedTexture.addControl(text);
// 3D mesh UI
const plane = BABYLON.MeshBuilder.CreatePlane('plane', {size: 2}, scene);
const advancedTexture3D = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(plane);
const button3D = BABYLON.GUI.Button.CreateSimpleButton('button3D', 'Click Me');
advancedTexture3D.addControl(button3D);
const light = new BABYLON.DirectionalLight('light', new BABYLON.Vector3(-1, -2, -1), scene);
light.position = new BABYLON.Vector3(20, 40, 20);
// Create shadow generator
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.useExponentialShadowMap = true;
shadowGenerator.usePoissonSampling = true;
// Add shadow casters
shadowGenerator.addShadowCaster(sphere);
shadowGenerator.addShadowCaster(box);
// Enable shadow receiving
ground.receiveShadows = true;
const particleSystem = new BABYLON.ParticleSystem('particles', 2000, scene);
particleSystem.particleTexture = new BABYLON.Texture('particle.png', scene);
// Emitter
particleSystem.emitter = new BABYLON.Vector3(0, 5, 0);
particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0);
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0);
// Colors
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
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;
// Life time
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5;
// Emission rate
particleSystem.emitRate = 1500;
// Direction
particleSystem.direction1 = new BABYLON.Vector3(-1, 8, 1);
particleSystem.direction2 = new BABYLON.Vector3(1, 8, -1);
// Gravity
particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
// Start
particleSystem.start();
import { useEffect, useRef } from 'react';
import * as BABYLON from '@babylonjs/core';
function BabylonScene() {
const canvasRef = useRef(null);
const engineRef = useRef(null);
const sceneRef = useRef(null);
useEffect(() => {
if (!canvasRef.current) return;
// Initialize
const engine = new BABYLON.Engine(canvasRef.current, true);
engineRef.current = engine;
const scene = new BABYLON.Scene(engine);
sceneRef.current = scene;
// Setup scene
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvasRef.current, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
// Render loop
engine.runRenderLoop(() => {
scene.render();
});
// Resize handler
const handleResize = () => engine.resize();
window.addEventListener('resize', handleResize);
// Cleanup
return () => {
window.removeEventListener('resize', handleResize);
scene.dispose();
engine.dispose();
};
}, []);
return (
<canvas
ref={canvasRef}
style={{ width: '100%', height: '100vh' }}
/>
);
}
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.FreeCamera('camera', new BABYLON.Vector3(0, 5, -10), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
sphere.position.y = 1;
const env = scene.createDefaultEnvironment();
// Enable WebXR
const xrHelper = await scene.createDefaultXRExperienceAsync({
floorMeshes: [env.ground],
disableTeleportation: false
});
// XR controller input
xrHelper.input.onControllerAddedObservable.add((controller) => {
controller.onMotionControllerInitObservable.add((motionController) => {
const trigger = motionController.getMainComponent();
trigger.onButtonStateChangedObservable.add(() => {
if (trigger.pressed) {
console.log('Trigger pressed');
}
});
});
});
return scene;
};
// Create from snippet
const nodeMaterial = await BABYLON.NodeMaterial.ParseFromSnippetAsync('#SNIPPET_ID', scene);
// Apply to mesh
nodeMaterial.build();
mesh.material = nodeMaterial;
// Or create programmatically
const nodeMaterial = new BABYLON.NodeMaterial('node', scene);
const positionInput = new BABYLON.InputBlock('position');
positionInput.setAsAttribute('position');
const worldPos = new BABYLON.TransformBlock('worldPos');
nodeMaterial.addOutputNode(worldPos);
// Merge meshes with same material
const merged = BABYLON.Mesh.MergeMeshes(
[mesh1, mesh2, mesh3],
true, // disposeSource
true, // allow32BitsIndices
undefined,
false, // multiMultiMaterials
true // preserveSerializationHelper
);
// Instances (for repeated meshes)
const instance1 = mesh.createInstance('instance1');
const instance2 = mesh.createInstance('instance2');
instance1.position.x = 5;
instance2.position.x = -5;
// Thin instances (even more efficient)
const buffer = new Float32Array(16 * count); // 16 floats per matrix
mesh.thinInstanceSetBuffer('matrix', buffer, 16);
// Freeze meshes (static meshes)
mesh.freezeWorldMatrix();
// Freeze materials
material.freeze();
// Simplify meshes (LOD)
const simplified = mesh.simplify(
[
{ quality: 0.8, distance: 10 },
{ quality: 0.4, distance: 50 },
{ quality: 0.2, distance: 100 }
],
true, // parallelProcessing
BABYLON.SimplificationType.QUADRATIC
);
// Scene optimizer
const options = new BABYLON.SceneOptimizerOptions();
options.addOptimization(new BABYLON.HardwareScalingOptimization(0, 1));
options.addOptimization(new BABYLON.ShadowsOptimization(1));
options.addOptimization(new BABYLON.PostProcessesOptimization(2));
options.addOptimization(new BABYLON.LensFlaresOptimization(3));
options.addOptimization(new BABYLON.ParticlesOptimization(4));
options.addOptimization(new BABYLON.TextureOptimization(5, 512));
options.addOptimization(new BABYLON.RenderTargetsOptimization(6));
options.addOptimization(new BABYLON.MergeMeshesOptimization(7));
const optimizer = new BABYLON.SceneOptimizer(scene, options);
optimizer.start();
// Octree (spatial partitioning)
const octree = scene.createOrUpdateSelectionOctree();
// Frustum culling
scene.blockMaterialDirtyMechanism = true;
// Skip pointer move picking
scene.skipPointerMovePicking = true;
// Freeze active meshes
scene.freezeActiveMeshes();
// Hardware scaling
engine.setHardwareScalingLevel(0.5); // Render at half resolution
// Adaptive quality
scene.onBeforeRenderObservable.add(() => {
const fps = engine.getFps();
if (fps < 30) {
// Reduce quality
engine.setHardwareScalingLevel(2);
} else if (fps > 55) {
// Increase quality
engine.setHardwareScalingLevel(1);
}
});
// Incremental loading
scene.useDelayedTextureLoading = true;
// Culling strategy
mesh.cullingStrategy = BABYLON.AbstractMesh.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY;
// Compressed textures
const texture = new BABYLON.Texture('texture.dds', scene);
// Mipmaps
texture.updateSamplingMode(BABYLON.Texture.TRILINEAR_SAMPLINGMODE);
// Anisotropic filtering
texture.anisotropicFilteringLevel = 4;
// KTX2 compression
const texture = new BABYLON.Texture('texture.ktx2', scene);
问题:未释放资源
// ❌ Bad - memory leak
function createAndRemoveMesh() {
const mesh = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
scene.removeMesh(mesh);
}
解决方案:正确释放
// ✅ Good
function createAndRemoveMesh() {
const mesh = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
mesh.dispose();
}
// Dispose entire scene
scene.dispose();
// Dispose engine
engine.dispose();
问题:每个网格 = 一个绘制调用
// ❌ Bad - 1000 draw calls
for (let i = 0; i < 1000; i++) {
const box = BABYLON.MeshBuilder.CreateBox('box' + i, {}, scene);
box.position.x = i;
}
解决方案:使用实例或合并
// ✅ Good - 1 draw call
const box = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
for (let i = 0; i < 1000; i++) {
const instance = box.createInstance('instance' + i);
instance.position.x = i;
}
问题:繁重的计算阻塞渲染
// ❌ Bad - blocks rendering
function createManyMeshes() {
for (let i = 0; i < 10000; i++) {
const mesh = BABYLON.MeshBuilder.CreateSphere('sphere' + i, {}, scene);
}
}
解决方案:使用异步/增量加载
// ✅ Good - incremental
async function createManyMeshes() {
for (let i = 0; i < 10000; i++) {
const mesh = BABYLON.MeshBuilder.CreateSphere('sphere' + i, {}, scene);
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
Basic Setup
// Get canvas element
const canvas = document.getElementById('renderCanvas');
// Create engine
const engine = new BABYLON.Engine(canvas, true, {
preserveDrawingBuffer: true,
stencil: true
});
// Create scene
const scene = new BABYLON.Scene(engine);
// Render loop
engine.runRenderLoop(() => {
scene.render();
});
// Handle resize
window.addEventListener('resize', () => {
engine.resize();
});
ES6/TypeScript Setup
import { Engine } from '@babylonjs/core/Engines/engine';
import { Scene } from '@babylonjs/core/scene';
import { FreeCamera } from '@babylonjs/core/Cameras/freeCamera';
import { Vector3 } from '@babylonjs/core/Maths/math.vector';
import { HemisphericLight } from '@babylonjs/core/Lights/hemisphericLight';
import { CreateSphere } from '@babylonjs/core/Meshes/Builders/sphereBuilder';
const canvas = document.getElementById('renderCanvas') as HTMLCanvasElement;
const engine = new Engine(canvas);
const scene = new Scene(engine);
// Camera setup
const camera = new FreeCamera('camera1', new Vector3(0, 5, -10), scene);
camera.setTarget(Vector3.Zero());
camera.attachControl(canvas, true);
// Lighting
const light = new HemisphericLight('light1', new Vector3(0, 1, 0), scene);
light.intensity = 0.7;
// Create mesh
const sphere = CreateSphere('sphere1', { segments: 16, diameter: 2 }, scene);
sphere.position.y = 2;
// Render
engine.runRenderLoop(() => {
scene.render();
});
Scene Configuration Options
const scene = new BABYLON.Scene(engine, {
// Optimize for large mesh counts
useGeometryUniqueIdsMap: true,
useMaterialMeshMap: true,
useClonedMeshMap: true
});
Free Camera (FPS-style)
const camera = new BABYLON.FreeCamera('camera1', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
// Movement settings
camera.speed = 0.5;
camera.angularSensibility = 2000;
camera.keysUp = [87]; // W
camera.keysDown = [83]; // S
camera.keysLeft = [65]; // A
camera.keysRight = [68]; // D
Arc Rotate Camera (Orbit)
const camera = new BABYLON.ArcRotateCamera(
'camera',
-Math.PI / 2, // alpha (horizontal rotation)
Math.PI / 2.5, // beta (vertical rotation)
15, // radius (distance)
new BABYLON.Vector3(0, 0, 0), // target
scene
);
camera.attachControl(canvas, true);
// Constraints
camera.lowerRadiusLimit = 5;
camera.upperRadiusLimit = 50;
camera.lowerBetaLimit = 0.1;
camera.upperBetaLimit = Math.PI / 2;
Universal Camera (Advanced)
const camera = new BABYLON.UniversalCamera('camera', new BABYLON.Vector3(0, 5, -10), scene);
camera.setTarget(BABYLON.Vector3.Zero());
camera.attachControl(canvas, true);
// Collision detection
camera.checkCollisions = true;
camera.applyGravity = true;
camera.ellipsoid = new BABYLON.Vector3(1, 1, 1);
Hemispheric Light (Ambient)
const light = new BABYLON.HemisphericLight('light1', new BABYLON.Vector3(0, 1, 0), scene);
light.intensity = 0.7;
light.diffuse = new BABYLON.Color3(1, 1, 1);
light.specular = new BABYLON.Color3(1, 1, 1);
light.groundColor = new BABYLON.Color3(0, 0, 0);
Directional Light (Sun-like)
const light = new BABYLON.DirectionalLight('dirLight', new BABYLON.Vector3(-1, -2, -1), scene);
light.position = new BABYLON.Vector3(20, 40, 20);
light.intensity = 0.5;
// Shadow setup
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.useExponentialShadowMap = true;
Point Light (Omni-directional)
const light = new BABYLON.PointLight('pointLight', new BABYLON.Vector3(0, 10, 0), scene);
light.intensity = 0.7;
light.diffuse = new BABYLON.Color3(1, 0, 0);
light.specular = new BABYLON.Color3(0, 1, 0);
// Range and falloff
light.range = 100;
light.radius = 0.1;
Spot Light (Focused)
const light = new BABYLON.SpotLight(
'spotLight',
new BABYLON.Vector3(0, 10, 0), // position
new BABYLON.Vector3(0, -1, 0), // direction
Math.PI / 3, // angle
2, // exponent
scene
);
light.intensity = 0.8;
Light Optimization (Include Only Specific Meshes)
// Only affect specific meshes
light.includedOnlyMeshes = [mesh1, mesh2, mesh3];
// Or exclude specific meshes
light.excludedMeshes = [mesh4, mesh5];
Built-in Shapes
// Box
const box = BABYLON.MeshBuilder.CreateBox('box', {
size: 2,
width: 2,
height: 2,
depth: 2
}, scene);
// Sphere
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {
diameter: 2,
segments: 32,
diameterX: 2,
diameterY: 2,
diameterZ: 2,
arc: 1,
slice: 1
}, scene);
// Cylinder
const cylinder = BABYLON.MeshBuilder.CreateCylinder('cylinder', {
height: 3,
diameter: 2,
tessellation: 24
}, scene);
// Plane
const plane = BABYLON.MeshBuilder.CreatePlane('plane', {
size: 5,
width: 5,
height: 5
}, scene);
// Ground
const ground = BABYLON.MeshBuilder.CreateGround('ground', {
width: 10,
height: 10,
subdivisions: 2
}, scene);
// Ground from heightmap
const ground = BABYLON.MeshBuilder.CreateGroundFromHeightMap('ground', 'heightmap.png', {
width: 100,
height: 100,
subdivisions: 100,
minHeight: 0,
maxHeight: 10
}, scene);
// Torus
const torus = BABYLON.MeshBuilder.CreateTorus('torus', {
diameter: 3,
thickness: 1,
tessellation: 16
}, scene);
// TorusKnot
const torusKnot = BABYLON.MeshBuilder.CreateTorusKnot('torusKnot', {
radius: 2,
tube: 0.6,
radialSegments: 64,
tubularSegments: 8,
p: 2,
q: 3
}, scene);
Mesh Transformations
// Position
mesh.position = new BABYLON.Vector3(0, 5, 10);
mesh.position.x = 5;
mesh.position.y = 2;
// Rotation (radians)
mesh.rotation = new BABYLON.Vector3(0, Math.PI / 2, 0);
mesh.rotation.y = Math.PI / 4;
// Scaling
mesh.scaling = new BABYLON.Vector3(2, 2, 2);
mesh.scaling.x = 1.5;
// Look at
mesh.lookAt(new BABYLON.Vector3(0, 0, 0));
// Parent-child relationships
childMesh.parent = parentMesh;
Mesh Properties
// Visibility
mesh.isVisible = true;
mesh.visibility = 0.5; // 0 = invisible, 1 = fully visible
// Picking
mesh.isPickable = true;
mesh.checkCollisions = true;
// Culling
mesh.cullingStrategy = BABYLON.AbstractMesh.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY;
// Receive shadows
mesh.receiveShadows = true;
Standard Material
const material = new BABYLON.StandardMaterial('material', scene);
// Colors
material.diffuseColor = new BABYLON.Color3(1, 0, 1);
material.specularColor = new BABYLON.Color3(0.5, 0.6, 0.87);
material.emissiveColor = new BABYLON.Color3(0, 0, 0);
material.ambientColor = new BABYLON.Color3(0.23, 0.98, 0.53);
// Textures
material.diffuseTexture = new BABYLON.Texture('diffuse.png', scene);
material.specularTexture = new BABYLON.Texture('specular.png', scene);
material.emissiveTexture = new BABYLON.Texture('emissive.png', scene);
material.ambientTexture = new BABYLON.Texture('ambient.png', scene);
material.bumpTexture = new BABYLON.Texture('normal.png', scene);
material.opacityTexture = new BABYLON.Texture('opacity.png', scene);
// Properties
material.alpha = 0.8;
material.backFaceCulling = true;
material.wireframe = false;
material.specularPower = 64;
// Apply to mesh
mesh.material = material;
PBR Material (Physically Based Rendering)
const pbr = new BABYLON.PBRMaterial('pbr', scene);
// Metallic workflow
pbr.albedoColor = new BABYLON.Color3(1, 1, 1);
pbr.albedoTexture = new BABYLON.Texture('albedo.png', scene);
pbr.metallic = 1.0;
pbr.roughness = 0.5;
pbr.metallicTexture = new BABYLON.Texture('metallic.png', scene);
// Or specular workflow
pbr.albedoTexture = new BABYLON.Texture('albedo.png', scene);
pbr.reflectivityTexture = new BABYLON.Texture('reflectivity.png', scene);
// Environment
pbr.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData('environment.dds', scene);
// Other maps
pbr.bumpTexture = new BABYLON.Texture('normal.png', scene);
pbr.ambientTexture = new BABYLON.Texture('ao.png', scene);
pbr.emissiveTexture = new BABYLON.Texture('emissive.png', scene);
mesh.material = pbr;
Multi-Materials
const multiMat = new BABYLON.MultiMaterial('multiMat', scene);
multiMat.subMaterials.push(material1);
multiMat.subMaterials.push(material2);
multiMat.subMaterials.push(material3);
mesh.material = multiMat;
mesh.subMeshes = [];
mesh.subMeshes.push(new BABYLON.SubMesh(0, 0, verticesCount, 0, indicesCount1, mesh));
mesh.subMeshes.push(new BABYLON.SubMesh(1, 0, verticesCount, indicesCount1, indicesCount2, mesh));
GLTF/GLB Import
// Append to scene
BABYLON.SceneLoader.Append('path/to/', 'model.gltf', scene, function(scene) {
console.log('Model loaded');
});
// Import mesh
BABYLON.SceneLoader.ImportMesh('', 'path/to/', 'model.gltf', scene, function(meshes) {
const mesh = meshes[0];
mesh.position.y = 5;
});
// Async version
const result = await BABYLON.SceneLoader.ImportMeshAsync(
null, // all meshes
'https://assets.babylonjs.com/meshes/',
'village.glb',
scene
);
console.log('Loaded meshes:', result.meshes);
// Load from binary
const result = await BABYLON.SceneLoader.AppendAsync(
'',
'data:' + arrayBuffer,
scene
);
Asset Manager (Batch Loading)
const assetsManager = new BABYLON.AssetsManager(scene);
// Add mesh task
const meshTask = assetsManager.addMeshTask('model', '', 'path/to/', 'model.gltf');
meshTask.onSuccess = function(task) {
task.loadedMeshes[0].position = new BABYLON.Vector3(0, 0, 0);
};
// Add texture task
const textureTask = assetsManager.addTextureTask('texture', 'texture.png');
textureTask.onSuccess = function(task) {
material.diffuseTexture = task.texture;
};
// Load all
assetsManager.onFinish = function(tasks) {
console.log('All assets loaded');
engine.runRenderLoop(() => scene.render());
};
assetsManager.load();
Havok Physics Setup
// Import Havok
import HavokPhysics from '@babylonjs/havok';
// Initialize
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
// Enable physics
scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
// Create physics aggregate for mesh
const sphereAggregate = new BABYLON.PhysicsAggregate(
sphere,
BABYLON.PhysicsShapeType.SPHERE,
{ mass: 1, restitution: 0.75 },
scene
);
// Ground (static)
const groundAggregate = new BABYLON.PhysicsAggregate(
ground,
BABYLON.PhysicsShapeType.BOX,
{ mass: 0 }, // mass 0 = static
scene
);
Physics Shapes
// Available shapes
BABYLON.PhysicsShapeType.SPHERE
BABYLON.PhysicsShapeType.BOX
BABYLON.PhysicsShapeType.CAPSULE
BABYLON.PhysicsShapeType.CYLINDER
BABYLON.PhysicsShapeType.CONVEX_HULL
BABYLON.PhysicsShapeType.MESH
BABYLON.PhysicsShapeType.HEIGHTFIELD
Physics Body Control
// Get body
const body = aggregate.body;
// Apply force
body.applyForce(
new BABYLON.Vector3(0, 10, 0), // force
new BABYLON.Vector3(0, 0, 0) // point of application
);
// Apply impulse
body.applyImpulse(
new BABYLON.Vector3(0, 5, 0),
new BABYLON.Vector3(0, 0, 0)
);
// Set velocity
body.setLinearVelocity(new BABYLON.Vector3(0, 5, 0));
body.setAngularVelocity(new BABYLON.Vector3(0, 1, 0));
// Properties
body.setMassProperties({ mass: 2 });
body.setCollisionCallbackEnabled(true);
Direct Animation
// Animate property
BABYLON.Animation.CreateAndStartAnimation(
'anim',
mesh,
'position.y',
30, // FPS
120, // total frames
mesh.position.y, // from
10, // to
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
Animation Class
const animation = new BABYLON.Animation(
'myAnimation',
'position.x',
30,
BABYLON.Animation.ANIMATIONTYPE_FLOAT,
BABYLON.Animation.ANIMATIONLOOPMODE_CYCLE
);
// Keyframes
const keys = [
{ frame: 0, value: 0 },
{ frame: 30, value: 10 },
{ frame: 60, value: 0 }
];
animation.setKeys(keys);
// Attach to mesh
mesh.animations.push(animation);
// Start
scene.beginAnimation(mesh, 0, 60, true);
Animation Groups
const animationGroup = new BABYLON.AnimationGroup('group', scene);
animationGroup.addTargetedAnimation(animation1, mesh1);
animationGroup.addTargetedAnimation(animation2, mesh2);
// Control
animationGroup.play();
animationGroup.pause();
animationGroup.stop();
animationGroup.speedRatio = 2.0;
// Events
animationGroup.onAnimationEndObservable.add(() => {
console.log('Animation complete');
});
Skeleton Animations (from imported models)
// Get skeleton from imported model
const skeleton = result.skeletons[0];
// Get animation ranges
const ranges = skeleton.getAnimationRanges();
// Play animation range
scene.beginAnimation(skeleton, 0, 100, true);
// Or use animation groups
result.animationGroups[0].play();
result.animationGroups[0].setWeightForAllAnimatables(0.5);
const createScene = function() {
const scene = new BABYLON.Scene(engine);
// Quick setup
scene.createDefaultCameraOrLight(true, true, true);
const env = scene.createDefaultEnvironment({
createGround: true,
createSkybox: true,
skyboxSize: 150,
groundSize: 50
});
// Your meshes
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
sphere.position.y = 1;
return scene;
};
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
// Load model
const result = await BABYLON.SceneLoader.ImportMeshAsync(
null,
'https://assets.babylonjs.com/meshes/',
'village.glb',
scene
);
// Setup physics
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
return scene;
};
createScene().then(scene => {
engine.runRenderLoop(() => scene.render());
});
scene.onPointerDown = function(evt, pickResult) {
if (pickResult.hit) {
console.log('Picked mesh:', pickResult.pickedMesh.name);
console.log('Pick point:', pickResult.pickedPoint);
// Highlight picked mesh
pickResult.pickedMesh.material.emissiveColor = new BABYLON.Color3(1, 0, 0);
}
};
// Or use action manager
mesh.actionManager = new BABYLON.ActionManager(scene);
mesh.actionManager.registerAction(
new BABYLON.ExecuteCodeAction(
BABYLON.ActionManager.OnPickTrigger,
function() {
console.log('Mesh clicked');
}
)
);
// Default pipeline
const pipeline = new BABYLON.DefaultRenderingPipeline('pipeline', true, scene, [camera]);
pipeline.samples = 4;
pipeline.fxaaEnabled = true;
pipeline.bloomEnabled = true;
pipeline.bloomThreshold = 0.8;
pipeline.bloomWeight = 0.5;
pipeline.bloomKernel = 64;
// Depth of field
pipeline.depthOfFieldEnabled = true;
pipeline.depthOfFieldBlurLevel = BABYLON.DepthOfFieldEffectBlurLevel.Low;
pipeline.depthOfField.focusDistance = 2000;
pipeline.depthOfField.focalLength = 50;
// Glow layer
const glowLayer = new BABYLON.GlowLayer('glow', scene);
glowLayer.intensity = 0.5;
// Highlight layer
const highlightLayer = new BABYLON.HighlightLayer('highlight', scene);
highlightLayer.addMesh(mesh, BABYLON.Color3.Green());
import { AdvancedDynamicTexture, Button, TextBlock, Rectangle } from '@babylonjs/gui';
// Fullscreen UI
const advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI('UI');
// Button
const button = BABYLON.GUI.Button.CreateSimpleButton('button', 'Click Me');
button.width = '150px';
button.height = '40px';
button.color = 'white';
button.background = 'green';
button.onPointerUpObservable.add(() => {
console.log('Button clicked');
});
advancedTexture.addControl(button);
// Text
const text = new BABYLON.GUI.TextBlock();
text.text = 'Hello World';
text.color = 'white';
text.fontSize = 24;
advancedTexture.addControl(text);
// 3D mesh UI
const plane = BABYLON.MeshBuilder.CreatePlane('plane', {size: 2}, scene);
const advancedTexture3D = BABYLON.GUI.AdvancedDynamicTexture.CreateForMesh(plane);
const button3D = BABYLON.GUI.Button.CreateSimpleButton('button3D', 'Click Me');
advancedTexture3D.addControl(button3D);
const light = new BABYLON.DirectionalLight('light', new BABYLON.Vector3(-1, -2, -1), scene);
light.position = new BABYLON.Vector3(20, 40, 20);
// Create shadow generator
const shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
shadowGenerator.useExponentialShadowMap = true;
shadowGenerator.usePoissonSampling = true;
// Add shadow casters
shadowGenerator.addShadowCaster(sphere);
shadowGenerator.addShadowCaster(box);
// Enable shadow receiving
ground.receiveShadows = true;
const particleSystem = new BABYLON.ParticleSystem('particles', 2000, scene);
particleSystem.particleTexture = new BABYLON.Texture('particle.png', scene);
// Emitter
particleSystem.emitter = new BABYLON.Vector3(0, 5, 0);
particleSystem.minEmitBox = new BABYLON.Vector3(-1, 0, 0);
particleSystem.maxEmitBox = new BABYLON.Vector3(1, 0, 0);
// Colors
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
particleSystem.minSize = 0.1;
particleSystem.maxSize = 0.5;
// Life time
particleSystem.minLifeTime = 0.3;
particleSystem.maxLifeTime = 1.5;
// Emission rate
particleSystem.emitRate = 1500;
// Direction
particleSystem.direction1 = new BABYLON.Vector3(-1, 8, 1);
particleSystem.direction2 = new BABYLON.Vector3(1, 8, -1);
// Gravity
particleSystem.gravity = new BABYLON.Vector3(0, -9.81, 0);
// Start
particleSystem.start();
import { useEffect, useRef } from 'react';
import * as BABYLON from '@babylonjs/core';
function BabylonScene() {
const canvasRef = useRef(null);
const engineRef = useRef(null);
const sceneRef = useRef(null);
useEffect(() => {
if (!canvasRef.current) return;
// Initialize
const engine = new BABYLON.Engine(canvasRef.current, true);
engineRef.current = engine;
const scene = new BABYLON.Scene(engine);
sceneRef.current = scene;
// Setup scene
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvasRef.current, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
// Render loop
engine.runRenderLoop(() => {
scene.render();
});
// Resize handler
const handleResize = () => engine.resize();
window.addEventListener('resize', handleResize);
// Cleanup
return () => {
window.removeEventListener('resize', handleResize);
scene.dispose();
engine.dispose();
};
}, []);
return (
<canvas
ref={canvasRef}
style={{ width: '100%', height: '100vh' }}
/>
);
}
const createScene = async function() {
const scene = new BABYLON.Scene(engine);
const camera = new BABYLON.FreeCamera('camera', new BABYLON.Vector3(0, 5, -10), scene);
camera.attachControl(canvas, true);
const light = new BABYLON.HemisphericLight('light', new BABYLON.Vector3(0, 1, 0), scene);
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {diameter: 2}, scene);
sphere.position.y = 1;
const env = scene.createDefaultEnvironment();
// Enable WebXR
const xrHelper = await scene.createDefaultXRExperienceAsync({
floorMeshes: [env.ground],
disableTeleportation: false
});
// XR controller input
xrHelper.input.onControllerAddedObservable.add((controller) => {
controller.onMotionControllerInitObservable.add((motionController) => {
const trigger = motionController.getMainComponent();
trigger.onButtonStateChangedObservable.add(() => {
if (trigger.pressed) {
console.log('Trigger pressed');
}
});
});
});
return scene;
};
// Create from snippet
const nodeMaterial = await BABYLON.NodeMaterial.ParseFromSnippetAsync('#SNIPPET_ID', scene);
// Apply to mesh
nodeMaterial.build();
mesh.material = nodeMaterial;
// Or create programmatically
const nodeMaterial = new BABYLON.NodeMaterial('node', scene);
const positionInput = new BABYLON.InputBlock('position');
positionInput.setAsAttribute('position');
const worldPos = new BABYLON.TransformBlock('worldPos');
nodeMaterial.addOutputNode(worldPos);
// Merge meshes with same material
const merged = BABYLON.Mesh.MergeMeshes(
[mesh1, mesh2, mesh3],
true, // disposeSource
true, // allow32BitsIndices
undefined,
false, // multiMultiMaterials
true // preserveSerializationHelper
);
// Instances (for repeated meshes)
const instance1 = mesh.createInstance('instance1');
const instance2 = mesh.createInstance('instance2');
instance1.position.x = 5;
instance2.position.x = -5;
// Thin instances (even more efficient)
const buffer = new Float32Array(16 * count); // 16 floats per matrix
mesh.thinInstanceSetBuffer('matrix', buffer, 16);
// Freeze meshes (static meshes)
mesh.freezeWorldMatrix();
// Freeze materials
material.freeze();
// Simplify meshes (LOD)
const simplified = mesh.simplify(
[
{ quality: 0.8, distance: 10 },
{ quality: 0.4, distance: 50 },
{ quality: 0.2, distance: 100 }
],
true, // parallelProcessing
BABYLON.SimplificationType.QUADRATIC
);
// Scene optimizer
const options = new BABYLON.SceneOptimizerOptions();
options.addOptimization(new BABYLON.HardwareScalingOptimization(0, 1));
options.addOptimization(new BABYLON.ShadowsOptimization(1));
options.addOptimization(new BABYLON.PostProcessesOptimization(2));
options.addOptimization(new BABYLON.LensFlaresOptimization(3));
options.addOptimization(new BABYLON.ParticlesOptimization(4));
options.addOptimization(new BABYLON.TextureOptimization(5, 512));
options.addOptimization(new BABYLON.RenderTargetsOptimization(6));
options.addOptimization(new BABYLON.MergeMeshesOptimization(7));
const optimizer = new BABYLON.SceneOptimizer(scene, options);
optimizer.start();
// Octree (spatial partitioning)
const octree = scene.createOrUpdateSelectionOctree();
// Frustum culling
scene.blockMaterialDirtyMechanism = true;
// Skip pointer move picking
scene.skipPointerMovePicking = true;
// Freeze active meshes
scene.freezeActiveMeshes();
// Hardware scaling
engine.setHardwareScalingLevel(0.5); // Render at half resolution
// Adaptive quality
scene.onBeforeRenderObservable.add(() => {
const fps = engine.getFps();
if (fps < 30) {
// Reduce quality
engine.setHardwareScalingLevel(2);
} else if (fps > 55) {
// Increase quality
engine.setHardwareScalingLevel(1);
}
});
// Incremental loading
scene.useDelayedTextureLoading = true;
// Culling strategy
mesh.cullingStrategy = BABYLON.AbstractMesh.CULLINGSTRATEGY_BOUNDINGSPHERE_ONLY;
// Compressed textures
const texture = new BABYLON.Texture('texture.dds', scene);
// Mipmaps
texture.updateSamplingMode(BABYLON.Texture.TRILINEAR_SAMPLINGMODE);
// Anisotropic filtering
texture.anisotropicFilteringLevel = 4;
// KTX2 compression
const texture = new BABYLON.Texture('texture.ktx2', scene);
Problem : Not disposing resources
// ❌ Bad - memory leak
function createAndRemoveMesh() {
const mesh = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
scene.removeMesh(mesh);
}
Solution : Properly dispose
// ✅ Good
function createAndRemoveMesh() {
const mesh = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
mesh.dispose();
}
// Dispose entire scene
scene.dispose();
// Dispose engine
engine.dispose();
Problem : Each mesh = one draw call
// ❌ Bad - 1000 draw calls
for (let i = 0; i < 1000; i++) {
const box = BABYLON.MeshBuilder.CreateBox('box' + i, {}, scene);
box.position.x = i;
}
Solution : Use instances or merge
// ✅ Good - 1 draw call
const box = BABYLON.MeshBuilder.CreateBox('box', {}, scene);
for (let i = 0; i < 1000; i++) {
const instance = box.createInstance('instance' + i);
instance.position.x = i;
}
Problem : Heavy computations blocking render
// ❌ Bad - blocks rendering
function createManyMeshes() {
for (let i = 0; i < 10000; i++) {
const mesh = BABYLON.MeshBuilder.CreateSphere('sphere' + i, {}, scene);
}
}
Solution : Use async/incremental loading
// ✅ Good - incremental
async function createManyMeshes() {
for (let i = 0; i < 10000; i++) {
const mesh = BABYLON.MeshBuilder.CreateSphere('sphere' + i, {}, scene);
if (i % 100 === 0) {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}
Problem : Camera not responding
// ❌ Bad - forgot attachControl
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
Solution : Always attach controls
// ✅ Good
const camera = new BABYLON.ArcRotateCamera('camera', 0, 0, 10, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, true);
Problem : Using scene before it's ready
// ❌ Bad
BABYLON.SceneLoader.ImportMesh('', 'path/', 'model.gltf', scene);
const mesh = scene.getMeshByName('meshName'); // null!
Solution : Use callbacks or async/await
// ✅ Good
const result = await BABYLON.SceneLoader.ImportMeshAsync('', 'path/', 'model.gltf', scene);
const mesh = scene.getMeshByName('meshName');
// Or with callback
BABYLON.SceneLoader.ImportMesh('', 'path/', 'model.gltf', scene, function(meshes) {
const mesh = meshes[0];
});
Problem : Forgot to enable physics or create aggregates
// ❌ Bad
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {}, scene);
sphere.physicsImpostor = new BABYLON.PhysicsImpostor(sphere, BABYLON.PhysicsImpostor.SphereImpostor, {mass: 1}, scene);
// Error: Physics not enabled!
Solution : Enable physics first, use aggregates
// ✅ Good
const havokInstance = await HavokPhysics();
const havokPlugin = new BABYLON.HavokPlugin(true, havokInstance);
scene.enablePhysics(new BABYLON.Vector3(0, -9.8, 0), havokPlugin);
const sphere = BABYLON.MeshBuilder.CreateSphere('sphere', {}, scene);
const aggregate = new BABYLON.PhysicsAggregate(
sphere,
BABYLON.PhysicsShapeType.SPHERE,
{mass: 1},
scene
);
BABYLON.Effect.ShadersStore['customVertexShader'] = `
precision highp float;
attribute vec3 position;
attribute vec2 uv;
uniform mat4 worldViewProjection;
varying vec2 vUV;
void main(void) {
gl_Position = worldViewProjection * vec4(position, 1.0);
vUV = uv;
}
`;
BABYLON.Effect.ShadersStore['customFragmentShader'] = `
precision highp float;
varying vec2 vUV;
uniform sampler2D textureSampler;
void main(void) {
gl_FragColor = texture2D(textureSampler, vUV);
}
`;
const shaderMaterial = new BABYLON.ShaderMaterial('shader', scene, {
vertex: 'custom',
fragment: 'custom'
}, {
attributes: ['position', 'uv'],
uniforms: ['worldViewProjection']
});
const computeShader = new BABYLON.ComputeShader('compute', engine, {
computeSource: `
#version 450
layout (local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
layout(std430, binding = 0) buffer OutputBuffer { vec4 data[]; } outputBuffer;
void main() {
uint index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 8u;
outputBuffer.data[index] = vec4(1.0, 0.0, 0.0, 1.0);
}
`
});
const noiseTexture = new BABYLON.NoiseProceduralTexture('noise', 256, scene);
noiseTexture.octaves = 4;
noiseTexture.persistence = 0.8;
noiseTexture.animationSpeedFactor = 5;
material.emissiveTexture = noiseTexture;
// Show inspector
scene.debugLayer.show();
// Show bounding boxes
scene.forceShowBoundingBoxes = true;
// Show wireframes
material.wireframe = true;
// Log FPS
setInterval(() => {
console.log('FPS:', engine.getFps());
}, 1000);
// Instrumentation
const instrumentation = new BABYLON.SceneInstrumentation(scene);
instrumentation.captureFrameTime = true;
console.log('Frame time:', instrumentation.frameTimeCounter.average);
This skill is based on Babylon.js 7.x. For latest features, consult the official documentation.
Weekly Installs
104
Repository
GitHub Stars
11
First Seen
Feb 27, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode104
kimi-cli102
github-copilot102
amp102
cline102
codex102
高性能浏览器网络框架:Web性能优化指南,减少延迟提升加载速度
592 周安装