three.js -- Shadows 投影

189 阅读2分钟

使用阴影不难,难的是优化性能
投影有一独立的投影贴图
1、要使用投影需要将要投影的物体添加到阴影贴图中
2、需要有物体接收阴影
3、开启灯光投影(有三种)

PointLight 点光
DirectionalLight 平行光
SpotLight 聚光灯

// 启用投影

const renderer = new THREE. WebGLRenderer({
    canvas: canvas
})
renderer.setSize(sizes.width, sizes.height)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))

renderer.shadowMap.enabled = true /* 1、启用投影 */
renderer.shadowMap.type = THREE.PCFSoftShadowMap /* 投影类型有三种 */
  
// 创建材质
const material = new THREE.MeshStandarMatterial()
material.roughness = 0.7

// 创建几何体和平面
const sphere = new THREE.Mesh(
  new THREE.SphrereBufferGeometry(0.5, 32, 32),
  material
)
sphere.castShadow = true /* 2、对象渲染到阴影贴图中 */

const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(5, 5),
  material
)
plane.receiveShadow = true /* 3、接收阴影 */

plane.position.x = - Math.PI * 0.5;
plane.position.y = -0.5
scene.add(sphere, plane)

// 创建灯光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(2, 2, -1)
directionalLight.castShadow = true /* 4、灯光投影渲染到阴影贴图中 */
// 投影清晰度(优化投影)
directionalLight.shadow.mapSize.width = 1024
directionalLight.shadow.mapSize.height = 1024
// 模糊度
directionalLight.shadow.radius = 10
// 投影是通过一个正交相机观看的,所以可以设置这个正交相机
// 一是可以优化性能,二是可以得到最佳效果
directionalLight.shadow.camera.top = 2
directionalLight.shadow.camera.right = 2
directionalLight.shadow.camera.bottom = -2
directionalLight.shadow.camera.left = -2
// 设置能看的投影的远近
directionalLight.shadow.camera.near = 1
directionalLight.shadow.camera.far = 6

// 添加灯光相机辅助对象
const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
directionalLightCameraHelper.visible = false /* 将投影相机调整到满意就将助手隐藏掉 */
scene.add(directionalLightCameraHelper)

SpotLight 聚光灯

const spotLight = new THREE.SpotLight(0xffffff, 0.4, Math.PI * 0.3)
spotLight.castShadow = true
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
spotLight.shadow.camera.fov = 30
spotLight.shadow.camera.mear = 1
spotLight.shadow.camera.far = 6

spotLight.position.set(0, 2, 2)
scene.add(spotLight)
scene.add(spotLight.target)

// 组手
const spotLightCameraHelper = new THREE.CameraHelper(spotLight.shadow.camera)
scene.add(spotLightCameraHelper)

PointLight 点光源

const pointLight = new THREE.PointLight(0xffffff, 0.3)
pointLight.castShadow = true

pointLight.shadow.mapSize.width = 1024
pointLight.shadow.mapSize.height = 1024
pointLight.shadow.camera.fov = 30
pointLight.shadow.camera.mear = 1
pointLight.shadow.camera.far = 6

通过纹理加载投影

//添加小球
const sphere = new THREE.Mesh(
  new THREE.SphereBufferGeometry(5, 5),
  new THREE.MeshBasicMaterial(0xffffff)
)
scene.add(sphere)

// 加载阴影纹理
const textureLoader = new THREE.TextureLoader()
const bakedShadow = textureLoader.load('/textures/bakedShadow.jpg')
// 给地板添加阴影纹理(但是这是静态的,不能移动物体,需要解决)
const plane = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(5, 5),
  new THREE.MeshBasicMaterial(0xffffff)
)
scene.add(plane)

// 解决动态贴图方案:
// 创建一个平面专门用来放投影纹理
// 使用透明贴图
// 加载另一种纹理
const simpleShadow = textureLoader.load('/textures/simpleShadow.jpg')
const sphereShadow = new THREE.Mesh(
  new THREE.PlaneBufferGeometry(1.5, 1.5),
  new THREE.MeshBasicMaterial({
    color: 0xff0000,
    transparent: true,
    alphaMap: simpleShadow
  })
)
sphereShadow.rotation.x = -Math.PI * 0.5
sphereShadow.position.y = plane.position.y + 0.01
scene.add(sphereShadow)

// 动画函数,投影动态移动
const clock = new THREE.Clock()
const tick = () => {
  const elapsedTime = clock.getElapsedTime()
  // 小球运动
  sphere.position.x = Math.cos(elapsedTime) * 1.5
  sphere.position.z = Math.sin(elapsedTime) * 1.5
  sphere.position,y = Math.abs(Math.sin(elapsedTime * 3))
  // 阴影跟随小球变化
  sphereShadow.position.x = sphere.position.x
  sphereShadow.position.z = sphere.position.z
  sphereShadow.material.opacity = (1-sphere.position.y) * 0.3
  
  controls.update()
  
  renderer.render(scene, camera)
  window.requestAnimationFrame(tick)
}
tick()