效果 一个球体从前到后逐渐显示

Three.js 粒子化物体逐渐显示效果_Math


​代码仓库​

逻辑: 使用uniforms传递变量 传递显示z轴的位置 在这一位置之前显示 之后不现实

着色器代码

uniform float uSize;
uniform float uLimitZ;
uniform float uRegionZ;
attribute float scale;
attribute float random;
varying vec3 vPosition;
varying float vHidden;

void main() {
vPosition = position;
vec3 p = position;
// p += random;

float disScale = 1.0;
vHidden = 1.0;
if(p.z < uLimitZ + uRegionZ && p.z > uLimitZ - uRegionZ) {
disScale = 2.0;
p += random;
}
if(p.z > uLimitZ) {
disScale = 0.0;
vHidden = 0.0;
}

vec4 viewPosition = modelViewMatrix * vec4(p, 1.0);
gl_Position = projectionMatrix * viewPosition;
// 点的大小
gl_PointSize = uSize * scale * disScale;
// 近大远小效果 第一个值根据场景自己调节
gl_PointSize *= 10. / -viewPosition.z;
}
varying vec3 vPosition;
uniform vec3 color;
varying float vHidden;

void main() {

if(vHidden == 0.0)
discard;

//获得以粒子中心为原点的圆形区域
//距离中心距离
float strength = distance(gl_PointCoord, vec2(.5));
// strength = step(.5, strength);
// strength *= 2.;
strength = 1. - strength;
strength = pow(strength, 10.0);

//圆形以外像素 不渲染
if(strength <= 0.04)
discard;

gl_FragColor = vec4(color, strength);
}

材质部分

import vertexShader from "./vertex.glsl";
import fragmentShader from "./fragment.glsl";
import { ShaderMaterial as THREEShaderMaterial } from "three";

export const ShaderMaterial = new THREEShaderMaterial({
uniforms: {
time: { value: 0 },
//弥补自定义shader没有PointsMaterial材质的size属性
size: { value: 8 },
},
// blending: AdditiveBlending,
transparent: true,
vertexShader,
fragmentShader
});
setRegionParams(mesh: IRegionParticle[0]) {
mesh.geometry.computeBoundingSphere();
const { radius } = mesh.geometry.boundingSphere!;

const vRegionZ = 0.1;

this.material.uniforms = {
...this.material.uniforms,
uLimitZ: { value: radius + vRegionZ * 2 },
uRegionZ: { value: vRegionZ },
};
this.limitZ = radius + vRegionZ * 2;
}
const init = (helper: ThreeHelper) => {
helper.addStats();
helper.camera.position.set(0, 1, 10);
helper.setBackground("#fffae5");

helper.addGUI();

helper
.loadGltf(
new URL("../../src/static/models/sphere.glb", import.meta.url)
)
.then((g) => {
helper.frameByFrame();

const sphere = g.scene.getObjectByName("球体") as Mesh;
if (!sphere) throw new Error("not find model");
{
const particle = meshToParticle(sphere, helper, 0.01, -1);
particle.position.set(2.5, 0, 6);
particle.rotateY(Math.PI / -5);
}
});
};

function meshToParticle(
mesh: Mesh,
helper: ThreeHelper,
speed = 0.03,
_dir = 1
) {
const particles = new RegionParticle(mesh, {
size: 1,
color: "#215cb5",
});
helper.add(particles.particle);

let uLimitZ = particles.limitZ * _dir;
let dir = _dir;
particles.particle.onAfterRender = () => {
uLimitZ -= speed * dir;
if (uLimitZ < -particles.limitZ || uLimitZ > particles.limitZ) {
dir *= -1;
}
particles.particle.material.updateUniforms("uLimitZ", uLimitZ);
};
return particles.particle;
}