Appearance
多个相机的创建
fps: 0
点击运行
<template>
<div id="cameraMore">
<div class="flex space-between">
<div>fps: {{ fps }}</div>
<div @click="onTrigger" class="pointer">点击{{ !isRunning ? '运行' : '关闭' }}</div>
</div>
<div v-if="isRunning">
<canvas id="cameraMore0"></canvas>
<canvas id="cameraMore1"></canvas>
<canvas id="cameraMore2"></canvas>
<canvas id="cameraMore3"></canvas>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, nextTick, onUnmounted } from 'vue'
import {
Engine,
Scene,
ArcRotateCamera,
Vector3,
HemisphericLight,
MeshBuilder,
PBRMetallicRoughnessMaterial
} from 'babylonjs'
let sceneResources
const fps = ref(0)
const isRunning = ref(false)
const onTrigger = async () => {
if (!isRunning.value) {
isRunning.value = true
await nextTick()
sceneResources = await initScene()
} else {
isRunning.value = false
destroy()
}
}
const initScene = async () => {
const ele = document.createElement('canvas') as HTMLCanvasElement
const div = document.getElementById('cameraMore') as HTMLElement
div.addEventListener('wheel', function(event) {
// 根据需要处理滚动
// 例如,可以修改相机的半径或角度
event.preventDefault() // 阻止默认滚动行为
})
const engine: any = new Engine(ele, true, {
preserveDrawingBuffer: true,
stencil: true,
disableWebGL2Support: false
})
engine.inputElement = document.getElementById('cameraMore0')
const scene = new Scene(engine)
scene.useRightHandedSystem = true
const camera0 = new ArcRotateCamera('Camera0', 0, 0.8, 5, new Vector3(0, 0, 0), scene)
camera0.setTarget(new Vector3(0, 0, 0))
camera0.lowerRadiusLimit = 4
camera0.upperRadiusLimit = 20
camera0.attachControl(document.getElementById('cameraMore0'), true)
const camera1 = new ArcRotateCamera('Camera1', 0, 0.8, 10, new Vector3(0, 0, 0), scene)
const camera2 = new ArcRotateCamera('Camera2', 0, 0.8, 10, new Vector3(0, 0, 0), scene)
const camera3 = new ArcRotateCamera('Camera3', 0, 0.8, 10, new Vector3(0, 0, 0), scene)
const createLight = () => {
const light = new HemisphericLight('light',new Vector3(1, 1, 0), scene)
return light
}
const createBox = () => {
const box = MeshBuilder.CreateBox('Box', {
size: 2
}, scene)
const mat = new PBRMetallicRoughnessMaterial('mat', scene)
mat.metallic = 1
mat.roughness = 0.5
box.material = mat
}
scene.createDefaultEnvironment()
engine.registerView(document.getElementById('cameraMore0'))
engine.registerView(document.getElementById('cameraMore1'), camera1)
engine.registerView(document.getElementById('cameraMore2'), camera2)
engine.registerView(document.getElementById('cameraMore3'), camera3)
let alpha = 0
scene.registerBeforeRender(() => {
camera1.radius = 10 + Math.cos(alpha) * 5
camera2.alpha += 0.01
camera3.beta = Math.cos(alpha)
alpha += 0.01
})
const runAnimate = () => {
engine.runRenderLoop(function() {
if (scene && scene.activeCamera) {
scene.render()
fps.value = engine.getFps().toFixed(2)
}
})
}
createLight()
createBox()
runAnimate()
return {
scene,
engine,
}
}
const destroy = () => {
if (sceneResources) {
sceneResources.engine.stopRenderLoop()
sceneResources.engine.dispose()
sceneResources.scene.dispose()
sceneResources = null
}
}
onMounted(async() => {
await nextTick()
})
onUnmounted(() => {
destroy()
})
</script>
box 朝向和 camera 朝向一致
fps: 0
点击运行
<template>
<div>
<div class="flex space-between">
<div>fps: {{ fps }}</div>
<div @click="onTrigger" class="pointer">点击{{ !isRunning ? '运行' : '关闭' }}</div>
</div>
<canvas v-if="isRunning" id="boxFollowCamera" class="stage"></canvas>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref, nextTick, onUnmounted } from 'vue'
import 'babylonjs-loaders'
import {
Engine,
Scene,
ArcRotateCamera,
Vector3,
StandardMaterial,
Color3,
Color4,
DirectionalLight,
MeshBuilder,
Axis,
Quaternion,
} from 'babylonjs'
import {
AdvancedDynamicTexture,
StackPanel,
Control,
TextBlock,
} from 'babylonjs-gui'
let sceneResources, adt
const fps = ref(0)
const isRunning = ref(false)
const isRightHandedSystem = false
const onTrigger = async () => {
if (!isRunning.value) {
isRunning.value = true
await nextTick()
sceneResources = await initScene()
} else {
isRunning.value = false
destroy()
}
}
const initScene = async () => {
const ele = document.getElementById("boxFollowCamera") as any
ele.addEventListener('wheel', function(event) {
// 根据需要处理滚动
// 例如,可以修改相机的半径或角度
event.preventDefault() // 阻止默认滚动行为
})
const engine: any = new Engine(ele, true, {
preserveDrawingBuffer: true,
stencil: true,
disableWebGL2Support: false
})
const scene: any = new Scene(engine)
// 使用左手坐标系
scene.useRightHandedSystem = isRightHandedSystem
const camera = new ArcRotateCamera('camera', 0, 0, 15, new Vector3(0, 0, 0), scene)
camera.upperBetaLimit = Math.PI / 2.2
camera.wheelPrecision = 30
camera.panningSensibility = 10
camera.attachControl(ele, true)
camera.setPosition(new Vector3(50, 50, 50))
const createAxis = () => {
const axisX = MeshBuilder.CreateLines(
'axisX', {
colors: [new Color4(1, 0, 0, 1), new Color4(1, 0, 0, 1)],
points: [new Vector3(0, 0, 0), new Vector3(80, 0, 0)]
},
scene
)
const axisY = MeshBuilder.CreateLines(
'axisY', {
colors: [new Color4(0, 1, 0, 1), new Color4(0, 1, 0, 1) ],
points: [new Vector3(0, 0, 0), new Vector3(0, 80, 0) ]
},
scene
)
const axisZ = MeshBuilder.CreateLines(
'axisZ', {
colors: [new Color4(0, 0, 1, 1), new Color4(0, 0, 1, 1)],
points: [new Vector3(0, 0, 0), new Vector3(0, 0, 80)]
},
scene
)
return [axisX, axisY, axisZ]
}
const createGui = async () => {
adt = AdvancedDynamicTexture.CreateFullscreenUI('UI')
const xBox = MeshBuilder.CreateBox('x', { size: 1 }, scene)
xBox.position = new Vector3(80, 0, 0)
const xPanel = new StackPanel()
xPanel.width = '20px'
xPanel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT
xPanel.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM
const x = new TextBlock()
x.text = 'X'
x.height = '30px'
x.color = 'red'
adt.addControl(xPanel)
xPanel.addControl(x)
xPanel.linkWithMesh(xBox)
const yBox = MeshBuilder.CreateBox('y', { size: 1 }, scene)
yBox.position = new Vector3(0, 80, 0)
const yPanel = new StackPanel()
yPanel.width = '20px'
yPanel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT
yPanel.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM
const y = new TextBlock()
y.text = 'Y'
y.height = '30px'
y.color = 'green'
adt.addControl(yPanel)
yPanel.addControl(y)
yPanel.linkWithMesh(yBox)
const zBox = MeshBuilder.CreateBox('z', { size: 1 }, scene)
zBox.position = new Vector3(0, 0, 80)
const zPanel = new StackPanel()
zPanel.width = '20px'
zPanel.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_RIGHT
zPanel.verticalAlignment = Control.VERTICAL_ALIGNMENT_BOTTOM
const z = new TextBlock()
z.text = 'Z'
z.height = '30px'
z.color = 'blue'
adt.addControl(zPanel)
zPanel.addControl(z)
zPanel.linkWithMesh(zBox)
}
const createLight = () => {
const light = new DirectionalLight('dir01', new Vector3(0, -1, -0.3), scene)
light.position = new Vector3(20, 60, 30)
return light
}
const createBox = () => {
const material = new StandardMaterial('m1')
material.emissiveColor = new Color3(0.2, 0.3, 0.4)
material.backFaceCulling = false
var box = MeshBuilder.CreateBox('box', {
width: 10,
height: 10,
depth: 1
}, scene)
box.material = material
box.position = new Vector3(10, 10, 10)
box.rotation.x = Math.PI / 2
return box
}
const runAnimate = () => {
engine.runRenderLoop(function() {
if (scene && scene.activeCamera) {
scene.render()
fps.value = engine.getFps().toFixed(2)
}
})
}
createAxis()
createGui()
createLight()
const box = createBox()
runAnimate()
scene.registerBeforeRender(() => {
// 获取相机的朝向向量
const cameraDirection = camera.getDirection(Axis.Z)
const rotationAxis = Vector3.Cross(Axis.Z, cameraDirection)
const rotationAngle = Math.acos(Vector3.Dot(Axis.Z, cameraDirection))
// BABYLON.Quaternion.RotationAxis 用于创建一个四元数(Quaternion),该四元数表示围绕指定轴旋转特定角度的旋转
const rotationQuaternion = Quaternion.RotationAxis(rotationAxis, rotationAngle)
// 将旋转应用到box
box.rotationQuaternion = rotationQuaternion
})
return {
scene,
engine,
}
}
const destroy = () => {
if (sceneResources) {
sceneResources.engine.stopRenderLoop()
sceneResources.engine.dispose()
sceneResources.scene.dispose()
sceneResources = null
}
if (adt) {
adt.dispose()
adt = null
}
}
onMounted(async() => {
await nextTick()
})
onUnmounted(() => {
destroy()
})
</script>