用threejs封装3d渲染器

159 阅读3分钟

本项目是基于vue3+threejs封装的3d渲染器 , 可加载glb obj fbx格式的3d效果

总结

onMounted(() => {
   InitBase()                               //初始化基础设施
   InitModels()                             //初始化模型
   renderer.setAnimationLoop(animation)     //循环渲染
})

1 初始化基础设施中包括: 初始化场景 + 初始化相机 + 初始化渲染器 + 初始化控制器 +初始化灯光

const InitScene = () => {
  scene = new THREE.Scene()                  // 场景
  const path = '/src/assets/pisa/'           // 环境贴图
  const format = '.png'
  const urls = [
    path + 'px' + format,
    path + 'nx' + format,
    path + 'py' + format,
    path + 'ny' + format,
    path + 'pz' + format,
    path + 'nz' + format
  ]
  sceneTexture = new THREE.CubeTextureLoader().load(urls)
  scene.background = sceneTexture                      // 环境贴图展示
}
 
 
const InitCamera = () => {
  camera = new THREE.PerspectiveCamera(
    40, 
    width.value / height.value,
    0.01,
    2000
  )
 
  camera.position.x = 0
  camera.position.y = 10
  camera.position.z = 20
}
 
const InitRenderer = () => {
  renderer = new THREE.WebGLRenderer({                //渲染器
    antialias: true,
    alpha: true,
    canvas: canvasElement.value
  })
  renderer.shadowMap.enabled = true                   //阴影渲染
  renderer.shadowMap.type = THREE.PCFSoftShadowMap   
}
 
const InitControls = () => {
  controls = new OrbitControls(camera, renderer.domElement)
  controls.enableDamping = true;                                  //阻尼
}
 
const InitLight = () => {
  const ambientHemisphere = new THREE.HemisphereLight(0xffffff, 0xbfd4d2, 1)   //环境光
  scene.add(ambientHemisphere)
 
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 2)           //半球光可以让3d人物亮起来
  hemiLight.position.set(0, 50, 0)
  scene.add(hemiLight)
 
  const dirLight = new THREE.DirectionalLight(0xffffff, 3)                     //平行光,类似于太阳光照
  dirLight.color.setHSL(0.1, 1, 0.95)
  dirLight.position.set(-30, 52.5, 30)
  dirLight.castShadow = true
  dirLight.shadow.camera.top = 4
  dirLight.shadow.camera.bottom = -4
  dirLight.shadow.camera.left = -4
  dirLight.shadow.camera.right = 4
  dirLight.shadow.camera.near = 0.1
  dirLight.shadow.camera.far = 400
  scene.add(dirLight)
}

2 初始化模型

const InitModels = () => {
    const loader = new GLTFLoader()                                      // 创建GLTF加载器
    loader.load(                                                         // 加载GLB模型
      props.modelUrl,
      gltf => {
        gltf.scene.traverse(function (child: any) {
          if (child.isMesh) {                                        // 为每个mesh设置环境贴图
            {
              child.castShadow = true
              child.receiveShadow = true
              child.material.envMap = sceneTexture
              child.material.needsUpdate = true
            }
        const model = gltf.scene                                    //设置box3包围盒
        const box = new THREE.Box3().setFromObject(model)
        const size = new THREE.Vector3()
        box.getSize(size)
 
        const center = new THREE.Vector3()                         //拿到盒子中心
        box.getCenter(center) 
        const targetWidth = 2
        const scaleRatio = targetWidth / size.z
 
        model.scale.x *= scaleRatio                     // 等比例缩放和调整3d人物中心位于底部中心
        model.scale.y *= scaleRatio
        model.scale.z *= scaleRatio
        model.position.y -= center.y - (size.y * scaleRatio) / 2
 
        scene.add(gltf.scene)                                      // 将模型添加到场景中
      },
      undefined,
      error => {
        console.error(error)
      }
    )
  }
}

3 循环渲染

function animation() {
  renderer.setPixelRatio(window.devicePixelRatio)                   //高分辨率屏幕上更加清晰
  renderer.setSize(width.value, height.value)
  if (camera) {                             //更新相机纵横比,匹配当前画布的宽高比,确保不被拉伸压缩
    camera.aspect = width.value / height.value  
    camera.updateProjectionMatrix()
  }
 
  renderer.render(scene, camera)
}

效果图如下

微信截图_20231124141420.png

拓展1: 如果效果是下图 , 说明环境贴图没效果 , 需查看自己初始化场景中是否设置了相关属性

const InitScene = () => {
  scene = new THREE.Scene()                  // 场景
  const path = '/src/assets/pisa/'           // 环境贴图
  const format = '.png'
  const urls = [
    path + 'px' + format,
    path + 'nx' + format,
    path + 'py' + format,
    path + 'ny' + format,
    path + 'pz' + format,
    path + 'nz' + format
  ]
  sceneTexture = new THREE.CubeTextureLoader().load(urls)
  scene.background = sceneTexture                      // 环境贴图展示
}

微信截图_20231124121226.png

拓展2 : 如果需要人物阴影 , 应再添加地面 , 并让地面可以接受阴影效果 (在初始化场景中去添加地面效果)

const InitScene = () => {
  scene = new THREE.Scene()                  // 场景
  const path = '/src/assets/pisa/'           // 环境贴图
  const format = '.png'
  const urls = [
    path + 'px' + format,
    path + 'nx' + format,
    path + 'py' + format,
    path + 'ny' + format,
    path + 'pz' + format,
    path + 'nz' + format
  ]
  sceneTexture = new THREE.CubeTextureLoader().load(urls)
  scene.background = sceneTexture                      // 环境贴图展示
 
 
  ,
{
  const groundGeo = new THREE.PlaneGeometry(20, 20)   // 创建了矩形
  const groundMat = new THREE.MeshLambertMaterial({   // 创建了材质
      color: 0x888888,
      envMap: sceneTexture       // 是否开启环境贴图
    })
  const ground = new THREE.Mesh(groundGeo, groundMat) // 创建了一个网格对象作为地面
  ground.rotation.x = -Math.PI / 2                   // 旋转地面与x轴平行
  ground.receiveShadow = true                        // 能接收阴影
 
  scene.add(ground)  
}
 
}

微信截图_20231124140501.png

拓展3 : 平行光和聚光灯效果的互换 , 上图是平行光效果图 , 下图是切换为聚光灯效果图

const InitLight = () => {
  let spotLight = new THREE.SpotLight(0xffffff, 300)
  spotLight.position.set(2.5, 5, 2.5)
  spotLight.angle = Math.PI / 4
  spotLight.penumbra = 1
  spotLight.decay = 2
  spotLight.distance = 0
  // 设置聚光灯的旋转角度
  // spotLight.rotation.x = Math.PI / -4 // 绕X轴旋转180度,使其朝下照射
 
  spotLight.castShadow = true
  spotLight.shadow.mapSize.width = 1024
  spotLight.shadow.mapSize.height = 1024
  spotLight.shadow.camera.near = 1
  spotLight.shadow.camera.far = 10
  spotLight.shadow.focus = 1
  scene.add(spotLight)
  // scene.add(new THREE.CameraHelper(spotLight.shadow.camera))
 
  const ambientHemisphere = new THREE.HemisphereLight(0xffffff, 0xbfd4d2, 1)
  scene.add(ambientHemisphere)
 
  const hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 2)
  hemiLight.position.set(0, 50, 0)
  scene.add(hemiLight)
 
}

微信截图_20231124141829.png