正交与透视相机的区别
- 箭头朝向表示,例如,人拿着相机是站着拍摄、侧着拍摄、躺着拍摄;
- 可通过透视相机去观察,正交相机与物体之间的情况:
createCamera () {
const size = 4;
// 创建正交相机
const orthoCamera = new THREE.OrthographicCamera(-size, size, size / 2, -size / 2, 0.1, 1000);
// 相机位置
orthoCamera.position.set(2, 2, 3)
// 设置相机朝向
orthoCamera.lookAt(this.scene.position)
// 相机添加场景中
this.scene.add(orthoCamera)
this.orthoCamera = orthoCamera
// this.camera = orthoCamera
// console.log(orthoCamera);
// 透视相机 第二个相机
const watcherCamera = new THREE.PerspectiveCamera(75, this.width / this.height)
// 设置相机位置
watcherCamera.position.set(2, 2, 6)
// 设置相机朝向
watcherCamera.lookAt(this.scene.position)
// 将相机添加到场景中
this.scene.add(watcherCamera)
this.camera = watcherCamera
},
// 添加辅助
helpers () {
// 创建辅助坐标系
const axesHelper = new THREE.AxesHelper();
// 相机辅助 观察正交相机
const cameraHelper = new THREE.CameraHelper(this.orthoCamera)
this.cameraHelper = cameraHelper
this.scene.add(axesHelper, cameraHelper)
},
tick () {
//相机辅助更新
this.cameraHelper.update()
}
dat.gui库调试
datGui () {
const _this = this
const gui = new dat.GUI();
const params = {
wireframe: false,
switchCamera () {
console.log(this.camera);
if (_this.camera.type === 'OrthographicCamera') {
// 透视相机
_this.camera = _this.watcherCamera;
// 轨道控制器启用 消除切换之间的干扰
_this.orbitControls.enabled = true;
} else {
_this.camera = _this.orthoCamera;
_this.orbitControls.enabled = false;
}
},
}
gui.add(this.camera.position, 'x', 0.1, 100, 0.1).name('positionX')
gui.add(this.camera.rotation, 'x', 0.1, 100, 0.1).name('rotationX')
gui.add(this.camera, 'near', 0.01, 10, 0.01).onChange(val => {
this.camera.near = val
this.camera.updateProjectionMatrix();
})
gui.add(this.camera, 'far', 1, 100, 1).onChange(val => {
this.camera.far = val
this.camera.updateProjectionMatrix();
})
// 缩放
gui.add(this.camera, 'zoom', 0.1, 10, 0.1).onChange(val => {
this.camera.zoom = val
this.camera.updateProjectionMatrix();
})
// 透视线框
gui.add(params, 'wireframe').onChange(val => {
this.mesh.material.wireframe = val;
})
// 相机切换
gui.add(params, 'switchCamera');
// 添加一个文件夹 可将多个参数放到一个文件夹下
const lightFolder = gui.addFolder('光照')
lightFolder.add(_this.directionalLight, 'intensity', 0, 1, 0.1)
lightFolder.add(_this.directionalLight.position, 'x', -10, 10, 0.1)
lightFolder.add(_this.directionalLight.position, 'y', -10, 10, 0.1)
lightFolder.add(_this.directionalLight.position, 'z', -10, 10, 0.1)
lightFolder.open() // 展开 默认不展开
},
相机视锥辅助对象
createCamera () {
const pCamera = new THREE.PerspectiveCamera(75, this.width / this.height, 1, 10)
pCamera.position.set(2, 2, 3)
pCamera.lookAt(this.scene.position)
this.scene.add(pCamera)
this.pCamera = pCamera;
this.camera = pCamera;
// 透视相机 第二个相机
const watcherCamera = new THREE.PerspectiveCamera(120, this.width / this.height, 0.1, 1000)
// 设置相机位置
watcherCamera.position.set(2, 2, 6)
// 设置相机朝向
watcherCamera.lookAt(this.scene.position)
// 将相机添加到场景中
this.watcherCamera = watcherCamera
// this.camera = watcherCamera
this.scene.add(watcherCamera)
},
datGui () {
const _this = this;
const gui = new dat.GUI();
const params = {
wireframe: false,
switchCamera() {
if ( _this.cameraIndex === 0 ) {
_this.camera = _this.watcherCamera;
_this.cameraIndex = 1;
} else {
_this.camera = _this.pCamera;
_this.cameraIndex = 0
}
},
}
// gui.add(this.camera.position, 'x').min(0.1).max(100).step(0.1).name('positionX');
gui.add(this.camera.position, 'x', 0.1, 100, 0.1).name('positionX')
gui.add(this.camera, 'near', 0.01, 10, 0.01).onChange(val => {
this.camera.near = val
this.camera.updateProjectionMatrix();
})
gui.add(this.camera, 'far', 1, 100, 1).onChange(val => {
this.camera.far = val
this.camera.updateProjectionMatrix();
})
// 缩放
gui.add(this.camera, 'zoom', 0.1, 10, 0.1).onChange(val => {
this.camera.zoom = val
this.camera.updateProjectionMatrix();
})
// 透视线框
gui.add(params, 'wireframe').onChange(val => {
this.mesh.material.wireframe = val;
})
gui.add(this.camera, 'fov', 40, 150, 1).onChange(val => {
this.camera.fov = val;
this.camera.updateProjectionMatrix();
})
gui.add(params, 'switchCamera');
},
// 添加辅助
helpers () {
// 相机辅助 观察相机
const cameraHelper = new THREE.CameraHelper(this.pCamera)
}
是否包含在视锥内
- 可通过 mesh对象的原型链上 computeBoundingBox()方法计算得出
- 调用结果:包围盒最小、最大的空间向量
frustumResult () {
// 通过camera计算视锥
const frustum = new THREE.Frustum()
// 更新以保证拿到最终结果
this.pCamera.updateProjectionMatrix()
frustum.setFromProjectionMatrix(
// 得到视锥体的矩阵
new THREE.Matrix4().multiplyMatrices(
this.pCamera.projectionMatrix,
this.pCamera.matrixWorldInverse,
)
)
const result = frustum.intersectsBox(this.mesh.geometry.boundingBox)
console.log(result); // true false
},
gui.add(this.camera, 'near', 0.01, 10, 0.01).onChange(val => {
this.frustumResult()
})
DragControls拖拽与OrbitControls轨道,同时开启
- 拖拽场景时,只有相机跟随着物体旋转,即仅 OrbitControls轨道生效
- 拖拽物体时,仅物体跟随鼠标移动,即仅 DragControls拖拽生效
controls () {
// 创建轨道控制器
const orbitControls = new OrbitControls(this.camera, this.canvas)
// 开启惯性
orbitControls.enableDamping = true;
this.orbitControls = orbitControls
// orbitControls 该对象上有datGui能调试的属性
console.log(orbitControls);
// 拖拽控制器
const dragControls = new DragControls([this.mesh], this.camera, this.canvas)
// 开启事件监听
dragControls.addEventListener('dragstart', ()=>{
// 拖拽开始事件
orbitControls.enabled = false
})
dragControls.addEventListener('dragend',()=>{
// 拖拽结束
orbitControls.enabled = true
})
},
datGui () {
const _this = this
const gui = new dat.GUI();
// 是否启用
gui.add(_this.orbitControls, 'enabled')
// 阻尼系数 物体运动的惯性大小
gui.add(_this.orbitControls, 'dampingFactor', 0.01, 0.2, 0.01)
// 相机平移的速度
gui.add(_this.orbitControls, 'panSpeed', 1, 10, 1)
// 实现物体自转
gui.add(_this.orbitControls, 'autoRotate')
// 物体自转速度
gui.add(_this.orbitControls, 'autoRotateSpeed', 1, 10, 1)
// 鼠标滚轮是否能缩放
gui.add(_this.orbitControls, 'enableZoom')
// 鼠标滚轮缩放速度
gui.add(_this.orbitControls, 'zoomSpeed')
},
在小窗口上展示,画布中裁剪的区域
- 需要2个相机对象观察
- 渲染2个相机看到的场景
- 裁剪时,需要裁剪2次,并且要开启 renderer.setScissorTest(true)裁剪检测
- 以上方法都是相互独立的,在各自的裁剪函数中,执行渲染;
- WebGLRenderer对象中,setScissor裁剪属性、setViewport视口属性,两者的x、y坐标轴如下:
- 相机对象中,rotation旋转 = quaternion四元数,只要物体发生旋转效果,两者都会触发,因此使用的时候,二选一即可;
- 缩略图与非缩略图出现宽高比例不一致问题:
createCamera () {
// 正交相机
const frustumSize = 2 // 设置显示相机前方高为2的内容 视锥尺寸
const aspect = this.width / this.height
const pCamera = new THREE.OrthographicCamera(-aspect*frustumSize, aspect*frustumSize, aspect, -aspect, 0.1, 1000)
// 设置相机位置
pCamera.position.set(1, 1, 2)
// 设置相机朝向
pCamera.lookAt(this.scene.position)
// 将相机添加到场景中
this.scene.add(pCamera)
this.pCamera = pCamera
this.camera = pCamera
// 缩略图相机
const thumbnailAspect = 150 / 200
const thumbnailCamera = new THREE.OrthographicCamera(-thumbnailAspect*frustumSize, thumbnailAspect*frustumSize, thumbnailAspect, -thumbnailAspect, 0.1, 1000)
// 位置
thumbnailCamera.position.set(1, 1, 2)
// 朝向
thumbnailCamera.lookAt(this.scene.position)
// 场景
this.scene.add(thumbnailCamera)
this.thumbnailCamera = thumbnailCamera
},
// 全局裁剪
clipScene(renderer) {
const dpr = window.devicePixelRatio || 1
// 裁剪
renderer.setScissor(0, 0, this.width, this.height)
// 背景色 透明度
renderer.setClearColor(0x999999, 0.5)
// 设置渲染器屏幕像素比 移动端解决像素问题
renderer.setPixelRatio(dpr)
// 设置渲染器大小
// 调用setSize() 相当于使用了 setViewport(0, 0, this.width, this.height)
renderer.setSize(this.width, this.height)
// 执行渲染
renderer.render(this.scene, this.camera)
},
// 裁剪缩略图
clipThumbnail (renderer) {
// 小窗口 w: 150 h: 200 margin: 10
const w = this.width - 150 - 10
// 更新位置
this.thumbnailCamera.position.copy(this.camera.position)
// 更新旋转 rotation = quaternion 物体触发旋转效果时,两个属性都会触发,仅用其一即可
this.thumbnailCamera.rotation.copy(this.camera.rotation)
// 更新四元数(更新旋转)
// this.thumbnailCamera.quaternion.copy(this.camera.quaternion)
// 更新缩放
this.thumbnailCamera.zoom = this.camera.zoom
// 更新相机矩阵
this.thumbnailCamera.updateProjectionMatrix()
renderer.setScissor(w, 20, 150, 200)
// 设置原因:需要同步 全局裁剪 视口的变化
renderer.setViewport(w, 20, 150, 200)
// 背景色
renderer.setClearColor(0x000000)
//执行渲染
renderer.render(this.scene, this.thumbnailCamera)
},
// 开启裁剪检测
this.renderer.setScissorTest(true)