此篇将threeJs相机的使用做一下记录,简单的封装为vue组件,以备参考。
之后慢慢完善此文。

一、立方相机cubeCamera

cubeCamera,构造一个包含6个PerspectiveCameras(透视摄像机)的立方摄像机,并将其拍摄的场景渲染到一个WebGLCubeRenderTarget上,生成目标纹理(WebGLCubeRenderTarget.texture)。
render target是一个缓存,缓存显卡为正在后台渲染的场景绘制的像素。被用于不同的效果,例如用在一个图像渲染到屏幕上之前先做一些后期处理。
此处,将WebGLCubeRenderTarget.texture作为某个具有envMap属性材质的模型的环境贴图,即此模型反射的环境图案。

CubeCamera.vue

<template>
  <div>
    <div id="canvasContainer" style="width: 900px; height: 800px"></div>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

let scene, camera, renderer, width, height, controls
onMounted(() => {
  init()
})

function init() {
  const canvasContainer = document.querySelector('#canvasContainer')
  width = canvasContainer.clientWidth
  height = canvasContainer.clientHeight

  // 1、创建场景
  scene = new THREE.Scene()
  
  // 2、创建渲染器  
  renderer = new THREE.WebGLRenderer()
  // renderer.setClearColor('#FFFFFF') // 设置渲染器的背景色
  renderer.setClearColor('#88b4e1')
  renderer.setSize(width, height) // 设置渲染区域尺寸  
  canvasContainer.appendChild(renderer.domElement) // 将渲染器创建的canvas元素添加到容器元素
  
  // 3、创建透视相机,用于拍摄主场景
  camera = new THREE.PerspectiveCamera(fov, width / height, 0.1, 1000)
  camera.position.set(100, 50, 200) // 设置相机位置
  camera.lookAt(scene.position) // 设置相机方向(指向的场景对象)
 
  // 4、创建立方相机
  cubeCamera() // 为具有镜面反射效果的模型A拍摄反射的图形,然后作为A的环境贴图
  // 要先创建renderer, 因为cubeCamera.update(renderer, scene)
  
  // 5、其他
  pointLight() // 点灯光
  initControls() // 用鼠标控制透视相机的拍摄角度
  axisHelper() // 坐标辅助器
  animate() 
}

// 循环动画渲染
function animate() {
  requestAnimationFrame(animate)
  renderer.render(scene, camera)
}

// 轨道控制器,使得相机围绕目标进行轨道运动
function initControls() {
  controls = new OrbitControls(camera, renderer.domElement)
}

// 辅助三维坐标系
function axisHelper() {
  const axis = new THREE.AxesHelper(250)
  scene.add(axis)
}

// 创建一个虚拟的球形网格Mesh的辅助对象来模拟点光源PointLight
function pointLight() {
  const pointLight = new THREE.PointLight(0xffffff, 1, 1000)
  pointLight.position.set(100, 150, 100)  //点光源位置
  scene.add(pointLight)
  // 创建点光源辅助对象模拟点光源
  const pointLightHelper = new THREE.PointLightHelper(pointLight, 1)
  scene.add(pointLightHelper)
}

let sphere, box
// 创建立方相机
function cubeCamera() {
  // 创建cube相机的渲染目标
  const cubeRenderTarget = new THREE.WebGLCubeRenderTarget(128, {
    format: THREE.RGBAFormat,
    generateMipmaps: true, 
    minFilter: THREE.LinearMipmapLinearFilter 
  });
  
  // 创建cube相机
  const cubeCamera = new THREE.CubeCamera(1, 100000, cubeRenderTarget)
  scene.add(cubeCamera)
  
  // 创建具能反射环境的模型
  const sphereGeometry = new THREE.SphereGeometry(50)
  const sphereMaterial = new THREE.MeshPhongMaterial({
    color: 0xff0000, 
    envMap: cubeRenderTarget.texture 
  });
  sphere = new THREE.Mesh(sphereGeometry, sphereMaterial)
  sphere.position.set(80, -50, 0)
  scene.add(sphere)
  
  // 创建被反射的模型
  const boxGeometry = new THREE.BoxGeometry(20, 20, 20)
  const boxMaterial = new THREE.MeshPhongMaterial({
    color: 0x00ff00 
  })
  box = new THREE.Mesh(boxGeometry, boxMaterial)
  box.position.set(80, 100, 0)
  scene.add(box)
  
  // 更新渲染目标对象
  sphere.visible = false
  cubeCamera.position.copy(sphere.position)
  cubeCamera.update(renderer, scene)

  // 可以渲染场景了
  sphere.visible = true
}
</script>

注意:光源与两个模型的位置关系

效果图:

二、阵列相机

1. ArrayCamera.vue

<template>
  <div>
    <h1>阵列相机(ArrayCamera)</h1>
    <div id="canvasContainer" style="width: 900px; height: 800px"></div>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { usePerspectiveCamera, useArrayCamera } from '@/composables/threejs/camera'

let scene, camera, renderer, width, height, mesh, controls

onMounted(() => {
  init()
  
})

function init() {
  const canvasContainer = document.querySelector('#canvasContainer')
  width = canvasContainer.clientWidth
  height = canvasContainer.clientHeight

  // 1、创建场景
  scene = new THREE.Scene()
  
  // 2、创建渲染器  
  renderer = new THREE.WebGLRenderer()
  // renderer.setClearColor('#FFFFFF') // 设置渲染器的背景色
  renderer.setClearColor('#88b4e1')
  renderer.setSize(width, height) // 设置渲染区域尺寸
  // 将渲染器创建的canvas元素添加容器元素
  canvasContainer.appendChild(renderer.domElement) 
  
  // 3、创建相机
  camera = arrayCamera()
  
  // 4、其他
  pointLight()
  initControls()
  axisHelper()
  boxGeo()
  animate()
}

function arrayCamera() {
  const camera1 = usePerspectiveCamera(width, height, { x: -100, y: 0, z: 0 }, scene.position)
  const camera2 = usePerspectiveCamera(width, height, { x: 100, y: 0, z: 0 }, scene.position)
  const camera3 = usePerspectiveCamera(width, height, { x: 0, y: 100, z: 0 }, scene.position)

  const cameras = [camera1, camera2, camera3]
  const camera = useArrayCamera(cameras, width, height, 2)
  return camera
}

// 循环动画渲
function animate() {
  requestAnimationFrame(animate)
  mesh.rotateY(0.01)
  renderer.render(scene, camera)
}

// 轨道控制器,使得相机围绕目标进行轨道运动
function initControls() {
  controls = new OrbitControls(camera, renderer.domElement)
}

// 辅助三维坐标系
function axisHelper() {
  const axis = new THREE.AxesHelper(250)
  scene.add(axis)
}

// 创建一个虚拟的球形网格Mesh的辅助对象来模拟点光源PointLight
function pointLight() {
  const pointLight = new THREE.PointLight(0xffffff, 1, 1000)
  pointLight.position.set(100, 150, 100)  //点光源位置
  scene.add(pointLight)
  // 创建点光源辅助对象模拟点光源
  const pointLightHelper = new THREE.PointLightHelper(pointLight, 1)
  scene.add(pointLightHelper)
}

// 创建立方体
// 几何体默认位于场景世界坐标的原点(0, 0, 0)
function boxGeo() {
  // const geometry = new THREE.BoxGeometry(50, 100, 20)
  // const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })
  // mesh = new THREE.Mesh(geometry, material)

  var geometry = new THREE.BoxGeometry(20, 20, 20)
  var material = new THREE.MeshLambertMaterial({
    color: 0x0000ff
  })
  mesh = new THREE.Mesh(geometry, material)

  // 将物体和灯光添加到场景里
  scene.add(mesh)
}
</script>

2. @/composables/threejs/camera.js

import * as THREE from 'three'

// 创建透视相机
function usePerspectiveCamera(width, height, position = { x: 0, y: 0, z: 0 }, lookPos = { x: 0, y: 0, z: 0 }, fov = 75, near = 0.1, far = 1000) {
  const camera = new THREE.PerspectiveCamera(fov, width / height, 0.1, 1000)
  camera.position.set(position.x, position.y, position.z) // 设置相机位置
  camera.lookAt(lookPos.x, lookPos.y, lookPos.z) // 设置相机方向(指向的场景对象)
  return camera
}

// 2. 创建摄像机阵列
// 画布的width, height
function useArrayCamera(cameras, width, height, colNum) {
  const rowNum = Math.ceil(cameras.length / colNum)
  const cameraArrs = []
  for(let i = 0; i < rowNum; i++) {
    const cameraItem = cameras.slice(i * colNum, (i + 1) * colNum)
    cameraArrs.push(cameraItem) 
  }

  for(const [rowIndex, rowCameras] of cameraArrs.entries()) {
    for(const [colIndex, camera] of rowCameras.entries()) {
      const colWidth = width / colNum
      const rowHeight = height / rowNum
      const x = colIndex * colWidth
      const y = rowIndex * rowHeight
      camera.viewport = new THREE.Vector4(x, y, colWidth, rowHeight) // x, y, width, height 根据每行每列相机数决定
      camera.updateMatrixWorld() // 更新物体及其后代的全局变换。
    }
  }
  
  const arrayCamera = new THREE.ArrayCamera(cameras)
  return arrayCamera
}

export {
  usePerspectiveCamera,
  useArrayCamera
}

效果图:


lavender
27 声望2 粉丝