threejs太阳光与阴影
Mool 人气:1前言
这篇文章实现智慧城市中模拟太阳光随时间变化产生对应场景效果。为了场景能够更逼真,我们一般会通过对接天气以及阳光等各种环境因素同步到场景中,使得场景能够更贴近现实。比如一些常见的天气系统,下雨、下雪、阴天、雾霾等,我之后会独立一篇文章中提现。这边文章主要介绍一系列灯光,主要是平行光对于太阳的模仿,以及一些材质的问题~
灯光与材质基础篇
常见的灯光:
- 点光源 (点光源可以理解为一个同时向四面八方散发光线,我们通常用来模拟灯泡,可以产生阴影)
- 平行光 (平行光可以想象成一个从无限远照射来的光束,通常用来模拟太阳光,可以产生阴影)
- 聚光灯 (聚光灯字面意思就是类似舞台灯光一样,照射突出特定圆弧形范围,可以产生阴影)
- 环境光 (一般用于改变整体场景的亮度,也是最常用的光源之一)
这里提一嘴材质:(仅仅列举常用的)
- 网格基础材质(MeshBasicMaterial,不支持阴影)
- FBR材质
- 物理标准材质(MeshStandardMaterial)
- MeshPhysicalMaterial
- 以上两者FBR材质相对于高光网格材质效果更好
- MeshPhongMaterial(高光网格材质,高亮表面、镜面反射)
- MeshLambertMaterial(网格Lambert材质,暗淡,漫反射)
这里简单做一下介绍,不懂的同学可以具体去了解某个材质
太阳光
添加平行光-----从东至西调整位置-----调整亮度以及颜色-----添加过渡模拟太阳光
接下来介绍本文的重点,如何模拟太阳光照的变化。其实原理非常简单,就是添加平行光,调整场景模型的阴影关系,根据时间实时变化平行光的位置以及光照强度以及颜色即可模拟~
整体调用代码
由于是一个demo,所以注重效果,一切从简实现功能
sun() { //两秒变化一次平行光 let i=0 setInterval(()=>{ this.initSun(i) i++ },2000) }
简单实现通过定时器以及提前写好对应位置光照的信息。主要是思想,酌情根据自己的需求可以改变~
这里这么写主要是实现效果,真实的应该根据系统时间将太阳光做出调整,包括根据天气原因,换汤不换药,主要还是
手动调整并存储为json通过传入时间以及天气去做出转化~
Viewer.prototype.initSun = function (type) { let position = {} let color = '#ffffff' let intensity = 1 switch (type) { case 0: position = { x: 270, y: 150, z: 0 } intensity = 5 break case 1: position = { x: 258, y: 170, z: 0 } intensity = 7 color = '#fcffc9' break case 2: position = { x: 245, y: 180, z: 0 } intensity = 10 color = '#ffe69f' break case 3: position = { x: 0, y: 100, z: 0 } intensity = 15 color = '#ffe69f' break case 4: position = { x: -245, y: 180, z: 0 } intensity = 10 color = '#e3894d' break case 5: position = { x: -258, y: 160, z: 0 } intensity = 10 color = '#ff8400' break default : position = { x: -270, y: 150, z: 0 } intensity = 8 color = '#ff8400' break } if (this.directionalLight) { this.directionalLight.setSun(position,color,intensity) } else { this.directionalLight = new zhdSun() this.directionalLight.renderFn(this.renderFunction) this.directionalLight.init({ position, color, intensity, scene: this.scene, currentlayers: this.currentlayers }) } }
太阳光类
这里主要对太阳光类的拆解与分析,封装的比较粗糙,酌情个人可以优化
import TWEEN from '@tweenjs/tween.js' import {zhdObject} from './zhdObject' export class zhdSun extends zhdObject { constructor() { super() this.light = null } } //由于添加了TWEEN动画库,记得在animate中实时更新TWEEN TWEEN.update()
初始化
这里做的是向场景中添加平行光,设置其阴影的范围以及距离等属性,因为我这边涉及层级,所以设置了平行光的层级
平行光可谓是所有灯光中阴影调整最麻烦的,想要平行光能够产生对的阴影效果,模型的产生阴影以及接收阴影要调整好,并且平行光的照射范围也要调整好。我效果图中不知大家有没有发现,在正午时刻的时候太阳光照射地面产生了一个长方形的范围阴影,这里是特地录制一个相对不那么完美的版本。
产生原因:平行光范围太小,但是一旦你调整平行光范围过大,由于地面是通过多个瓦片加载的,就会出现条纹状的阴影
如下图
解决方法:调整平行光阴影的bias属性,有助于减少阴影中的伪影
init({position, color, intensity , currentlayers, scene}) { const directionalLight = new THREE.DirectionalLight(color, intensity) // 新建一个平行光源,颜色未白色,强度为1 this.light = directionalLight directionalLight.position.set(position.x, position.y, position.z) // 将此平行光源调整到一个合适的位置 directionalLight.castShadow = true // 将此平行光源产生阴影的属性打开 // 设置平行光的的阴影属性,即一个长方体的长宽高,在设定值的范围内的物体才会产生阴影 const d =100 //阴影范围 directionalLight.shadow.camera.left = -d directionalLight.shadow.camera.right = d directionalLight.shadow.camera.top = d directionalLight.shadow.camera.bottom = -d directionalLight.shadow.camera.near = 20 directionalLight.shadow.camera.far = 8000 directionalLight.shadow.mapSize.x = 2048 // 定义阴影贴图的宽度和高度,必须为2的整数此幂 directionalLight.shadow.mapSize.y = 2048 // 较高的值会以计算时间为代价提供更好的阴影质量 directionalLight.shadow.bias = -0.0005 //解决条纹阴影的出现 this.setlayers(directionalLight, currentlayers) scene.add(directionalLight) // 将此平行光源加入场景中,我们才可以看到这个光源 return directionalLight }
设置平行光信息
设置平行光的信息:包括位置、颜色、强度
setSun(position, color, intensity) { this.setTweens(this.light.position, position, 2000) this.light.color = new THREE.Color( color ) this.light.intensity = intensity }
Tween
这里简单介绍TWEEN不懂的可以去看我之前的文章,主要是一个动画库,这里做简单的封装
setTweens(obj, newObj, duration = 1500) { var ro = new TWEEN.Tween(obj) ro.to(newObj, duration) // 变化后的位置以及动画时间 ro.easing(TWEEN.Easing.Sinusoidal.InOut) ro.onUpdate(function () { }) ro.start() }
总结
加载全部内容