three.js学习(10)

159 阅读4分钟

灯光与阴影

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()
 })