包阅导读总结
1. 关键词: `threejs、风力发电车、可视化、着色器、动画`
2. 总结:
本文介绍了使用 threejs 开发的风力发电机物联可视化系统,涵盖着色器效果、多种动画、模型材质问题及相关代码,包括切割动画、自定义动画贝塞尔曲线、齿轮动画等,还提到了发光效果的实现及相关处理。
3. 主要内容:
– 风力发电机物联可视化系统
– 技术栈:three.js 0.165.0、vite 4.3.2、nodejs v18.19.0
– 效果图:一镜到底动画、切割动画、线稿动画等
– 代码及功能介绍
– 着色器:创建顶点和片元着色器,通过动态修改中心位置实现动态光效
– 切割动画:使用数学库平面和属性 constant,通过修改其值实现动画
– 模型材质问题:处理透明和不透明材质渲染时的深度问题
– 自定义动画贝塞尔曲线:可自行设置动画曲线控制动画执行
– 齿轮:包含自带动画、展开的补间动画、发光效果
– 自带动画通过 HandleAnimation 类控制
– 补间动画使用变换控制器定位模型位置
– 发光效果通过 threejs 相关通道和合成器实现,并处理材质切换
思维导图:
文章地址:https://juejin.cn/post/7379906492038889512
文章来源:juejin.cn
作者:孙_华鹏
发布时间:2024/6/14 5:59
语言:中文
总字数:3068字
预计阅读时间:13分钟
评分:81分
标签:前端,three.js,数据可视化,动画,着色器
以下为原文内容
本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com
本文使用threejs开发一款风力发电机物联可视化系统,包含着色器效果、动画、补间动画和开发过程中使用模型材质遇到的问题,内含大量gif效果图,
视频讲解及源码见文末
技术栈
- three.js 0.165.0
- vite 4.3.2
- nodejs v18.19.0
效果图
一镜到底动画
切割动画
线稿动画
外壳透明度动画
展开齿轮动画
发光线条动画
代码及功能介绍
着色器
文中用到一个着色器,就是给模型增加光感的动态光影
创建顶点着色器 vertexShader:
varying vec2 vUv;void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}
创建片元着色器 vertexShader:
varying vec2 vUv;uniform vec2 u_center; // 添加这一行 void main() { // 泡泡颜色 vec3 bubbleColor = vec3(0.9, 0.9, 0.9); // 乳白色 // 泡泡中心位置 vec2 center = u_center; // 计算当前像素到泡泡中心的距离 float distanceToCenter = distance(vUv, center); // 计算透明度,可以根据实际需要调整 float alpha = smoothstep(0.1, 0.0, distanceToCenter); gl_FragColor = vec4(bubbleColor, alpha);
创建着色器材质 bubbleMaterial
export const bubbleMaterial = new THREE.ShaderMaterial({ vertexShader: vertexShader, fragmentShader: fragmentShader, transparent: true, depthTest: true, depthWrite: false, uniforms: { u_center: { value: new THREE.Vector2(0.3, 0.3) } },});
从代码中可以看到 uniform
声明了一个变量u_center
,目的是为了在render方法中动态修改中心位置,从而实现动态光效的效果,
具体引用 render 方法中
let t = performance.now() * 0.001; bubbleMaterial.uniforms.u_center.value.x = Math.sin(t) * 0.5 + 0.5; bubbleMaterial.uniforms.u_center.value.y = Math.cos(t) * 0.5 + 0.5;
官网案例 # Uniform,详细介绍了uniform的使用方法,支持通过变量对着色器材质中的属性进行改变
从模型上可能看不出什么,下面的图是在一个圆球上加的这个效果
着色器中有几个参数可以自定义也可以自己修改, float alpha = smoothstep(0.6, 0.0, distanceToCenter);
中的smoothstep
是一个常用的函数,用于在两个值之间进行平滑插值。具体来说,smoothstep(edge0, edge1, x)
函数会计算x
在edge0
和edge1
之间的平滑过渡值。当x
小于edge0
时,返回值为 0;当x
大于edge1
时,返回值为 1;而当x
在edge0
和edge1
之间时,它返回一个在 0 和 1 之间的平滑过渡值。
切割动画
切割动画使用的是数学库平面THREE.Plane
和属性 constant
,通过修改constant值即可实现动画,从normal
法向量起至constant的距离为可展示内容。
从原点到平面的有符号距离。 默认值为0.
constant取模型的box3包围盒的min值,至max值做补间动画,以下是代码示意
const wind = windGltf.sceneconst boxInfo = wind.userData.box3Info;const max = boxInfo.worldPosition.z + boxInfo.max.zconst min = boxInfo.worldPosition.z + boxInfo.min.z let tween = new TWEEN.Tween({ d: min - 0.2 }) .to({ d: max + 0.1 }, 1000 * 2) .start() .onUpdate(({ d }) => { clippingPlane.constant = d })
详看切割效果图
图中添加了切割线的辅助线,可以通过右侧的操作面板显示或隐藏。
模型材质需要注意的问题
由于齿轮在风车的内容部,并且风车模型开启了transparent=true
,那么计算透明度深度就会出现问题,首先要设置 depthWrite = true
,开启深度缓存区,renderOrder = -1
,
这个值将使得scene graph(场景图)中默认的的渲染顺序被覆盖, 即使不透明对象和透明对象保持独立顺序。 渲染顺序是由低到高来排序的,默认值为0。
threejs的透明材质渲染和不透明材质渲染的时候,会互相影响,而调整renderOrder
顺序则可以让透明对象和不透明对象相对独立的渲染。
depthWrite
对比
renderOrder
对比
自定义动画贝塞尔曲线
众所周知,贝塞尔曲线通常用于调整关键帧动画,创建平滑的、曲线的运动路径。本文中使用的tweenjs就内置了众多的运动曲线easing(easingFunction?: EasingFunction): this;
类型,虽然有很多内置,但是毕竟需求是无限的,接下来介绍的方法就是可以自己设置动画的贝塞尔曲线,来控制动画的执行曲线。
具体使用
const controlPoints = [ { x: 0 }, { x: 0.5 }, { x: 2 }, { x: 1 }];const cubicBezier = new CubicBezier(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3]);let tween = new TWEEN.Tween(edgeLineGroup.scale).to(windGltf.scene.scale.clone().set(1, 1, 1), 1000 * 2).easing((t) => { return cubicBezier.get(t).x}).start().onComplete(() => { lineOpacityAction(0.3) res({ tween })})
在tween的easing
的回调中添加一个方法,方法中调用了cubicBezier
,下面就介绍一下这个方法
源码
[p0]– 起点 [p1]– 第一个控制点 [p2]– 第二个控制点 [p3] – 终点
export class CubicBezier { private p0: { x: number; }; private p1: { x: number; }; private p2: { x: number; }; private p3: { x: number; }; constructor(p0: { x: number; }, p1: { x: number; }, p2: { x: number; }, p3: { x: number; }) { this.p0 = p0; this.p1 = p1; this.p2 = p2; this.p3 = p3; } get(t: number): { x: number; } { const p0 = this.p0; const p1 = this.p1; const p2 = this.p2; const p3 = this.p3; const mt = 1 - t; const mt2 = mt * mt; const mt3 = mt2 * mt; const t2 = t * t; const t3 = t2 * t; const x = mt3 * p0.x + 3 * mt2 * t * p1.x + 3 * mt * t2 * p2.x + t3 * p3.x; return { x }; }}
类CubicBezier
支持get
方法,通过四个关键点位信息,绘制三次贝塞尔曲线,参数t
在0到1之间变化,当t
从0变化到1时,曲线上的点从p0
平滑地过渡到p3
。
mt = 1 - t;
:这是t
的补数(1减去t
)。mt2 = mt * mt;
和mt3 = mt2 * mt;
:计算mt
的平方和立方。t2 = t * t;
和t3 = t2 * t;
:计算t
的平方和立方。
这是通过取四个点的x
坐标的加权和来完成的,其中权重是基于t
的幂的。具体来说,p0
的权重是(1-t)^3
,p1
的权重是3 * (1-t)^2 * t
,p2
的权重是3 * (1-t) * t^2
,而p3
的权重是t^3
。
{ x: 0 },{ x: 0.5 },{ x: 2 },{ x: 1 }
这组数据形成的曲线效果是由start参数到end的两倍参数再到end参数
具体效果如下
齿轮
齿轮动画
模型中自带动画
源码中有一整套的动画播放类方法,HandleAnimation
,其中功能包含播放训话动画,切换动画,播放一次动画,绘制骨骼,镜头跟随等功能。
具体使用方法:
motorAnimation = new HandleAnimation(motorGltf.scene, motorGltf.animations) motorAnimation.play('Take 001')
在render中调用
motorAnimation && motorAnimation.upDate()
齿轮展开(补间动画)
补间动画在齿轮展开时调用,使用的tweenjs,这里讲一下定位运动后的模型位置,使用# 变换控制器(TransformControls),代码中有封装好的完整的使用方法,在TransformControls.ts
中,包含同时存在轨道控制器时与变换控制器对场景操作冲突时的处理。
使用方法:
TransformControls(mesh, ()=>{ console.log(mesh.position)})
齿轮发光
发光效果方法封装在utls/index.ts
中的unreal
方法,使用的是threejs提供的虚幻发光通道RenderPass
,UnrealBloomPass
,以及合成器EffectComposer
,方法接受参数如下
const createParams = { threshold: 0, strength: 0.972, radius: 0.21, exposure: 1.55 };
调用方法如下:
const { finalComposer: F, bloomComposer: B, renderScene: R, bloomPass: BP } = unreal(scene, camera, renderer, width, height, params) finalComposer = F bloomComposer = B renderScene = R bloomPass = BP bloomPass.threshold = 0
除了调用方法还有一些需要调整的地方,比如发光时模型什么材质,又或者不发光时又是什么材质,这里需要单独定义,并在render渲染函数中调用
if (guiParams.isLight) { if (bloomComposer) { scene.traverse(darkenNonBloomed.bind(this)); bloomComposer.render(); } if (finalComposer) { scene.traverse(restoreMaterial.bind(this)); finalComposer.render(); } }
在scene.traverse
的回调中,检验模型是否为发光体,再进行材质的更换,这里用的标识是 object.userData.isLight
为true
时,判定该物体为发光物体。其他物体则不发光
回调方法
function darkenNonBloomed(obj: THREE.Mesh) { if (bloomLayer) { if (!obj.userData.isLight && bloomLayer.test(obj.layers) === false) { materials[obj.uuid] = obj.material; obj.material = darkMaterial; } }}function restoreMaterial(obj: THREE.Mesh) { if (materials[obj.uuid]) { obj.material = materials[obj.uuid]; delete materials[obj.uuid]; }}
再场景的右上角我们新增了几个参数,用来调整线条的发光效果,下面通过动图看一下,图片有点大,请耐心等待加载
好啦,本篇文章到此,如看源码有不明白的地方,可私信~
最近正在筹备工具库,以上可视化常用的方法都将涵盖在里面
历史文章
three.js——商场楼宇室内导航系统 内附源码
three.js——可视化高级涡轮效果+警报效果 内附源码
高德地图巡航功能 内附源码
three.js——3d塔防游戏 内附源码
three.js+物理引擎——跨越障碍的汽车 可操作 可演示
百度地图——如何计算地球任意两点之间距离 内附源码
threejs——可视化地球可操作可定位
three.js 专栏
源码及讲解
源码 www.aspiringcode.com/content?id=…
体验地址:display.aspiringcode.com:8888/html/171422…