threejs 光带扩散动画

2024-01-11 01:04
文章标签 动画 扩散 threejs 光带

本文主要是介绍threejs 光带扩散动画,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、创建光带

(1) 设置光带顶点

(2)  设置光带顶点透明度属性

二、光带动画

完整代码

html文件代码

 js文件代码

最后展示一下项目里的效果:


最近项目中要求做一段光带效果动画,尝试着写了一下,下面是本次分享光带扩散动画的效果预览:

20240110_204035

一、创建光带

(1) 设置光带顶点

这里使用缓冲区几何体bufferGeometry,通过设置顶点属性position来构成光带模型,在创建顶点之前需要一下几个必备参数:

r光带初始时的半径
h光带的高度
radian弧度值
segment间隔段数,光带由N段矩形构成(矩形由2个三角形构建),此属性决定矩形数量,值越大光带越接近圆形
interval每段间隔的弧度值
// 创建缓冲区几何体
const geometry = new THREE.BufferGeometry();
// 光带初始半径
const r = 10;
// 光带高度
const h = 10;
// 弧度
let radian = 0;
// 间隔段数,此值越高光带棱角越分明
const segment = 50;
// 弧度间隔
const interval = (Math.PI * 2) / segment;

接下来就是创建光带的顶点位置数组了,光带由N个矩形组成,一个矩形又由两个三角形构成;

for循环遍历间隔段数segment,每3个值代表一个顶点位置,3个顶点位置又组成一个三角形;

x轴上的位置使用Math.cos函数得出,z轴上的位置使用Math.sin函数得出,y轴则看三角形的三个点创建顺序来得出。此处我创建点位时,三角形底下的点为点2,所以点2的y值设置为0

第一个三角形点位顺序(第二个三角形类推,这里不展示了):

最后通过bufferAttribute属性设置几何体顶点位置,注意顶点位置数组需要转换成32位浮点类型的数组

// 顶点位置数组
const vertexPosArr = [];
// 遍历出光带的顶点数据
for (let i = 0; i < segment; i++) {// 弧度逐渐增加,从0度增加到360度radian += interval;// 计算出两个三角形的顶点位置,形成一个矩形平面,最后多个矩形平面组成圆形的光带vertexPosArr.push(// 第一个三角形Math.cos(radian) * r, h, Math.sin(radian) * r, // 点1Math.cos(radian) * r, 0, Math.sin(radian) * r, // 点2Math.cos(radian + interval) * r, 0, Math.sin(radian + interval) * r,  // 点3// 第二个三角形Math.cos(radian) * r, h, Math.sin(radian) * r, // 点1Math.cos(radian + interval) * r, 0, Math.sin(radian + interval) * r,  // 点2Math.cos(radian + interval) * r, h, Math.sin(radian + interval) * r,  // 点3)
}// 设置几何体缓冲区position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(vertexPosArr), 3);

(2)  设置光带顶点透明度属性

光带是渐变透明的,由黑到白(效果中蓝色是因为材质设置了蓝色将白色替换了);

通过获取顶点的getY函数获取当前顶点的y值(也就是顶点的高度),(1-顶点高度) / 光带高度使光带从下往上逐渐透明,也可以换成顶点高度 / 光带高度使光带从上往下逐渐透明

// 设置几何体缓冲区position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(vertexPosArr), 3);
// 获取顶点
const position = geometry.attributes.position;
// 顶点总数量
const count = position.count;
// 透明度数组,每个顶点位置将会对应一个透明度
const alphaArr = [];
// 根据高度设置顶点透明度
for (let i = 0; i < count; i++) {alphaArr.push((1 - position.getY(i) / h));
}
// 设置几何体缓冲区alpha属性
geometry.attributes.alpha = new THREE.BufferAttribute(new Float32Array(alphaArr), 1);

(3)  创建光带材质

这里使用的普通网格材质,这里必须设置side属性为THREE.DoubleSide双面可见、材质透明度transparent属性开启

至于材质使用onBeforeCompile函数替换着色器shader代码一块这里不做说明了,因为这一块东西很多,一时也说不清楚。

// 创建光带的材质
const material = new THREE.MeshBasicMaterial({color: '#00ffff',side: THREE.DoubleSide,transparent: true,depthTest: false,
})
// 材质渲染前所执行,替换shader着色器代码
material.onBeforeCompile = (shader) => {shader.vertexShader = shader.vertexShader.replace('void main() {',`// 引进透明度分量attribute float alpha; // varying声明一个属性,赋值透明度分量alpha,让片元着色器能拿到这个属性varying float vAlpha;void main() {vAlpha = alpha;`,)shader.fragmentShader = shader.fragmentShader.replace('void main() {',`// 引进从顶点着色器传递的透明度分量varying float vAlpha;void main() {`,)shader.fragmentShader = shader.fragmentShader.replace('#include <output_fragment>',`#include <output_fragment>// 设置颜色和透明度值,让光带有一个渐变效果gl_FragColor = vec4( outgoingLight, vAlpha  );`,)
}
const lightBand = new THREE.Mesh(geometry, material);
scene.add(lightBand);

二、光带动画

这里的光带动画是写在循环执行函数内的,有过threejs基础一定不陌生;

这里每次使用clone属性克隆光带模型获取scale的x值(换成z值也一样,y值不可以),通过if判断光带当前缩放大小来决定相应操作;

这里光带将从1倍扩散到9倍,7倍到9倍的时候会逐渐减小光带高度,这有就又了光带扩散动画末尾的逐渐消失效果,最后超过9倍重置缩放倍数为1,形成循环;

// 渲染循环
function render () {// 光带当前缩放倍数let scale = lightBand.clone().scale.x;// 小于7时scale不断增加if (scale < 7) {scale += 0.02;// 重新设置光带缩放倍数lightBand.scale.set(scale, 1, scale);} // 小于8时scale不断增加,但是光带高度逐渐减小else if (scale < 9) {scale += 0.02;lightBand.scale.set(scale, (9 - scale) / 2, scale);} // 大于9时光带缩放倍数重置为1else {scale = 0;lightBand.scale.set(scale, scale, scale);}renderer.render(scene, camera);requestAnimationFrame(render);
}
render();

完整代码

这里使用的html+js构建的小案例,threejs使用的148的版本

html文件代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<style>body {overflow: hidden;margin: 0;}
</style><body><div id="webgl"></div><script type="importmap">{"imports":{"three":"../../build/three.module.js","three/addons/": "../../examples/jsm/"}}</script><script src="./index.js" type="module"></script>
</body></html>

 js文件代码

import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';const width = window.innerWidth;
const height = window.innerHeight;// 创建场景
const scene = new THREE.Scene();// 设置光源
const pointLight = new THREE.PointLight('#ffffff', 1, 0);
pointLight.position.set(200, 0, 200);
scene.add(pointLight);
const ambientLight = new THREE.AmbientLight(0xffffff, 0.2);
scene.add(ambientLight);// 创建透视相机
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(0, 0, 100);
camera.lookAt(0, 0, 0);// 创建渲染器
const renderer = new THREE.WebGLRenderer({antialias: true,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);const planeGeometry = new THREE.PlaneGeometry(200, 200);
const planeMaterial = new THREE.MeshBasicMaterial({ color: '#696969' });
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotateX(-Math.PI / 2);
scene.add(plane);// 创建缓冲区几何体
const geometry = new THREE.BufferGeometry();
// 光带初始半径
const r = 10;
// 光带高度
const h = 10;
// 弧度
let radian = 0;
// 间隔段数,此值越高光带棱角越分明
const segment = 50;
// 弧度间隔
const interval = (Math.PI * 2) / segment;
// 顶点位置数组
const vertexPosArr = [];
// 遍历出光带的顶点数据
for (let i = 0; i < segment; i++) {// 弧度逐渐增加,从0度增加到360度radian += interval;// 计算出两个三角形的顶点位置,形成一个矩形平面,最后多个矩形平面组成圆形的光带vertexPosArr.push(// 第一个三角形Math.cos(radian) * r, h, Math.sin(radian) * r, // 点1Math.cos(radian) * r, 0, Math.sin(radian) * r, // 点2Math.cos(radian + interval) * r, 0, Math.sin(radian + interval) * r,  // 点3// 第二个三角形Math.cos(radian) * r, h, Math.sin(radian) * r, // 点1Math.cos(radian + interval) * r, 0, Math.sin(radian + interval) * r,  // 点2Math.cos(radian + interval) * r, h, Math.sin(radian + interval) * r,  // 点3)
}// 设置几何体缓冲区position属性
geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(vertexPosArr), 3);
// 获取顶点数
const position = geometry.attributes.position;
// 顶点总数量
const count = position.count;
// 透明度数组,每个顶点位置将会对应一个透明度
const alphaArr = [];
// 根据高度设置顶点透明度
for (let i = 0; i < count; i++) {const temp = 1 - position.getY(i) / h;alphaArr.push(temp);console.log(temp, position.getY(i))
}
// 设置几何体缓冲区alpha属性
geometry.attributes.alpha = new THREE.BufferAttribute(new Float32Array(alphaArr), 1);
// 创建光带的材质
const material = new THREE.MeshBasicMaterial({color: '#00ffff',side: THREE.DoubleSide,transparent: true,depthTest: false,
})
// 材质渲染前所执行,替换shader着色器代码
material.onBeforeCompile = (shader) => {shader.vertexShader = shader.vertexShader.replace('void main() {',`// 引进透明度分量attribute float alpha; // varying声明一个属性,赋值透明度分量alpha,让片元着色器能拿到这个属性varying float vAlpha;void main() {vAlpha = alpha;`,)shader.fragmentShader = shader.fragmentShader.replace('void main() {',`// 引进从顶点着色器传递的透明度分量varying float vAlpha;void main() {`,)shader.fragmentShader = shader.fragmentShader.replace('#include <output_fragment>',`#include <output_fragment>// 设置颜色和透明度值,让光带有一个渐变效果gl_FragColor = vec4( outgoingLight, vAlpha  );`,)
}
const lightBand = new THREE.Mesh(geometry, material);
scene.add(lightBand);// 渲染循环
function render () {// 光带当前缩放倍数let scale = lightBand.clone().scale.x;// 小于7时scale不断增加if (scale < 7) {scale += 0.02;// 重新设置光带缩放倍数lightBand.scale.set(scale, 1, scale);}// 小于8时scale不断增加,但是光带高度逐渐减小else if (scale < 9) {scale += 0.02;lightBand.scale.set(scale, (9 - scale) / 2, scale);}// 大于9时光带缩放倍数重置为1else {scale = 0;lightBand.scale.set(scale, scale, scale);}renderer.render(scene, camera);requestAnimationFrame(render);
}
render();// 创建相机轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', () => {renderer.render(scene, camera);
})// 设置界面跟随窗口自适应
window.onresize = function () {renderer.setSize(window.innerWidth, window.innerHeight);camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();
}

最后展示一下项目里的效果:

案例中如有不足的请补充,不懂的也可以问我,我知道的会尽力解答

这篇关于threejs 光带扩散动画的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/592648

相关文章

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

用Unity2D制作一个人物,实现移动、跳起、人物静止和动起来时的动画:中(人物移动、跳起、静止动作)

上回我们学到创建一个地形和一个人物,今天我们实现一下人物实现移动和跳起,依次点击,我们准备创建一个C#文件 创建好我们点击进去,就会跳转到我们的Vision Studio,然后输入这些代码 using UnityEngine;public class Move : MonoBehaviour // 定义一个名为Move的类,继承自MonoBehaviour{private Rigidbo

动画AnimationDrawable、转动

现实开发中:很多地方都用到 点击动画的特效; 本案例本人做了三个关于“动” 画 的效果; 先上图: 总体图: A: B: 1:点击图片按钮,效果是:图片闪动; 通过在xml中定义:标签:animation-list来实现点击动画的效果;  是否循环标签:oneshot ;   时间间隔标签:duration ; 要显示的图片标签:drawable ;

13 transition数组的动画使用

划重点 动画:transitiontransition-group :数组动画数组的 添加 / 删除 豆腐粉丝汤 清淡又健康 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><me

12 动画transition的使用2

划重点 Vue 动画:transition / transform在动画周期中执行动动画(上一篇是通过动画样式控制动画) 清蒸扇贝 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><

【前端】animation动画以及利用vue制作简单的透明度改变动画,包含vue生命周期实现

一. 问题描述 想做一个文字透明度从1到0然后再从0到1的css动画。 二. 代码写法 2.1 animation写法 2.1.1 animation属性key 2.1.2 代码展示 <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=de