0.前言:
前阵子接到一个需求是需要开发一个能够跑在环形屏幕上的3d展示项目
刚接到这个项目的时候我以为能吧相机fov直接调整成360°,后来发现我想多了。。babylonjs中的相机最大视角就是π(180°)
然后我又想说,那我干脆两个相机拼一起。
但是两个相机边缘会有失真,效果不好效果不好。
算了 暴力一点,我写了个全景相机类,想用几个相机拼接用几个相机,相机数量越多拼接的越丝滑,移动的话利用主相机的控制器进行移动,其他副相机都绑在这个相机身上。
(本文完整代码连接如下,为ts编写,需要js移步ts官网的playground编译成自己需要的版本)
1.使用方法:
首先导入
import AnnularCamera from "./AnnularCamera.ts";
然后使用
// 默认相机
private defaultCamera ():void {
this.camera = new AnnularCamera('defaultCamera', new Vector3(0, 2, -2), this.scene)
this.camera.ellipsoid = new Vector3(0.15, 0.9, 0.15)
this.camera.speed = 0.2
this.camera.checkCollisions = true
this.camera.applyGravity = true
this.camera.attachControl(this.canvas, true)
}
齐活儿
2.代码讲解
首先导入我们需要的东西
import { Scene, UniversalCamera, Vector3, Viewport } from '@babylonjs/core'
创建AnnularCamera类
export default class AnnularCamera {
constructor (
name:string, // 相机名
position:Vector3, // 位置
scene:Scene, // 场景
CameraCount?:number // 相机数量
){
//...
}
}
声明所需要的私有变量
private _name: string // 相机名
private readonly scene:Scene // 场景
private readonly mainCamera:UniversalCamera // 主相机
private deputyCameras:UniversalCamera[] = [] // 副相机组
private readonly CameraCount:number // 相机数量
private _position:Vector3 // 相机位置
private _ellipsoid!:Vector3 // 碰撞检测盒子大小
private _speed!: number // 相机速度
private _checkCollisions!: boolean // 检查碰撞检测
private _applyGravity!: boolean // 应用重力
在构造函数中进行初始化操作
constructor (
name:string, // 相机名
position:Vector3, // 位置
scene:Scene, // 场景
CameraCount?:number // 相机数量
) {
// 初始化相机名
this._name = name
// 初始化相机位置
this._position = position
// 初始化相机数量
this.CameraCount = (CameraCount || 4) < 4 ? 4 : (CameraCount || 4)
// 初始化场景
this.scene = scene
// 初始化主相机
this.mainCamera = new UniversalCamera(
this._name,
this.position,
this.scene
)
this.mainCamera.fov = Math.PI / (this.CameraCount / 2) // 设置主相机视野
this.mainCamera.fovMode = UniversalCamera.FOVMODE_HORIZONTAL_FIXED // 设置主相机视野模式
// eslint-disable-next-line no-unused-expressions
scene.activeCameras?.push(this.mainCamera) // 主相机推入场景激活相机组
this.mainCamera.viewport = new Viewport(0, 0, 1 / this.CameraCount, 1) // 设置主相机视口
// 初始化副相机组
for (let i = 0; i < this.CameraCount - 1; i++) {
// 定义副相机
const deputyCamera = new UniversalCamera(
this._name + 'Camera' + (i + 1),
new Vector3(0, 0, 0),
this.scene
)
deputyCamera.parent = this.mainCamera // 设置父级别为主相机
this.deputyCameras.push(deputyCamera) // 推入副相机组
// eslint-disable-next-line no-unused-expressions
scene.activeCameras?.push(deputyCamera) // 副相机推入场景激活相机组
deputyCamera.rotation = new Vector3(0, (Math.PI / (this.CameraCount / 2)) * (i + 1), 0) // 设置副相机旋转
deputyCamera.fov = Math.PI / (this.CameraCount / 2) // 设置相机视野
deputyCamera.fovMode = UniversalCamera.FOVMODE_HORIZONTAL_FIXED // 设置相机视野模式
deputyCamera.viewport = new Viewport((1 / this.CameraCount) * (i + 1), 0, 1 / this.CameraCount, 1) // 设置相机视口
}
}
公开主相机的attachControl
public attachControl = (canvas: HTMLCanvasElement, noPreventDefault?: boolean | undefined):void => {
this.mainCamera.attachControl(canvas, noPreventDefault)
}
将主相机常用的一些方法和属性用setter和getter公开
/*
* getter/setter
*/
// 碰撞检测盒getter/setter
public get ellipsoid (): Vector3 {
return this._ellipsoid
}
public set ellipsoid (value: Vector3) {
this.mainCamera.ellipsoid = value
this._ellipsoid = value
}
// 相机位置getter/setter
public get position (): Vector3 {
return this._position
}
public set position (value: Vector3) {
this.mainCamera.position = value
this._position = value
}
// 相机名getter/setter
public get name (): string {
return this._name
}
public set name (value: string) {
this.mainCamera.name = value
this._name = value
}
// 相机速度
public get speed (): number {
return this._speed
}
public set speed (value: number) {
this.mainCamera.speed = value
this._speed = value
}
// 是否应用碰撞检测
public get checkCollisions (): boolean {
return this._checkCollisions
}
public set checkCollisions (value: boolean) {
this.mainCamera.checkCollisions = value
this._checkCollisions = value
}
// 是否应用重力
public get applyGravity (): boolean {
return this._applyGravity
}
public set applyGravity (value: boolean) {
this.mainCamera.applyGravity = value
this._applyGravity = value
}
齐活儿。
3.使用
放映的时候,用英伟达或者amd驱动的跨屏幕融合吧环形屏幕搞成理论上的一整块,然后网页全屏就可以用。
4.后记
其实现在还有一些问题,就是目前移动控制器的正向是主相机的朝向,在上面画面的最左侧,之后还需要优化一下,将主相机挪到画面的中间(最好是能够随意挪动,想放在第几个放在第几个)