效果
所用插件:
threeJs,
MeshSurfaceSampler.js —webgl表面采样器
MeshSurfaceSampler表面采样器
用于采集模型表面随机一个点,根据这个原理我可以创建一个三维向量:
const sum = 20000;//创建20000个点
const sampler = new THREE.MeshSurfaceSampler(mesh).build();//为模型添加采样器
const tempPosition = new THREE.Vector3();//三维向量
const vertices = [];
for (let i = 0; i < sum; i ++) {
sampler.sample(tempPosition);//通过采样器随机位置采样--(x,y,z)
vertices.push(tempPosition.x, tempPosition.y, tempPosition.z);//每次的随机位置保存在vertices数组
}
通过这样我们得到了一个模型表面的20000个点的位置信息。
正式开始我的粒子化
首先我需要明确流程:
- 采样 15000 个坐标并将它们存储在一个数组中
- 从坐标和材质创建几何体
- 将几何体和材质组合成一个 Points 对象
- 将它们添加到场景中
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(90, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 7, 10);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const group = new THREE.Group();
scene.add(group);
const geometry = new THREE.TorusKnotGeometry(4, 1.3, 100, 16);
const torusKnot = new THREE.Mesh(geometry);
//为torusKnot 添加采样器
const sampler = new THREE.MeshSurfaceSampler(torusKnot).build();
const vertices = [];
const tempPosition = new THREE.Vector3();
//采样 15000 个坐标并将它们存储在一个数组中
for (let i = 0; i < 15000; i ++) {
sampler.sample(tempPosition);
vertices.push(tempPosition.x, tempPosition.y, tempPosition.z);
}
//从坐标和材质创建几何体
const pointsGeometry = new THREE.BufferGeometry();
pointsGeometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
//将几何体和材质组合成一个 Points 对象
const pointsMaterial = new THREE.PointsMaterial({
color: 0xff61d5,
size: 0.03
});
const points = new THREE.Points(pointsGeometry, pointsMaterial);
group.add(points);
function render () {
group.rotation.y += 0.01;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(render);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
window.addEventListener('resize', onWindowResize);
使用 3D 模型
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const renderer = new THREE.WebGLRenderer({
antialias: true,
alpha: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.z = 230;
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const group = new THREE.Group();
scene.add(group);
const vertices = [];
const colors = [];
const sparklesGeometry = new THREE.BufferGeometry();
const sparklesMaterial = new THREE.PointsMaterial({
size: 3,
alphaTest: 0.2,
map: new THREE.TextureLoader().load("https://assets.codepen.io/127738/dotTexture.png"),
vertexColors: true // Let Three.js knows that each point has a different color
});
const points = new THREE.Points(sparklesGeometry, sparklesMaterial);
group.add(points);
let sampler = null;
let elephant = null;
new THREE.OBJLoader().load(
"https://assets.codepen.io/127738/Mesh_Elephant.obj",
(obj) => {
elephant = obj.children[0];
elephant.material = new THREE.MeshBasicMaterial({
color: 0x000000,
transparent: true,
opacity: 0.05
});
group.add(obj);
addPoint(elephant)
renderer.setAnimationLoop(render);
},
(xhr) => console.log((xhr.loaded / xhr.total) * 100 + "% loaded"),
(err) => console.error(err)
);
function addPoint(obj) {
const sum = 3500;
/* 随机3种颜色 */
const palette = [new THREE.Color("#FAAD80"), new THREE.Color("#FF6767"), new THREE.Color("#FF3D68"), new THREE.Color("#A73489")];
const sampler = new THREE.MeshSurfaceSampler(obj).build();
const tempPosition = new THREE.Vector3();
let vertices = new Float32Array(sum*3);
let colors = new Float32Array(sum*3);
for (let i = 0; i < sum; i ++) {
sampler.sample(tempPosition);//随机位置采样
vertices[i*3+0] = (tempPosition.x);
vertices[i*3+1] = (tempPosition.y);
vertices[i*3+2] = (tempPosition.z);
const color = palette[Math.floor(Math.random() * palette.length)];
colors[i*3+0] = (color.r);
colors[i*3+1] = (color.g);
colors[i*3+2] = (color.b);
}
sparklesGeometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3) );
sparklesGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
console.log(obj);
}
function render(a) {
group.rotation.y += 0.002;
controls.update();
renderer.render(scene, camera);
}
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
代码参考:https://codepen.io/Mamboleoo/pen/rNmRqeK
结语
我们可以在这的基础上添加辉光效果,可以参考threeJs官网案例:https://threejs.org/examples/#webgl_postprocessing_unreal_bloom;
实现开头的效果。
我希望你今天学到了一些东西
资料参考:https://tympanus.net/codrops/2021/08/31/surface-sampling-in-three-js/