灯光与阴影
1.灯光与阴影的关系与设置
首先我们先来了解哪些光可以产生阴影:
AmbientLight 环境光: 是不能投射阴影的因为它没有方向。
RectAreaLight 平面光光源: 像是光从窗户投射进来,是不能投射阴影的。
DirectionalLight 平行光: 可以投射阴影。
SpotLight 聚光灯: 可以投射阴影
PointLight 点光源: 可以投射阴影。效果会类似于聚光灯,只不过它是个“全方位”的聚光灯。
接下来了解哪些材质支持光源:
MeshBasicMaterial 基础网格材质: 不受光照影响。
MeshStandardMaterial 标准网格材质: 基于物理的渲染(PBR) ,能够产生阴影。效果比下面两个更逼真,但性能差些。它的纹理贴图、属性等基本都包含了下面两种,因此平时基本上都是使用这个即可。
MeshLambertMaterial Lambert网格材质: 这是一种非光泽表面的材质,没有镜面高光。(一般用于木材,石材等)
MeshPhongMaterial Phong网格材质: 一种用于具有镜面高光的光泽表面的材质。
MeshPhysicalMaterial 物理网格材质: 标准网格材质的扩展,提供了更高级的基于物理的渲染属性。效果就会更逼真,当然消耗更多性能。
MeshToonMaterial 卡通材质: 也是基于光照的。
ok,了解了一些后,来动手试试。接下来五步,一步都不可以少。
1.材质要满足能够对光照有反应
首先画出一个球体在中心,下面铺上一张“地毯”好让我们可以观察阴影。这里记得需要使用可以支持光照。还要设置光源。
//添加物体
//导入物体
const sphereGeometry = new THREE.SphereGeometry(1, 20, 20)
const material = new THREE.MeshStandardMaterial()
const sphere = new THREE.Mesh(sphereGeometry, material)
scene.add(sphere)
//添加平面
const planeGeometry = new THREE.PlaneBufferGeometry(10, 10)
const plane = new THREE.Mesh(planeGeometry, material)
plane.position.set(0, -1, 0) //设置向下移动1个单位
plane.rotation.x = -Math.PI / 2 //负的90度旋转,就是这个平面躺下,正面朝上,因为没有设置两面渲染,所以只有正面显示
scene.add(plane)
//灯光
//环境光
const light = new THREE.AmbientLight(0xffffff, 0.1) //颜色,强度
scene.add(light)
//直线光(平行光)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.position.set(10, 10, 10)
scene.add(directionalLight)
2.设置渲染器开启阴影的计算 renderer.shadowMap.enabled = true
//初始化渲染器
const renderer = new THREE.WebGLRenderer()
//设置渲染尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight)
//开启场景中的阴影贴图
renderer.shadowMap.enabled = true
3.设置光照投射阴影 directionalLight.castShadow = true
//直线光(平行光)
const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
directionalLight.castShadow = true
4.设置物体投射阴影 sphere.castShadow = true
让我们的小球能够进行阴影的投射
const sphere = new THREE.Mesh(sphereGeometry, material)
//投射阴影
sphere.castShadow = true
5.设置物体接收阴影 plane.receiveShadow = true
在上面我们不仅创建了一个小球,还创建了一个平面,这里我们需要设置让平面允许接收阴影。
const plane = new THREE.Mesh(planeGeometry, material)
//接收阴影
plane.receiveShadow = true
2.平行光阴影属性与阴影相机原理
1.平行光的阴影的模糊度属性
将此值设置为大于1的值将模糊阴影的边缘。
较高的值会在阴影中产生不必要的条带效果-更大的mapSize将允许在这些效果变得可见之前使用更高的值。
mapSize默认是512*512
//设置阴影贴图模糊度
directionalLight.shadow.radius = 20
//设置阴影贴图分辨率
// directionalLight.shadow.mapSize.set(2048, 2048)
directionalLight.shadow.mapSize.set(4096, 4096)
2.平行光的阴影的相机属性
directionalLight.shadow.camera.far 投影远点 (离相机远)
directionalLight.shadow.camera.near 投影近点 (离相机近的点)
directionalLight.shadow.camera.top 投影上边界
directionalLight.shadow.camera.bottom 投影下边界
directionalLight.shadow.camera.left 投影左边界
directionalLight.shadow.camera.right 投影右边界
我们想象在光源的地方有一个相机,相机的拍摄范围是一个立方体,立方体有6个面,正对应了上面的6个属性。而投影近点可以说就是光源,我们可以把它的数值调大一些,那么光源就会离相机远,也就是离物体近,那么由近大远小的原理,我们知道如果光源太接近物体的话 那么影子就会变小了。
//设置平行光投射相机的属性
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
我们可以设置一个gui来调整投影近点看看效果。
//初始化GUI界面
const gui = new dat.GUI()
gui.add(directionalLight.shadow.camera, "near").min(0).max(10).step(0.1).onChange(() => {
//每次更改相机属性,一定要调用更新投影矩阵的方法 updateProjectionMatrix()
directionalLight.shadow.camera.updateProjectionMatrix()
})