Skip to content

多个相机的创建

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>