效果 一个球体从前到后逐渐显示
代码仓库
逻辑: 使用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;
}