<script setup>
// 导入three.js
import * as THREE from 'three'
// 导入轨道控制器
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 导入gui
import * as dat from 'dat.gui'
/**
* 1. 创建场景
**/
const scene = new THREE.Scene()
/**
* 2. 创建透视相机(近大远小,相机只渲染场景内的东西)
**/
const camera = new THREE.PerspectiveCamera(
45, // 视角
window.innerWidth / window.innerHeight, // 视椎体长宽比
0.1, // 近端面
1000 // 远端面
)
camera.position.set(0, 0, 10)
camera.lookAt(0, 0, 0)
/**
* 3. 创建渲染器
**/
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement) // 在body上添加渲染器,domElement指向canvas
/*
* 4. 创建几何体
*/
/*
* 5. 坐标轴
*/
const axesHelper = new THREE.AxesHelper(5) // 坐标轴线段长度
scene.add(axesHelper)
/*
6. 控制器(使相机围绕目标运动)
*/
const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true // 添加轨道阻尼效果
/*
* 7. 渲染
*/
function animate () {
controls.update()
requestAnimationFrame(animate)
// cube.rotation.x += 0.01
// cube.rotation.y += 0.01
renderer.render(scene, camera)
}
animate()
</script>
/*
* 4. 创建几何体
*/
// MeshStandardMaterial
const material = new THREE.MeshStandardMaterial()
material.roughness = 0.4
// 球体
const sphere = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 32, 32),
material
)
sphere.position.x = -1.5
// cube
const cube = new THREE.Mesh(
new THREE.BoxGeometry(0.75, 0.75, 0.75),
material
)
// 环
const torus = new THREE.Mesh(
new THREE.TorusGeometry(0.3, 0.2, 32, 64),
material
)
torus.position.x = 1.5
// 平面
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(5, 5),
material
)
plane.rotation.x = - Math.PI * 0.5
plane.position.y = - 0.65
scene.add(sphere, cube, torus, plane)
/*
* 光
*/
// 环境光
const light = new THREE.AmbientLight(0xffffff, 0.5) // soft white light
scene.add(light)
-
添加平行光及阴影(在以上基础场景、物体及环境光的代码上找到对应位置进行补充),使用 OrthographicCamera 正交相机 来计算阴影
-
灯光阴影的条件:
- 材质需要对光照有反应
- 设置渲染器开启阴影的计算
renderer.shadowMap.enabled = true
- 设置光照投射阴影
directionalLight.castShadow = true
- 设置物体投射阴影
sphere.castShadow = true
- 设置物体接收阴影
plane.receiveShadow = true
(这里是平面接收阴影)
- CameraHelper平行光投射相机
/*
* 光
*/
// 平行光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5)
directionalLight.position.set(10, 10, 10) // 平行光是无限远的,这里的position指的是光的方向
scene.add(directionalLight)
// CameraHelper 模拟平行光投射相机视椎体
const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera)
scene.add(directionalLightCameraHelper)
/**
* 3. 创建渲染器
**/
...
...
renderer.shadowMap.enabled = true // 开启场景中的阴影贴图
/*
* 4. 创建几何体
*/
...
...
sphere.castShadow = true // sphere 投射阴影
// 平面接收阴影
...
...
plane.receiveShadow = true
// 平行光源投射阴影
directionalLight.castShadow = true
// 阴影边缘模糊度
directionalLight.shadow.radius = 20
// 阴影贴图分辨率
directionalLight.shadow.mapSize.set(2048, 2048)
// 平行光投射相机的属性
directionalLight.shadow.camera.near = 0.5
directionalLight.shadow.camera.far = 500
directionalLight.shadow.camera.top = 5
directionalLight.shadow.camera.bottom = -5
directionalLight.shadow.camera.left = -5
directionalLight.shadow.camera.right = 5
-
添加聚光灯及阴影(在以上基础场景、物体及环境光的代码上找到对应位置进行补充),like a falshlight,to rotate the SpotLight, we need to add its target property to the scene and then move it,使用 PerspectiveCamera 透视相机 计算阴影
- color
- intensity
- distance
- angle
- pernumbra
- decay
/*
* 光
*/
// 聚光灯
const spotLight = new THREE.SpotLight(0x78ff00, 0.5, 10, Math.PI * 0.1, 0.25, 1)
spotLight.position.set(0, 2, 3)
spotLight.castShadow = true
spotLight.target = sphere // Object3D,一个物体,聚光灯目标,移动物体时,灯光跟随物体
spotLight.angle = Math.PI / 6 // 聚光灯角度,一般不超过90°
spotLight.distance = 5 // 聚光灯光源发出的距离
spotLight.penumbra = 0.5 // 聚光灯半影衰减,数值越接近1时,灯光边缘越模糊
// 阴影边缘模糊度
spotLight.shadow.radius = 20
// 阴影贴图分辨率
spotLight.shadow.mapSize.set(4096, 4096)
// console.log(spotLight)
scene.add(spotLight)
// console.log(spotLight.target)
scene.add(spotLight.target)
spotLight.target.position.x = -0.75
-
添加点光源及阴影(在以上基础场景、物体及环境光的代码上找到对应位置进行补充)
- 这里添加了一个
smallball
用于展示点光源的位置
const smallBall = new THREE.Mesh(
new THREE.SphereGeometry(0.1, 20, 20),
new THREE.MeshBasicMaterial({
color: 0xff0000
})
)
smallBall.position.set(2, 2, 2)
// 点光源
const pointLight = new THREE.PointLight(0xff0000, 2)
// pointLight.position.set(2, 2, 2);
pointLight.castShadow = true
// 阴影边缘模糊度
pointLight.shadow.radius = 20
// 阴影贴图分辨率
pointLight.shadow.mapSize.set(512, 4096)
smallBall.add(pointLight)
scene.add(smallBall)
-
半球光 HemisphereLight,这一步可去掉
AmbientLight
观察,如下代码只保留directionalLight
,半球光需要设置天空光线颜色和地面光线颜色,在物体的中间部分会呈现两种光线颜色的混合与渐变(当前代码的呈现结果是物体顶面是红色、地面是蓝色)
- color (or skyColor)
- groundColor
- intensity
// DirectionalLight
const directionalLight = new THREE.DirectionalLight(0x00fffc, 0.3)
directionalLight.position.set(1, 0.25, 0)
scene.add(directionalLight)
// HemisphereLight
const hemisphereLight = new THREE.HemisphereLight(0xff0000, 0x0000ff, 0.3)
scene.add(hemisphereLight)
-
平面光源 RectAreaLight,works like a big rectangle,这里可以只保留平面光源进行观察,环境光也不需要;the
RectAreaLight
only works with MeshStandardMaterial
and MeshPhysicalMaterial
- color
- intensity
- width
- height
// RectAreaLight
const rectAreaLight = new THREE.RectAreaLight(0x4e00ff, 2, 1, 1)
rectAreaLight.position.set(-1.5, 0, 1.5)
rectAreaLight.lookAt(new THREE.Vector3())
scene.add(rectAreaLight)
-
Cost
-
minimal Cost: AmbientLight、HemisphereLight
-
moderate Cost:DirectionalLight、PointLight
-
Hight Cost:SpotLight、RectAreaLight
-
Helper,模拟场景内灯光的辅助对象,效果如下图
// Helpers
const hemisphereLightHelper = new THREE.HemisphereLightHelper(hemisphereLight, 0.2)
scene.add(hemisphereLightHelper)
const directionalLightHelper = new THREE.DirectionalLightHelper(directionalLight, 0.2)
scene.add(directionalLightHelper)
const pointLightHelper = new THREE.PointLightHelper(pointLight, 0.2)
scene.add(pointLightHelper)
const spotLightHelper = new THREE.SpotLightHelper(spotLight) // SpotLightHelper have no size
scene.add(spotLightHelper)
// RectAreaLightHelper 是一个附加组件,需要显示导入(three.js版本:0.155.0)
import { RectAreaLightHelper } from 'three/addons/helpers/RectAreaLightHelper.js';
const rectAreaLightHelper = new RectAreaLightHelper(rectAreaLight)
scene.add(rectAreaLightHelper)