今日份案例是阴影图查看器。主要技术:光源、阴影和阴影图查看器的引入和使用。具体效果如下:
首先需要搭建基础环境,场景、相机、渲染器、控制器。不清楚的可以查看three.js基础案例day01。
1. 添加圆环扭结几何体、立方体和平面
// torus 扭结几何体
let geomery = new THREE.TorusKnotGeometry(25, 8, 200, 20)
let material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 150,
specular: 0x222222,
})
torusKnot = new THREE.Mesh(geomery, material)
torusKnot.scale.multiplyScalar(1 / 18)
torusKnot.position.y = 3
scene.add(torusKnot)
// cube
geomery = new THREE.BoxGeometry(3, 3, 3)
cube = new THREE.Mesh(geomery, material)
cube.position.set(9, 3, 9)
scene.add(cube)
// ground
geomery = new THREE.BoxGeometry(10, 0.15, 10)
material = new THREE.MeshPhongMaterial({
color: 0xa0adaf,
shininess: 150,
specular: 0x111111,
})
ground = new THREE.Mesh(geomery, material)
ground.scale.multiplyScalar(3)
scene.add(ground)
2.添加聚光灯和平行光
spotLight = new THREE.SpotLight(0xffffff)
spotLight.name = 'Spot light'
spotLight.angle = Math.PI / 5
spotLight.penumbra - 0.3
spotLight.position.set(10, 10, 5)
scene.add(spotLight)
dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.name = 'Dir light'
dirLight.position.set(0, 10, 0)
scene.add(dirLight)
3.设置参数 使物体产生阴影
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.BasicShadowMap
spotLight.castShadow = true
dirLight.castShadow = true
torusKnot.castShadow = true
torusKnot.receiveShadow = true
cube.castShadow = true
cube.receiveShadow = true
ground.castShadow = false
ground.receiveShadow = true
4.创建相机辅助器并将其添加到场景中
注:线框显示的其实的可视化聚光灯和方向灯的阴影相机,并设置了近远裁剪面,阴影贴图大小和视场的边界
// 近裁剪面的距离
spotLight.shadow.camera.near = 8
// 远裁剪面的距离
spotLight.shadow.camera.far = 30
// 阴影贴图的大小
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
scene.add(new THREE.CameraHelper(spotLight.shadow.camera))
dirLight.shadow.camera.near = 1
dirLight.shadow.camera.far = 10
// 边界
dirLight.shadow.camera.right = 15
dirLight.shadow.camera.left = -15
dirLight.shadow.camera.top = 15
dirLight.shadow.camera.bottom = -15
scene.add(new THREE.CameraHelper(dirLight.shadow.camera))
5. 物体运动起来
// clock最开始有设置 clock = new THREE.Clock()
// 帧时间
const delta = clock.getDelta()
torusKnot.rotation.x += 0.25 * delta
torusKnot.rotation.y += 2 * delta
torusKnot.rotation.z += 1 * delta
cube.rotation.x += 0.25 * delta
cube.rotation.y += 2 * delta
cube.rotation.z += 1 * delta
6.引入阴影图查看器并使用
function resizeShadowMapViewer() {
const size = width * 0.15
dirLightShadowMapViewer.position.x = 10
dirLightShadowMapViewer.position.y = 10
dirLightShadowMapViewer.size.width = size
dirLightShadowMapViewer.size.height = size
dirLightShadowMapViewer.update()
// 使用.set() 会自动调用.update()
spotLightShadowMapViewer.size.set(size, size)
spotLightShadowMapViewer.position.set(size + 20, 10)
}
// 需要在render中调用
function render() {
dirLightShadowMapViewer.render(renderer)
spotLightShadowMapViewer.render(renderer)
}
全部代码
<template>
<div id="threeId" ref="elementRef"></div>
</template>
<script setup lang="ts">
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { ShadowMapViewer } from 'three/examples/jsm/utils/ShadowMapViewer.js'
import { ref, onMounted } from 'vue'
let width, height
let scene, camera, renderer, controls
let spotLight, dirLight, torusKnot, cube, ground
let dirLightShadowMapViewer, spotLightShadowMapViewer
let clock = new THREE.Clock()
const elementRef = ref(null)
onMounted(() => {
const element = elementRef.value
width = element.offsetWidth
height = element.offsetHeight
initScene()
initLights()
initMeshes()
enableShadow()
initCameraHelper()
initShadowMapViewer()
render()
})
function initScene() {
// 初始化场景: 创建场景,相机,物体,渲染器
scene = new THREE.Scene()
scene.background = new THREE.Color(0x808080)
camera = new THREE.PerspectiveCamera(60, width / height, 1, 1000)
camera.position.set(20, 10, 20)
scene.add(camera)
// 三维坐标
// const axesHelper = new THREE.AxesHelper(40)
// scene.add(axesHelper)
// antialias:是否执行抗锯齿
renderer = new THREE.WebGLRenderer({ antialias: true })
// 设备像素比
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height)
elementRef.value.appendChild(renderer.domElement)
controls = new OrbitControls(camera, renderer.domElement)
}
function initLights() {
scene.add(new THREE.AmbientLight(0x404040))
spotLight = new THREE.SpotLight(0xffffff)
spotLight.name = 'Spot light'
spotLight.angle = Math.PI / 5
spotLight.penumbra - 0.3
spotLight.position.set(10, 10, 5)
scene.add(spotLight)
dirLight = new THREE.DirectionalLight(0xffffff, 1)
dirLight.name = 'Dir light'
dirLight.position.set(0, 10, 0)
scene.add(dirLight)
}
function initMeshes() {
// torus
let geomery = new THREE.TorusKnotGeometry(25, 8, 200, 20)
let material = new THREE.MeshPhongMaterial({
color: 0xff0000,
shininess: 150,
specular: 0x222222,
})
torusKnot = new THREE.Mesh(geomery, material)
torusKnot.scale.multiplyScalar(1 / 18)
torusKnot.position.y = 3
scene.add(torusKnot)
// cube
geomery = new THREE.BoxGeometry(3, 3, 3)
cube = new THREE.Mesh(geomery, material)
cube.position.set(9, 3, 9)
scene.add(cube)
// ground
geomery = new THREE.BoxGeometry(10, 0.15, 10)
material = new THREE.MeshPhongMaterial({
color: 0xa0adaf,
shininess: 150,
specular: 0x111111,
})
ground = new THREE.Mesh(geomery, material)
ground.scale.multiplyScalar(3)
scene.add(ground)
}
function enableShadow() {
renderer.shadowMap.enabled = true
renderer.shadowMap.type = THREE.BasicShadowMap
spotLight.castShadow = true
dirLight.castShadow = true
torusKnot.castShadow = true
torusKnot.receiveShadow = true
cube.castShadow = true
cube.receiveShadow = true
ground.castShadow = false
ground.receiveShadow = true
}
function initCameraHelper() {
// 近裁剪面的距离
spotLight.shadow.camera.near = 8
// 远裁剪面的距离
spotLight.shadow.camera.far = 30
// 阴影贴图的大小
spotLight.shadow.mapSize.width = 1024
spotLight.shadow.mapSize.height = 1024
scene.add(new THREE.CameraHelper(spotLight.shadow.camera))
dirLight.shadow.camera.near = 1
dirLight.shadow.camera.far = 10
// 边界
dirLight.shadow.camera.right = 15
dirLight.shadow.camera.left = -15
dirLight.shadow.camera.top = 15
dirLight.shadow.camera.bottom = -15
scene.add(new THREE.CameraHelper(dirLight.shadow.camera))
}
function initShadowMapViewer() {
dirLightShadowMapViewer = new ShadowMapViewer(dirLight)
spotLightShadowMapViewer = new ShadowMapViewer(spotLight)
resizeShadowMapViewer()
}
function resizeShadowMapViewer() {
const size = width * 0.15
dirLightShadowMapViewer.position.x = 10
dirLightShadowMapViewer.position.y = 10
dirLightShadowMapViewer.size.width = size
dirLightShadowMapViewer.size.height = size
dirLightShadowMapViewer.update()
// 使用.set() 会自动调用.update()
spotLightShadowMapViewer.size.set(size, size)
spotLightShadowMapViewer.position.set(size + 20, 10)
}
function render() {
requestAnimationFrame(render)
const delta = clock.getDelta()
controls.update()
renderer.render(scene, camera)
torusKnot.rotation.x += 0.25 * delta
torusKnot.rotation.y += 2 * delta
torusKnot.rotation.z += 1 * delta
cube.rotation.x += 0.25 * delta
cube.rotation.y += 2 * delta
cube.rotation.z += 1 * delta
dirLightShadowMapViewer.render(renderer)
spotLightShadowMapViewer.render(renderer)
}
</script>
<style scoped>
#threeId {
width: 100%;
height: 100%;
}
</style>