threejs太阳光与阴影效果实例代码
作者:Mool
前言
这篇文章实现智慧城市中模拟太阳光随时间变化产生对应场景效果。为了场景能够更逼真,我们一般会通过对接天气以及阳光等各种环境因素同步到场景中,使得场景能够更贴近现实。比如一些常见的天气系统,下雨、下雪、阴天、雾霾等,我之后会独立一篇文章中提现。这边文章主要介绍一系列灯光,主要是平行光对于太阳的模仿,以及一些材质的问题~
灯光与材质基础篇
常见的灯光:
- 点光源 (点光源可以理解为一个同时向四面八方散发光线,我们通常用来模拟灯泡,可以产生阴影)
- 平行光 (平行光可以想象成一个从无限远照射来的光束,通常用来模拟太阳光,可以产生阴影)
- 聚光灯 (聚光灯字面意思就是类似舞台灯光一样,照射突出特定圆弧形范围,可以产生阴影)
- 环境光 (一般用于改变整体场景的亮度,也是最常用的光源之一)
这里提一嘴材质:(仅仅列举常用的)
- 网格基础材质(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() }
总结
到此这篇关于threejs太阳光与阴影效果的文章就介绍到这了,更多相关threejs太阳光与阴影内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!