r3f喷雾火焰组件
import { useTexture } from "@react-three/drei";
import { useFrame, useThree } from "@react-three/fiber";
import React, { useRef } from "react";
import * as THREE from "three";
const _VS = `
uniform float pointMultiplier;
attribute float size;
attribute float angle;
attribute vec4 colour;
varying vec4 vColour;
varying vec2 vAngle;
void main() {
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_Position = projectionMatrix * mvPosition;
gl_PointSize = size * pointMultiplier / gl_Position.w;
vAngle = vec2(cos(angle), sin(angle));
vColour = colour;
}`;
const _FS = `
uniform sampler2D diffuseTexture;
varying vec4 vColour;
varying vec2 vAngle;
void main() {
vec2 coords = (gl_PointCoord - 0.5) * mat2(vAngle.x, vAngle.y, -vAngle.y, vAngle.x) + 0.5;
gl_FragColor = texture2D(diffuseTexture, coords) * vColour;
}`;
class LinearSpline {
constructor(lerp) {
this._points = [];
this._lerp = lerp;
}
AddPoint(t, d) {
this._points.push([t, d]);
}
Get(t) {
let p1 = 0;
for (let i = 0; i < this._points.length; i++) {
if (this._points[i][0] >= t) {
break;
}
p1 = i;
}
const p2 = Math.min(this._points.length - 1, p1 + 1);
if (p1 == p2) {
return this._points[p1][1];
}
return this._lerp(
(t - this._points[p1][0]) / (this._points[p2][0] - this._points[p1][0]),
this._points[p1][1],
this._points[p2][1]
);
}
}
function Fire({ color = "red", speed = 0.016 }) {
// let timeNum = 0;
const geometryRef = useRef(null);
const camear = useThree().camera;
let particles = [];
let gdfsghk;
let alphaSpline = new LinearSpline((t, a, b) => {
return a + t * (b - a);
});
alphaSpline.AddPoint(0.0, 0.0);
alphaSpline.AddPoint(0.1, 1.0);
alphaSpline.AddPoint(0.6, 1.0);
alphaSpline.AddPoint(1.0, 0.0);
let colourSpline = new LinearSpline((t, a, b) => {
const c = a.clone();
return c.lerp(b, t);
});
colourSpline.AddPoint(0.0, new THREE.Color(color));
colourSpline.AddPoint(0.0, new THREE.Color(color));
let sizeSpline = new LinearSpline((t, a, b) => {
return a + t * b;
});
sizeSpline.AddPoint(0.0, 1.0);
sizeSpline.AddPoint(0.5, 5.0);
sizeSpline.AddPoint(1.0, 1.0);
const fireImg = useTexture("/images/yun.png");
const uniforms = {
diffuseTexture: {
value: fireImg,
},
pointMultiplier: {
value:
window.innerHeight / (3.0 * Math.tan((0.5 * 60.0 * Math.PI) / 180.0)),
},
};
function addParticles(timeElapsed) {
if (!gdfsghk) {
gdfsghk = 0.0;
}
gdfsghk += timeElapsed;
const n = Math.floor(gdfsghk * 75.0);
gdfsghk -= n / 75.0;
for (let i = 0; i < n; i++) {
const life = Math.random() * 0.75 + 0.25;
particles.push({
position: new THREE.Vector3(
(Math.random() * 2 - 1) * 1.0,
(Math.random() * 2 - 1) * 1.0,
(Math.random() * 2 - 1) * 1.0
),
size: Math.random() * 0.5 + 0.5,
colour: new THREE.Color(),
alpha: 1.0,
life: life,
maxLife: life,
rotation: Math.random() * 2.0 * Math.PI,
velocity: new THREE.Vector3(0, -15, 0),
});
}
}
//更新形状
function updateGeometry() {
const positions = [];
const sizes = [];
const colours = [];
const angles = [];
for (let p of particles) {
positions.push(p.position.x, p.position.y, p.position.z);
colours.push(p.colour.r, p.colour.g, p.colour.b, p.alpha);
sizes.push(p.currentSize);
angles.push(p.rotation);
}
geometryRef.current.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);
geometryRef.current.setAttribute(
"size",
new THREE.Float32BufferAttribute(sizes, 1)
);
geometryRef.current.setAttribute(
"colour",
new THREE.Float32BufferAttribute(colours, 4)
);
geometryRef.current.setAttribute(
"angle",
new THREE.Float32BufferAttribute(angles, 1)
);
geometryRef.current.attributes.position.needsUpdate = true;
geometryRef.current.attributes.size.needsUpdate = true;
geometryRef.current.attributes.colour.needsUpdate = true;
geometryRef.current.attributes.angle.needsUpdate = true;
}
//更新粒子
function updateParticles(timeElapsed) {
for (let p of particles) {
p.life -= timeElapsed;
}
particles = particles.filter((p) => {
return p.life > 0.0;
});
for (let p of particles) {
const t = 1.0 - p.life / p.maxLife;
p.rotation += timeElapsed * 0.5;
p.alpha = alphaSpline.Get(t);
p.currentSize = p.size * sizeSpline.Get(t);
p.colour.copy(colourSpline.Get(t));
p.position.add(p.velocity.clone().multiplyScalar(timeElapsed));
const drag = p.velocity.clone();
drag.multiplyScalar(timeElapsed * 0.1);
drag.x =
Math.sign(p.velocity.x) *
Math.min(Math.abs(drag.x), Math.abs(p.velocity.x));
drag.y =
Math.sign(p.velocity.y) *
Math.min(Math.abs(drag.y), Math.abs(p.velocity.y));
drag.z =
Math.sign(p.velocity.z) *
Math.min(Math.abs(drag.z), Math.abs(p.velocity.z));
p.velocity.sub(drag);
}
particles.sort((a, b) => {
const d1 = camear.position.distanceTo(a.position);
const d2 = camear.position.distanceTo(b.position);
if (d1 > d2) {
return -1;
}
if (d1 < d2) {
return 1;
}
return 0;
});
}
//步长时间内重复调用
function step(timeElapsed) {
addParticles(timeElapsed);
updateParticles(timeElapsed);
updateGeometry();
}
useFrame((state, dealy, obj) => {
step(speed);
});
return (
<points name="fireShader">
<bufferGeometry
ref={geometryRef}
position={new THREE.Float32BufferAttribute([], 5)}
size={new THREE.Float32BufferAttribute([], 0.5)}
colour={new THREE.Float32BufferAttribute([], 1)}
angle={new THREE.Float32BufferAttribute([], 1)}
/>
<shaderMaterial
uniforms={uniforms}
vertexShader={_VS}
fragmentShader={_FS}
blending={THREE.AdditiveBlending}
depthTest={true}
depthWrite={false}
transparent={true}
vertexColors={true}
/>
</points>
);
}
export default Fire;
这是贴图:
本文作者:sy0313
本文链接:https://www.cnblogs.com/sunyan97/p/17440489.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步